import { useCollection, useIdentity } from '@deplixo/sdk'; import { Icon } from './components/icons.jsx'; import Onboarding from './components/Onboarding.jsx'; import Home from './components/Home.jsx'; import Tasks from './components/Tasks.jsx'; import Schedule from './components/Schedule.jsx'; import Lists from './components/Lists.jsx'; import Assistant from './components/Assistant.jsx'; import Settings from './components/Settings.jsx'; import Pro from './components/Pro.jsx'; import ProFeatures from './components/ProFeatures.jsx'; import { parseDateStr, isSameDay, formatTime } from './components/utils.js'; const DEFAULT_PREFS = { theme: 'light', accent: '#5b3fd1', avatarUrl: '', displayName: '', onboarded: false, isPro: false, assistantName: '', background: '', }; function applyTheme(prefs) { if (typeof document === 'undefined') return; const root = document.documentElement; root.setAttribute('data-theme', prefs.theme || 'light'); if (prefs.accent) { root.style.setProperty('--primary', prefs.accent); // Soft tint derived from accent via color-mix (modern browsers) root.style.setProperty('--primary-tint', `color-mix(in srgb, ${prefs.accent} 14%, transparent)`); root.style.setProperty('--primary-dark', `color-mix(in srgb, ${prefs.accent} 80%, black)`); } // Pro custom background — applied to body so it shows behind the app shell. if (prefs.isPro && prefs.background) { document.body.style.backgroundImage = prefs.background; document.body.style.backgroundAttachment = 'fixed'; document.body.style.backgroundSize = 'cover'; } else { document.body.style.backgroundImage = ''; document.body.style.backgroundAttachment = ''; document.body.style.backgroundSize = ''; } } function App() { const { user, loading: idLoading } = useIdentity(); // Per-user collections (filtered by author.id). const tasksCol = useCollection('tasks'); const eventsCol = useCollection('events'); const remindersCol = useCollection('reminders'); const shoppingCol = useCollection('shopping'); const datesCol = useCollection('important_dates'); const prefsCol = useCollection('preferences'); const subscriptionsCol = useCollection('subscriptions'); const [tab, setTab] = useState('home'); const [showSettings, setShowSettings] = useState(false); const [showPro, setShowPro] = useState(false); const [showProFeatures, setShowProFeatures] = useState(false); const myId = user?.id; const mine = (arr) => (arr || []).filter(x => x.author?.id === myId); const tasks = mine(tasksCol.items); const events = mine(eventsCol.items); const reminders = mine(remindersCol.items); const shopping = mine(shoppingCol.items); const dates = mine(datesCol.items); const myPrefsItem = mine(prefsCol.items)[0]; const prefs = { ...DEFAULT_PREFS, ...(myPrefsItem?.value || {}) }; // Most recent active subscription for this user. const mySubscriptionItem = mine(subscriptionsCol.items) .slice() .sort((a, b) => (b.value?.startedAt || '').localeCompare(a.value?.startedAt || ''))[0]; const subscription = mySubscriptionItem?.value || null; useEffect(() => { applyTheme(prefs); }, [prefs.theme, prefs.accent, prefs.background, prefs.isPro]); const updatePrefs = async (patch) => { const next = { ...prefs, ...patch }; if (myPrefsItem) await prefsCol.update(myPrefsItem.id, next); else await prefsCol.add(next); }; const saveSubscription = async (sub) => { if (mySubscriptionItem) await subscriptionsCol.update(mySubscriptionItem.id, sub); else await subscriptionsCol.add(sub); }; // --- CRUD helpers --- const toggle = async (col, item) => { await col.update(item.id, { ...item.value, done: !item.value?.done }); }; // Add batches (used by assistant) const addMany = async (col, items) => { for (const it of items) await col.add(it); }; // Plan My Day — simple heuristic const planMyDay = () => { const today = new Date(); const todayEvents = events .filter(e => isSameDay(parseDateStr(e.value?.date), today)) .sort((a,b) => (a.value?.startTime || '').localeCompare(b.value?.startTime || '')); const todayTasks = tasks.filter(t => !t.value?.done).slice(0, 4); let plan = "Here's a suggested plan for today:\n\n"; if (todayEvents.length === 0 && todayTasks.length === 0) { plan = "Your day looks wide open! A great time to catch up on things you've been putting off, or take a well-deserved break. ✨"; } else { todayEvents.forEach(e => { const s = formatTime(e.value?.startTime) || 'anytime'; const en = e.value?.endTime ? `–${formatTime(e.value.endTime)}` : ''; plan += `• ${s}${en} — ${e.value?.title}\n`; }); if (todayTasks.length) { plan += `\nTry to fit in:\n`; todayTasks.forEach(t => { plan += `• ${t.value?.title}\n`; }); } } // Jump to assistant and show the plan as a message setPlanMessage(plan); setTab('assistant'); }; // Communicate a plan message into the assistant via a key reset const [planMessage, setPlanMessage] = useState(null); // Quick-add: open assistant with empty input (same UX) const quickAdd = () => setTab('assistant'); if (idLoading) { return