Skip to content

fix(calendar): sync Google Calendar on focus, resume, and manual refresh - #297

Merged
h4yfans merged 1 commit into
mainfrom
fix/calendar-sync-triggers
Apr 20, 2026
Merged

fix(calendar): sync Google Calendar on focus, resume, and manual refresh#297
h4yfans merged 1 commit into
mainfrom
fix/calendar-sync-triggers

Conversation

@h4yfans

@h4yfans h4yfans commented Apr 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Users were waiting 5+ min for remote Google Calendar changes (including deletions) to propagate to memry because sync only ran on the background 5-min poll. macOS App Nap / system sleep make this worse — setInterval doesn't catch up on wake.

This PR adds three faster sync triggers, all funneled through a single 10-second cooldown so rapid-fire signals don't stampede the API:

  • Window focusmainWindow.on('focus') fires when the user clicks back into memry.
  • System resumepowerMonitor.on('resume') fires on wake-from-sleep, registered with the runner lifecycle (cleaned up in stopGoogleCalendarSyncRunner).
  • Manual refresh button — new toolbar button that calls the existing refreshGoogleCalendarProvider IPC. REFRESH_PROVIDER was already wired end-to-end; only the UI surface was missing. The button is shown only when Google calendars are connected and spins while the sync is in-flight.

The 5-min background poll remains unchanged as the silent safety net.

Why

Reported symptom: events deleted in Google Calendar still appeared in memry for 5+ minutes. Investigation confirmed the deletion pipeline itself (archivedAt tombstone + projection filter) is correct — the delay was purely in how often sync ran, compounded by OS-level timer throttling.

What changed

File Change
apps/desktop/src/main/calendar/google/google-sync-runner.ts New triggerGoogleCalendarSyncNow(reason) with 10s cooldown; powerMonitor.on('resume', …) wired to runner start/stop.
apps/desktop/src/main/calendar/google/sync-service.ts Re-export triggerGoogleCalendarSyncNow.
apps/desktop/src/main/calendar/google/sync-service-cadence.test.ts Mocked powerMonitor in electron mock (runner now imports it).
apps/desktop/src/main/calendar/google/sync-service-trigger.test.ts New. 7 tests — cooldown, expiry, error swallowing, resume handler register/cleanup.
apps/desktop/src/main/index.ts mainWindow.on('focus', …) fires the window-focus trigger.
apps/desktop/src/renderer/src/components/calendar/calendar-shell.tsx Refresh button in toolbar (visible only when Google calendars are connected).

Reviewer notes

  • No new IPC channels or contracts — the manual refresh path reuses the existing CalendarChannels.invoke.REFRESH_PROVIDER, which was already wired to syncGoogleCalendarNow().
  • Cooldown is shared across focus/resume/manual signals, so the three paths can't compound into a sync storm. A user who clicks back into memry right after a system wake will still only issue one sync.
  • The 5-min poll is unchanged. This PR is strictly additive.
  • Push-channel path (CALENDAR_PUSH_ENABLED=1) is unaffected. When push is enabled, poll already drops to 30 min; these new triggers make the poll-only path feel closer to push without requiring the server-side watch infrastructure.
  • Exported a small __resetTriggerForTests() helper from google-sync-runner.ts to keep the cooldown deterministic across test cases.

Test plan

  • pnpm --filter @memry/desktop test — 7130 pass (+7 new), 1 skipped (pre-existing)
  • pnpm --filter @memry/desktop typecheck:node — clean
  • pnpm --filter @memry/desktop typecheck:web — clean
  • Manual: delete event in Google Calendar → click back into memry → event disappears within ~1 s
  • Manual: delete event → close laptop lid for a few min → reopen → event disappears immediately on wake
  • Manual: click the refresh button (with Google connected) → icon spins while syncing → calendar updates

Out of scope

  • Reconciliation after cursor reset — if Google's syncToken expires or is invalidated, the fallback initial sync doesn't archive events that existed locally but are no longer returned by Google. Worth a separate PR once this one ships; lower priority because it only bites on re-auth / 30+ day offline windows.

Users were waiting 5+ min for remote Google Calendar changes (including
deletions) to appear in memry because sync ran only on the background
5-min poll. macOS App Nap and system sleep further delayed this — the
poll interval drifts until the app is foreground.

Adds three faster sync triggers, all funneled through a single 10-second
cooldown so rapid signals don't stampede the API:

  - Window focus: mainWindow.on('focus') fires when the user clicks back
    into memry.
  - System resume: powerMonitor.on('resume') fires when the machine wakes
    from sleep, registered with the runner lifecycle so it's cleaned up
    on stopGoogleCalendarSyncRunner.
  - Manual refresh: new button in the calendar toolbar that calls the
    existing refreshGoogleCalendarProvider IPC (REFRESH_PROVIDER was
    already wired; only the UI surface was missing).

The 5-min background poll remains unchanged as a safety net.

Tests: 7 new in sync-service-trigger.test.ts covering cooldown behavior,
cooldown expiry, error swallowing, and powerMonitor handler
registration/cleanup. The existing cadence test's electron mock was
extended with powerMonitor since the runner now imports it.
@h4yfans
h4yfans merged commit cb12134 into main Apr 20, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant