Skip to content

Commit a092624

Browse files
committed
add participants syncing for apple-calendar
1 parent 2bfe1e1 commit a092624

File tree

25 files changed

+517
-114
lines changed

25 files changed

+517
-114
lines changed

apps/desktop/src/components/devtool/seed/shared/calendar.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ export const createCalendar = () => {
1818
"Shared Calendar",
1919
]);
2020

21+
const calendarId = id();
2122
return {
22-
id: id(),
23+
id: calendarId,
2324
data: {
2425
user_id: DEFAULT_USER_ID,
26+
tracking_id_calendar: `mock-${calendarId}`,
2527
name: template,
2628
created_at: faker.date.past({ years: 1 }).toISOString(),
2729
enabled: faker.datatype.boolean(),

apps/desktop/src/components/devtool/seed/shared/event.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,12 @@ export const createEvent = (calendar_id: string) => {
177177
description = faker.helpers.arrayElement(topics);
178178
}
179179

180+
const eventId = id();
180181
return {
181-
id: id(),
182+
id: eventId,
182183
data: {
183184
user_id: DEFAULT_USER_ID,
185+
tracking_id_event: `mock-${eventId}`,
184186
calendar_id,
185187
title,
186188
started_at: startsAt.toISOString(),

apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants/chip.tsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ export function ParticipantChip({ mappingId }: { mappingId: string }) {
1313

1414
const assignedHumanId = details?.humanId;
1515
const sessionId = details?.sessionId;
16+
const source = details?.source;
1617

1718
const handleRemove = useRemoveParticipant({
1819
mappingId,
1920
assignedHumanId,
2021
sessionId,
22+
source,
2123
});
2224

2325
const handleClick = useCallback(() => {
@@ -29,7 +31,7 @@ export function ParticipantChip({ mappingId }: { mappingId: string }) {
2931
}
3032
}, [assignedHumanId]);
3133

32-
if (!details) {
34+
if (!details || source === "excluded") {
3335
return null;
3436
}
3537

@@ -64,6 +66,12 @@ function useParticipantDetails(mappingId: string) {
6466
mappingId,
6567
main.STORE_ID,
6668
);
69+
const source = main.UI.useCell(
70+
"mapping_session_participant",
71+
mappingId,
72+
"source",
73+
main.STORE_ID,
74+
);
6775

6876
if (!result) {
6977
return null;
@@ -81,6 +89,7 @@ function useParticipantDetails(mappingId: string) {
8189
orgId: (result.org_id as string | undefined) || undefined,
8290
orgName: result.org_name as string | undefined,
8391
sessionId: result.session_id as string,
92+
source: source as string | undefined,
8493
};
8594
}
8695

@@ -106,10 +115,12 @@ function useRemoveParticipant({
106115
mappingId,
107116
assignedHumanId,
108117
sessionId,
118+
source,
109119
}: {
110120
mappingId: string;
111121
assignedHumanId: string | undefined;
112122
sessionId: string | undefined;
123+
source: string | undefined;
113124
}) {
114125
const store = main.UI.useStore(main.STORE_ID);
115126
const indexes = main.UI.useIndexes(main.STORE_ID);
@@ -153,6 +164,12 @@ function useRemoveParticipant({
153164
}
154165
}
155166

156-
store.delRow("mapping_session_participant", mappingId);
157-
}, [store, indexes, mappingId, assignedHumanId, sessionId]);
167+
if (source === "auto") {
168+
store.setPartialRow("mapping_session_participant", mappingId, {
169+
source: "excluded",
170+
});
171+
} else {
172+
store.delRow("mapping_session_participant", mappingId);
173+
}
174+
}, [store, indexes, mappingId, assignedHumanId, sessionId, source]);
158175
}

apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants/input.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ function useLinkHumanToSession(
295295
created_at: new Date().toISOString(),
296296
session_id: sessionId,
297297
human_id: p.humanId,
298+
source: "manual",
298299
}),
299300
[userId, sessionId],
300301
main.STORE_ID,

apps/desktop/src/components/settings/calendar/configure/apple.tsx

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { AlertCircleIcon, ArrowRightIcon, CheckIcon } from "lucide-react";
44
import { useMemo } from "react";
55

66
import {
7-
type AppleCalendar,
87
commands as appleCalendarCommands,
98
colorToCSS,
109
} from "@hypr/plugin-apple-calendar";
@@ -21,6 +20,7 @@ import { Button } from "@hypr/ui/components/ui/button";
2120
import { cn } from "@hypr/utils";
2221

2322
import * as main from "../../../../store/tinybase/main";
23+
import { findCalendarByTrackingId } from "../../../../utils/calendar";
2424
import { PROVIDERS } from "../shared";
2525
import {
2626
type CalendarGroup,
@@ -188,25 +188,6 @@ function useAppleCalendarSelection() {
188188
const calendars = main.UI.useTable("calendars", main.STORE_ID);
189189
const { user_id } = main.UI.useValues(main.STORE_ID);
190190

191-
const setCalendarRow = main.UI.useSetRowCallback(
192-
"calendars",
193-
(cal: AppleCalendar) => cal.id,
194-
(cal: AppleCalendar, store) => {
195-
const existing = store.getRow("calendars", cal.id);
196-
return {
197-
user_id: user_id!,
198-
created_at: existing?.created_at || new Date().toISOString(),
199-
name: cal.title,
200-
enabled: existing?.enabled ?? false,
201-
provider: "apple",
202-
source: cal.source.title,
203-
color: colorToCSS(cal.color),
204-
};
205-
},
206-
[user_id],
207-
main.STORE_ID,
208-
);
209-
210191
const { mutate: syncCalendars, isPending } = useMutation({
211192
mutationKey: ["appleCalendars", "sync"],
212193
mutationFn: async () => {
@@ -219,10 +200,27 @@ function useAppleCalendarSelection() {
219200
}
220201
return result.data;
221202
},
222-
onSuccess: (calendars) => {
223-
store?.transaction(() => {
224-
for (const cal of calendars) {
225-
setCalendarRow(cal);
203+
onSuccess: (incomingCalendars) => {
204+
if (!store || !user_id) return;
205+
206+
store.transaction(() => {
207+
for (const cal of incomingCalendars) {
208+
const existingRowId = findCalendarByTrackingId(store, cal.id);
209+
const rowId = existingRowId ?? crypto.randomUUID();
210+
const existing = existingRowId
211+
? store.getRow("calendars", existingRowId)
212+
: null;
213+
214+
store.setRow("calendars", rowId, {
215+
user_id,
216+
created_at: existing?.created_at || new Date().toISOString(),
217+
tracking_id_calendar: cal.id,
218+
name: cal.title,
219+
enabled: existing?.enabled ?? false,
220+
provider: "apple",
221+
source: cal.source.title,
222+
color: colorToCSS(cal.color),
223+
});
226224
}
227225
});
228226
},

apps/desktop/src/hooks/useAutoCloser.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ export function useAutoCloser(
1212
outside?: boolean;
1313
},
1414
) {
15-
const ref = useRef<HTMLDivElement>(null!);
15+
const ref = useRef<HTMLDivElement | null>(null);
1616

1717
const handleClose = useCallback(() => {
1818
onClose();
1919
}, [onClose]);
2020

2121
useHotkeys("esc", handleClose, { enabled: esc }, [handleClose]);
22-
useOnClickOutside(ref, outside ? handleClose : () => {});
22+
useOnClickOutside(
23+
ref as React.RefObject<HTMLDivElement>,
24+
outside ? handleClose : () => {},
25+
);
2326

2427
return ref;
2528
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,24 @@ export interface Ctx {
88
from: Date;
99
to: Date;
1010
calendarIds: Set<string>;
11+
calendarTrackingIdToId: Map<string, string>;
1112
}
1213

1314
export function createCtx(store: Store, queries: Queries<Schemas>): Ctx | null {
1415
const resultTable = queries.getResultTable(QUERIES.enabledAppleCalendars);
1516

1617
const calendarIds = new Set(Object.keys(resultTable));
18+
const calendarTrackingIdToId = new Map<string, string>();
19+
20+
for (const calendarId of calendarIds) {
21+
const calendar = store.getRow("calendars", calendarId);
22+
const trackingId = calendar?.tracking_id_calendar as string | undefined;
23+
if (trackingId) {
24+
calendarTrackingIdToId.set(trackingId, calendarId);
25+
}
26+
}
1727

18-
if (calendarIds.size === 0) {
28+
if (calendarTrackingIdToId.size === 0) {
1929
return null;
2030
}
2131

@@ -32,6 +42,7 @@ export function createCtx(store: Store, queries: Queries<Schemas>): Ctx | null {
3242
from,
3343
to,
3444
calendarIds,
45+
calendarTrackingIdToId,
3546
};
3647
}
3748

apps/desktop/src/services/apple-calendar/fetch/existing.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { Ctx } from "../ctx";
22
import type { ExistingEvent } from "./types";
33

4-
export function fetchExistingEvents(ctx: Ctx): Array<ExistingEvent> {
5-
const events: Array<ExistingEvent> = [];
4+
export function fetchExistingEvents(ctx: Ctx): ExistingEvent[] {
5+
const events: ExistingEvent[] = [];
66

77
ctx.store.forEachRow("events", (rowId, _forEachCell) => {
88
const event = ctx.store.getRow("events", rowId);
@@ -20,6 +20,7 @@ export function fetchExistingEvents(ctx: Ctx): Array<ExistingEvent> {
2020
if (eventDate >= ctx.from && eventDate <= ctx.to) {
2121
events.push({
2222
id: rowId,
23+
tracking_id_event: event.tracking_id_event as string | undefined,
2324
user_id: event.user_id as string | undefined,
2425
created_at: event.created_at as string | undefined,
2526
calendar_id: calendarId,

apps/desktop/src/services/apple-calendar/fetch/incoming.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import type { EventParticipant } from "@hypr/store";
66
import type { Ctx } from "../ctx";
77
import type { IncomingEvent } from "./types";
88

9-
export async function fetchIncomingEvents(
10-
ctx: Ctx,
11-
): Promise<Array<IncomingEvent>> {
9+
export async function fetchIncomingEvents(ctx: Ctx): Promise<IncomingEvent[]> {
10+
const trackingIds = Array.from(ctx.calendarTrackingIdToId.keys());
11+
1212
const results = await Promise.all(
13-
Array.from(ctx.calendarIds).map(async (calendarId) => {
13+
trackingIds.map(async (trackingId) => {
1414
const result = await appleCalendarCommands.listEvents({
15-
calendar_tracking_id: calendarId,
15+
calendar_tracking_id: trackingId,
1616
from: ctx.from.toISOString(),
1717
to: ctx.to.toISOString(),
1818
});
@@ -45,8 +45,8 @@ async function normalizeAppleEvent(event: AppleEvent): Promise<IncomingEvent> {
4545
}
4646

4747
return {
48-
id: event.event_identifier,
49-
calendar_id: event.calendar.id,
48+
tracking_id_event: event.event_identifier,
49+
tracking_id_calendar: event.calendar.id,
5050
title: event.title,
5151
started_at: event.start_date,
5252
ended_at: event.end_date,
Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
import { EventStorage } from "@hypr/store";
22

3-
type EventBaseForSync = { id: string };
3+
export type IncomingEvent = {
4+
tracking_id_event: string;
5+
tracking_id_calendar: string;
6+
title?: string;
7+
started_at?: string;
8+
ended_at?: string;
9+
location?: string;
10+
meeting_link?: string;
11+
description?: string;
12+
participants?: string;
13+
};
414

5-
export type IncomingEvent = EventBaseForSync &
6-
Omit<EventStorage, "user_id" | "created_at">;
7-
8-
export type ExistingEvent = EventBaseForSync & EventStorage;
15+
export type ExistingEvent = {
16+
id: string;
17+
tracking_id_event?: string;
18+
} & EventStorage;

0 commit comments

Comments
 (0)