perf: load calendar events instantly, sync CalDAV in background

Split loadEvents into two functions: loadEvents (instant DB read) and
syncAndReload (background CalDAV sync + reload). Events now appear
immediately when switching to the Calendar tab instead of waiting for
the CalDAV sync to complete.
This commit is contained in:
2026-02-09 18:37:14 +01:00
parent b94b5f5ed8
commit 0e406e4dca
2 changed files with 17 additions and 14 deletions

View File

@@ -405,7 +405,7 @@ CalDAV sync with external calendar servers (e.g., Radicale) using `tsdav` and `i
**Sync Triggers (client-side via `CaldavConfigService.sync()`):** **Sync Triggers (client-side via `CaldavConfigService.sync()`):**
- **Login** (`login.tsx`): After successful authentication - **Login** (`login.tsx`): After successful authentication
- **Auto-login** (`AuthGuard.tsx`): After `loadStoredUser()` if authenticated - **Auto-login** (`AuthGuard.tsx`): After `loadStoredUser()` if authenticated
- **Calendar timer** (`calendar.tsx`): Every 10s while Calendar tab is focused, via `setInterval` in `useFocusEffect` - **Calendar timer** (`calendar.tsx`): Events load instantly from DB on focus (`loadEvents`), CalDAV sync runs in background (`syncAndReload`) and reloads events after. Repeats every 10s via `setInterval`
- **Sync button** (`settings.tsx`): Manual trigger in CaldavSettings - **Sync button** (`settings.tsx`): Manual trigger in CaldavSettings
**Lazy sync (server-side in ChatService):** **Lazy sync (server-side in ChatService):**
@@ -618,7 +618,7 @@ NODE_ENV=development # development = pretty logs, production = JSON
- Orange dot indicator for days with events - Orange dot indicator for days with events
- Tap-to-open modal overlay showing EventCards for selected day - Tap-to-open modal overlay showing EventCards for selected day
- Supports events from adjacent months visible in grid - Supports events from adjacent months visible in grid
- Uses `useFocusEffect` for automatic reload on tab focus with periodic CalDAV sync (10s interval while focused) - Events load instantly from local DB on tab focus, CalDAV sync runs non-blocking in background (`syncAndReload`) with 10s interval
- DeleteEventModal integration for recurring event deletion with three modes - DeleteEventModal integration for recurring event deletion with three modes
- EventOverlay hides when DeleteEventModal is open (fixes modal stacking on web) - EventOverlay hides when DeleteEventModal is open (fixes modal stacking on web)
- Chat screen fully functional with FlashList, message sending, and event confirm/reject - Chat screen fully functional with FlashList, message sending, and event confirm/reject

View File

@@ -85,15 +85,9 @@ const Calendar = () => {
const { events, setEvents, deleteEvent } = useEventsStore(); const { events, setEvents, deleteEvent } = useEventsStore();
// Sync CalDAV then load events for current view // Load events from local DB (fast, no network sync)
const loadEvents = useCallback(async () => { const loadEvents = useCallback(async () => {
try { try {
try {
await CaldavConfigService.sync();
} catch {
// No CalDAV config or sync failed — not critical
}
// Calculate first visible day (up to 6 days before month start) // Calculate first visible day (up to 6 days before month start)
const firstOfMonth = new Date(currentYear, monthIndex, 1); const firstOfMonth = new Date(currentYear, monthIndex, 1);
const dayOfWeek = firstOfMonth.getDay(); const dayOfWeek = firstOfMonth.getDay();
@@ -119,16 +113,25 @@ const Calendar = () => {
} }
}, [monthIndex, currentYear, setEvents]); }, [monthIndex, currentYear, setEvents]);
// Load events when tab gains focus or month/year changes // Sync CalDAV in background, then reload events
// NOTE: Wrapper needed because loadEvents is async (returns Promise) const syncAndReload = useCallback(async () => {
// and useFocusEffect expects a sync function (optionally returning cleanup) try {
await CaldavConfigService.sync();
await loadEvents();
} catch {
// Sync failed — not critical
}
}, [loadEvents]);
// Load events instantly on focus, then sync in background periodically
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
loadEvents(); loadEvents();
syncAndReload();
const interval = setInterval(loadEvents, 10_000); const interval = setInterval(syncAndReload, 10_000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [loadEvents]), }, [loadEvents, syncAndReload]),
); );
// Re-open overlay after back navigation from editEvent // Re-open overlay after back navigation from editEvent