Skip to content

Commit d273c5c

Browse files
fix: preserve google calendar selections across sync
1 parent 3a3c109 commit d273c5c

File tree

3 files changed

+120
-13
lines changed

3 files changed

+120
-13
lines changed

apps/desktop/src/calendar/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,37 @@ export function findCalendarByTrackingId(
4242

4343
return foundRowId;
4444
}
45+
46+
export function findLegacyCalendarByTrackingId(
47+
store: Store<Schemas>,
48+
{
49+
provider,
50+
trackingId,
51+
source,
52+
}: {
53+
provider: string;
54+
trackingId: string;
55+
source?: string;
56+
},
57+
): string | null {
58+
if (!source) {
59+
return null;
60+
}
61+
62+
let foundRowId: string | null = null;
63+
64+
store.forEachRow("calendars", (rowId, _forEachCell) => {
65+
if (foundRowId) return;
66+
const row = store.getRow("calendars", rowId);
67+
if (
68+
row?.provider === provider &&
69+
!row?.connection_id &&
70+
row?.tracking_id_calendar === trackingId &&
71+
row?.source === source
72+
) {
73+
foundRowId = rowId;
74+
}
75+
});
76+
77+
return foundRowId;
78+
}

apps/desktop/src/services/calendar/ctx.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,4 +170,51 @@ describe("syncCalendars", () => {
170170
name: "Personal",
171171
});
172172
});
173+
174+
test("preserves enabled state for legacy Google calendars without connection ids", async () => {
175+
const store = createStore();
176+
177+
store.setRow("calendars", "legacy-row", {
178+
user_id: "user-1",
179+
created_at: "2026-03-25T00:00:00.000Z",
180+
tracking_id_calendar: "primary",
181+
name: "John (Hyprnote)",
182+
enabled: true,
183+
provider: "google",
184+
source: "john@hyprnote.com",
185+
color: "#4285f4",
186+
connection_id: "",
187+
});
188+
189+
pluginCalendar.listCalendars.mockResolvedValue({
190+
status: "success",
191+
data: [
192+
{
193+
id: "primary",
194+
title: "John (Hyprnote)",
195+
source: "john@hyprnote.com",
196+
color: "#4285f4",
197+
},
198+
],
199+
});
200+
201+
await syncCalendars(store, [
202+
{
203+
provider: "google",
204+
connection_ids: ["conn-john"],
205+
},
206+
]);
207+
208+
const calendars = getCalendarsByConnection(store, "google");
209+
210+
expect(calendars).toHaveLength(1);
211+
expect(calendars[0]).toMatchObject({
212+
id: "legacy-row",
213+
tracking_id_calendar: "primary",
214+
name: "John (Hyprnote)",
215+
enabled: true,
216+
source: "john@hyprnote.com",
217+
connection_id: "conn-john",
218+
});
219+
});
173220
});

apps/desktop/src/services/calendar/ctx.ts

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99

1010
import {
1111
findCalendarByTrackingId,
12+
findLegacyCalendarByTrackingId,
1213
getCalendarTrackingKey,
1314
} from "~/calendar/utils";
1415
import { QUERIES, type Schemas, type Store } from "~/store/tinybase/store/main";
@@ -127,19 +128,38 @@ export async function syncCalendars(
127128

128129
store.transaction(() => {
129130
const disabledCalendarIds = new Set<string>();
131+
const legacyMatchedRowIds = new Set<string>();
132+
133+
for (const { calendars } of perConnection) {
134+
for (const cal of calendars) {
135+
const legacyRowId = findLegacyCalendarByTrackingId(store, {
136+
provider,
137+
trackingId: cal.id,
138+
source: cal.source ?? undefined,
139+
});
140+
141+
if (legacyRowId) {
142+
legacyMatchedRowIds.add(legacyRowId);
143+
}
144+
}
145+
}
130146

131147
for (const rowId of store.getRowIds("calendars")) {
132148
const row = store.getRow("calendars", rowId);
133149
if (
134-
row.provider === provider &&
135-
!incomingKeys.has(
136-
getCalendarTrackingKey({
137-
provider: row.provider as string | undefined,
138-
connectionId: row.connection_id as string | undefined,
139-
trackingId: row.tracking_id_calendar as string | undefined,
140-
}),
141-
)
150+
legacyMatchedRowIds.has(rowId) ||
151+
(row.provider === provider &&
152+
!incomingKeys.has(
153+
getCalendarTrackingKey({
154+
provider: row.provider as string | undefined,
155+
connectionId: row.connection_id as string | undefined,
156+
trackingId: row.tracking_id_calendar as string | undefined,
157+
}),
158+
))
142159
) {
160+
if (legacyMatchedRowIds.has(rowId)) {
161+
continue;
162+
}
143163
disabledCalendarIds.add(rowId);
144164
store.delRow("calendars", rowId);
145165
} else if (row.provider === provider && !row.enabled) {
@@ -158,11 +178,17 @@ export async function syncCalendars(
158178

159179
for (const { connectionId, calendars } of perConnection) {
160180
for (const cal of calendars) {
161-
const existingRowId = findCalendarByTrackingId(store, {
162-
provider,
163-
connectionId,
164-
trackingId: cal.id,
165-
});
181+
const existingRowId =
182+
findCalendarByTrackingId(store, {
183+
provider,
184+
connectionId,
185+
trackingId: cal.id,
186+
}) ??
187+
findLegacyCalendarByTrackingId(store, {
188+
provider,
189+
trackingId: cal.id,
190+
source: cal.source ?? undefined,
191+
});
166192
const rowId = existingRowId ?? crypto.randomUUID();
167193
const existing = existingRowId
168194
? store.getRow("calendars", existingRowId)

0 commit comments

Comments
 (0)