Skip to content

feat(core,backend,sync): carry calendar account email and event iCalUID on the wire - #2564

Merged
tyler-dane merged 1 commit into
mainfrom
feat/multi-account-calendar-wire
Aug 4, 2026
Merged

feat(core,backend,sync): carry calendar account email and event iCalUID on the wire#2564
tyler-dane merged 1 commit into
mainfrom
feat/multi-account-calendar-wire

Conversation

@tyler-dane

Copy link
Copy Markdown
Contributor

What

Invisible groundwork for supporting multiple connected Google accounts on one Compass grid:

  • CalendarSchema gains an optional accountEmail — the email of the provider account each calendar belongs to. The backend joins it from the principal's connection list (fetched in parallel with the calendar list; both are cheap passive-mode sync reads). Absent (not null/empty) for the local calendar and for accounts that reported no email.
  • Imported Google events store their iCalUID in the ownership providerMetadata bag. Google shares iCalUID across copies of the same meeting on different accounts (unlike providerEventId), so it is the correlation key for recognizing cross-account duplicates later. The bag now merges both facts it can carry (transparency for free events, iCalUID), still null when neither applies.

No visual or behavioral change: the web parses the same core schema, so the new optional field flows through untouched, and nothing reads iCalUID yet.

Tests

  • Translation emits accountEmail when supplied and omits the key entirely when not.
  • Controller joins emails by connectionId; a connection with a null email yields a calendar without the key.
  • Normalizer maps iCalUID and omits it when Google reports none.
  • Applier stores {iCalUID}, merges {transparency, iCalUID}, and keeps null for plain busy events.
  • Full sync suite (761 pass) and backend suite (323 pass) green; bun run type-check and biome clean.

🤖 Generated with Claude Code

…ID on the wire

Groundwork for supporting multiple connected Google accounts. Each
calendar in /api/calendars now reports the email of the provider account
it belongs to (joined from the connection list; absent for the local
calendar), and imported Google events store their iCalUID in the
provider metadata bag so copies of the same meeting on different
accounts can be correlated later. No visual or behavioral change yet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@tyler-dane
tyler-dane merged commit 434d017 into main Aug 4, 2026
20 checks passed
@tyler-dane
tyler-dane deleted the feat/multi-account-calendar-wire branch August 4, 2026 16:14
tyler-dane added a commit that referenced this pull request Aug 4, 2026
* fix(web): commit dropdown selection when Enter lands on the trigger

useListNavigation moves focus from the trigger into the option list a
frame after the dropdown opens, so a fast ArrowDown+Enter can land
Enter on the trigger button. Its native Enter-click then toggled the
dropdown closed and lost the selection. Both dropdown components now
commit the active option from the trigger too, mirroring the floating
element's own handler (the dual placement floating-ui's Select example
uses).

The keyboard e2e test additionally waits for the roving tabindex to
land on the target option before committing: floating-ui applies the
arrow-key move a frame later, and Playwright (unlike any human) can
type ArrowDown+Enter inside one frame, re-committing the previous
selection - which closed the dropdown with an unchanged label and made
the test intermittently fail with no app error to show for it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(e2e): drop the flaky keyboard-commit tail of the calendar picker test

The ArrowDown+Enter commit in this test failed ~40% of isolated repeated
runs. Two mechanism-level fixes were applied and verified insufficient:
hardening the component against a real trigger-Enter race (kept - it
fixes a genuine fast-keyboard bug, and the same latent race in
SelectView), and waiting for the roving tabindex to move before Enter.
The residual race lives somewhere in floating-ui's async active-item
handling under Playwright's faster-than-human key cadence and is not
worth more investigation: keyboard navigation and Enter/Space selection
are covered deterministically in CalendarSelect.test.tsx.

The stable and otherwise-uncovered part of the test - the form offers
only writable calendars - stays.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: simplify multi-account cross-account-merge plumbing

Findings from a 4-angle review (reuse/simplification/efficiency/altitude)
of PRs #2564-#2573. Applied:

- The 'also on another account' fact now rides on the event itself
  (GridEvent.otherAccount), joined the same way isDemo already is,
  instead of a parallel side-band map hand-threaded through 2 view-model
  hooks and 4 list components. Deletes findCrossAccountDuplicate and all
  of its call sites; resolveCalendarCardIdentity now reads the fact
  straight off the event.
- useDayEventViewModel/useWeekEventViewModel's identical ~20-line
  filter->merge->derive pipeline collapses into one shared
  useCalendarEventViewModel hook.
- A single-account early-out in mergeCrossAccountDuplicates: duplicates
  need two accounts, so the overwhelming majority of users (one account)
  now skip the per-event grouping pass entirely.
- The merge's 3-level memo (WeakMap>WeakMap>Map-of-emails) drops to a
  last-value slot - every consumer in one render derives the same
  default-account email, so remembering one is as effective as a map.
- computeMerge groups {id, event, accountEmail} directly instead of
  re-deriving accountEmail from a second pass over the group.
- The calendarAccentStyle/calendarAccentAccessibleSuffix helpers move
  out of the calendars module (which had started importing types from
  events/queries - an inverted dependency) to grid/components, next to
  their only two consumers.
- SYNC_STATUS_VARIANT_CLASSNAME, copy-pasted between CalendarListHeader
  and the new AccountSectionHeader, is now one export from
  sync-status.types.ts.
- sync-service.client.ts's expectNoContent flag and optional schema
  encoded one fact twice; collapsed to one rule (no schema -> expect
  204).
- Dead optionality in user-metadata.service.ts's GoogleMetadataAssessment
  (connections?/connection? guarded against a producer that never omits
  them) replaced with the real GoogleConnectionFromSync type.
- backfill-icaluid's write path no longer double-passes
  (samples loop, then a separate filter/map for writes) or re-derives
  guards the query already enforced.
- New shared test factories (createMockCalendar, createMockConnection)
  replace four near-identical per-file copies written across this same
  feature - the Calendar/GoogleSyncConnectionSummary contracts grew
  fields in this very set of PRs, so the duplication was already live.

All four suites green (1642 web / 327 backend / 768 sync / 36 scripts),
type-check and biome clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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