From d273c5c980a8a8ce2069ea77438ad05dfffa8712 Mon Sep 17 00:00:00 2001 From: ComputelessComputer Date: Fri, 27 Mar 2026 23:12:24 -0700 Subject: [PATCH] fix: preserve google calendar selections across sync --- apps/desktop/src/calendar/utils.ts | 34 ++++++++++++ .../desktop/src/services/calendar/ctx.test.ts | 47 +++++++++++++++++ apps/desktop/src/services/calendar/ctx.ts | 52 ++++++++++++++----- 3 files changed, 120 insertions(+), 13 deletions(-) diff --git a/apps/desktop/src/calendar/utils.ts b/apps/desktop/src/calendar/utils.ts index c7781f28e1..d14c899cd6 100644 --- a/apps/desktop/src/calendar/utils.ts +++ b/apps/desktop/src/calendar/utils.ts @@ -42,3 +42,37 @@ export function findCalendarByTrackingId( return foundRowId; } + +export function findLegacyCalendarByTrackingId( + store: Store, + { + provider, + trackingId, + source, + }: { + provider: string; + trackingId: string; + source?: string; + }, +): string | null { + if (!source) { + return null; + } + + let foundRowId: string | null = null; + + store.forEachRow("calendars", (rowId, _forEachCell) => { + if (foundRowId) return; + const row = store.getRow("calendars", rowId); + if ( + row?.provider === provider && + !row?.connection_id && + row?.tracking_id_calendar === trackingId && + row?.source === source + ) { + foundRowId = rowId; + } + }); + + return foundRowId; +} diff --git a/apps/desktop/src/services/calendar/ctx.test.ts b/apps/desktop/src/services/calendar/ctx.test.ts index ecae3b0072..489374031b 100644 --- a/apps/desktop/src/services/calendar/ctx.test.ts +++ b/apps/desktop/src/services/calendar/ctx.test.ts @@ -170,4 +170,51 @@ describe("syncCalendars", () => { name: "Personal", }); }); + + test("preserves enabled state for legacy Google calendars without connection ids", async () => { + const store = createStore(); + + store.setRow("calendars", "legacy-row", { + user_id: "user-1", + created_at: "2026-03-25T00:00:00.000Z", + tracking_id_calendar: "primary", + name: "John (Hyprnote)", + enabled: true, + provider: "google", + source: "john@hyprnote.com", + color: "#4285f4", + connection_id: "", + }); + + pluginCalendar.listCalendars.mockResolvedValue({ + status: "success", + data: [ + { + id: "primary", + title: "John (Hyprnote)", + source: "john@hyprnote.com", + color: "#4285f4", + }, + ], + }); + + await syncCalendars(store, [ + { + provider: "google", + connection_ids: ["conn-john"], + }, + ]); + + const calendars = getCalendarsByConnection(store, "google"); + + expect(calendars).toHaveLength(1); + expect(calendars[0]).toMatchObject({ + id: "legacy-row", + tracking_id_calendar: "primary", + name: "John (Hyprnote)", + enabled: true, + source: "john@hyprnote.com", + connection_id: "conn-john", + }); + }); }); diff --git a/apps/desktop/src/services/calendar/ctx.ts b/apps/desktop/src/services/calendar/ctx.ts index 5c76ae5a57..7f3a362aad 100644 --- a/apps/desktop/src/services/calendar/ctx.ts +++ b/apps/desktop/src/services/calendar/ctx.ts @@ -9,6 +9,7 @@ import type { import { findCalendarByTrackingId, + findLegacyCalendarByTrackingId, getCalendarTrackingKey, } from "~/calendar/utils"; import { QUERIES, type Schemas, type Store } from "~/store/tinybase/store/main"; @@ -127,19 +128,38 @@ export async function syncCalendars( store.transaction(() => { const disabledCalendarIds = new Set(); + const legacyMatchedRowIds = new Set(); + + for (const { calendars } of perConnection) { + for (const cal of calendars) { + const legacyRowId = findLegacyCalendarByTrackingId(store, { + provider, + trackingId: cal.id, + source: cal.source ?? undefined, + }); + + if (legacyRowId) { + legacyMatchedRowIds.add(legacyRowId); + } + } + } for (const rowId of store.getRowIds("calendars")) { const row = store.getRow("calendars", rowId); if ( - row.provider === provider && - !incomingKeys.has( - getCalendarTrackingKey({ - provider: row.provider as string | undefined, - connectionId: row.connection_id as string | undefined, - trackingId: row.tracking_id_calendar as string | undefined, - }), - ) + legacyMatchedRowIds.has(rowId) || + (row.provider === provider && + !incomingKeys.has( + getCalendarTrackingKey({ + provider: row.provider as string | undefined, + connectionId: row.connection_id as string | undefined, + trackingId: row.tracking_id_calendar as string | undefined, + }), + )) ) { + if (legacyMatchedRowIds.has(rowId)) { + continue; + } disabledCalendarIds.add(rowId); store.delRow("calendars", rowId); } else if (row.provider === provider && !row.enabled) { @@ -158,11 +178,17 @@ export async function syncCalendars( for (const { connectionId, calendars } of perConnection) { for (const cal of calendars) { - const existingRowId = findCalendarByTrackingId(store, { - provider, - connectionId, - trackingId: cal.id, - }); + const existingRowId = + findCalendarByTrackingId(store, { + provider, + connectionId, + trackingId: cal.id, + }) ?? + findLegacyCalendarByTrackingId(store, { + provider, + trackingId: cal.id, + source: cal.source ?? undefined, + }); const rowId = existingRowId ?? crypto.randomUUID(); const existing = existingRowId ? store.getRow("calendars", existingRowId)