2025-06-30 18:58:24 +02:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
*/
|
2025-06-14 19:22:53 +02:00
|
|
|
import { Info } from "~/shared/utils/luxon";
|
2025-05-26 13:53:11 +02:00
|
|
|
|
|
|
|
interface SyncOperation {
|
|
|
|
controller: AbortController,
|
2025-06-14 19:22:53 +02:00
|
|
|
promise: Promise<Ref<ClientSchedule>>,
|
2025-05-26 13:53:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export const useSchedulesStore = defineStore("schedules", () => {
|
|
|
|
const sessionStore = useSessionStore();
|
2025-06-14 19:22:53 +02:00
|
|
|
const accountStore = useAccountStore();
|
2025-05-26 13:53:11 +02:00
|
|
|
|
|
|
|
const state = {
|
|
|
|
activeScheduleId: ref<number | undefined>(111),
|
2025-06-14 19:22:53 +02:00
|
|
|
schedules: ref<Map<number, Ref<ClientSchedule>>>(new Map()),
|
2025-05-26 13:53:11 +02:00
|
|
|
pendingSyncs: ref<Map<number, SyncOperation>>(new Map()),
|
|
|
|
};
|
|
|
|
|
|
|
|
const getters = {
|
|
|
|
activeSchedule: computed(() => {
|
|
|
|
if (state.activeScheduleId.value === undefined)
|
|
|
|
throw Error("No active schedule");
|
|
|
|
const schedule = state.schedules.value.get(state.activeScheduleId.value);
|
|
|
|
if (!schedule)
|
|
|
|
throw Error("Active schedule has not been fetched");
|
|
|
|
return schedule;
|
|
|
|
}),
|
|
|
|
};
|
|
|
|
|
|
|
|
const actions = {
|
|
|
|
async fetch(id: number) {
|
|
|
|
if (id !== 111) { throw Error("invalid id"); }
|
|
|
|
console.log("schedules store fetch", id);
|
|
|
|
const schedule = state.schedules.value.get(id);
|
|
|
|
if (schedule) {
|
|
|
|
console.log("return cached");
|
|
|
|
return schedule;
|
|
|
|
}
|
|
|
|
const pending = state.pendingSyncs.value.get(id);
|
|
|
|
if (pending) {
|
|
|
|
console.log("return pending");
|
|
|
|
return pending.promise;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log("return new fetch");
|
|
|
|
const requestFetch = useRequestFetch();
|
|
|
|
const controller = new AbortController();
|
2025-06-14 19:22:53 +02:00
|
|
|
const zone = Info.normalizeZone(accountStore.activeTimezone);
|
|
|
|
const locale = accountStore.activeLocale;
|
2025-05-26 13:53:11 +02:00
|
|
|
const promise = (async () => {
|
|
|
|
try {
|
2025-06-14 19:22:53 +02:00
|
|
|
const apiSchedule = await requestFetch("/api/schedule", { signal: controller.signal });
|
|
|
|
if (apiSchedule.deleted) {
|
|
|
|
throw new Error("Unexpecetd deleted schedule");
|
|
|
|
}
|
|
|
|
const schedule = ref(ClientSchedule.fromApi(apiSchedule, { zone, locale })) as Ref<ClientSchedule>;
|
2025-05-26 13:53:11 +02:00
|
|
|
state.schedules.value.set(id, schedule);
|
|
|
|
state.pendingSyncs.value.delete(id);
|
|
|
|
return schedule;
|
|
|
|
} catch (err: any) {
|
|
|
|
if (err.name !== "AbortError")
|
|
|
|
state.pendingSyncs.value.delete(id);
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
state.pendingSyncs.value.set(id, {
|
|
|
|
controller,
|
|
|
|
promise,
|
|
|
|
});
|
|
|
|
return promise;
|
|
|
|
},
|
|
|
|
async resync(id: number) {
|
|
|
|
if (id !== 111) { throw Error("invalid id"); }
|
|
|
|
const pending = state.pendingSyncs.value.get(id);
|
|
|
|
if (pending) {
|
|
|
|
pending.controller.abort();
|
|
|
|
}
|
|
|
|
state.schedules.value.delete(id);
|
|
|
|
state.pendingSyncs.value.delete(id);
|
|
|
|
await actions.fetch(id);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
watch(() => sessionStore.id, (id, oldId) => {
|
|
|
|
for (const [scheduleId, pending] of state.pendingSyncs.value) {
|
|
|
|
console.log("Aborting pending schedule sync", scheduleId, "due session.id change from", oldId, "to", id);
|
|
|
|
pending.controller.abort();
|
|
|
|
state.pendingSyncs.value.delete(scheduleId);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
appEventSource?.addEventListener("update", (event) => {
|
2025-06-11 21:05:17 +02:00
|
|
|
if (event.data.type !== "schedule-update") {
|
|
|
|
return;
|
|
|
|
}
|
2025-05-26 13:53:11 +02:00
|
|
|
const schedule = state.schedules.value.get(111);
|
2025-06-11 21:05:17 +02:00
|
|
|
const update = event.data.data;
|
|
|
|
// XXX validate updatedFrom/updatedAt here
|
|
|
|
if (schedule && !schedule.value.deleted && !update.deleted) {
|
2025-06-14 19:22:53 +02:00
|
|
|
const zone = Info.normalizeZone(accountStore.activeTimezone);
|
|
|
|
const locale = accountStore.activeLocale;
|
2025-06-23 22:46:39 +02:00
|
|
|
schedule.value.apiUpdate(update, { zone, locale })
|
2025-05-26 13:53:11 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return { ...state, ...getters, ...actions };
|
|
|
|
});
|