feat: tweak calendar and clean it up

This commit is contained in:
2026-06-27 14:19:28 +09:30
parent fe1c2f0250
commit eae2e0587c
21 changed files with 1336 additions and 126 deletions
+64
View File
@@ -16,10 +16,20 @@ import {
readGoogleCalendarState,
writeGoogleCalendarState,
} from "@/seqta/utils/googleCalendar/storage";
import {
clampSyncWeeks,
getAutoSyncWeekly,
getSyncWeeksAhead,
} from "@/seqta/utils/googleCalendar/syncSettings";
import type {
GoogleCalendarStatus,
GoogleCalendarSyncResult,
} from "@/seqta/utils/googleCalendar/types";
import {
clearWeeklySyncAlarm,
ensureWeeklySyncAlarm,
registerWeeklySyncAlarmListener,
} from "./googleCalendarWeekly";
function base64UrlEncode(buffer: ArrayBuffer): string {
const bytes = new Uint8Array(buffer);
@@ -197,6 +207,8 @@ async function connectGoogleCalendar(): Promise<GoogleCalendarSyncResult> {
connectedAt: Date.now(),
});
await ensureWeeklySyncAlarm();
return { success: true, configured: true, connected: true };
} catch (err) {
return {
@@ -209,11 +221,16 @@ async function connectGoogleCalendar(): Promise<GoogleCalendarSyncResult> {
async function getGoogleCalendarStatus(): Promise<GoogleCalendarStatus> {
const state = await readGoogleCalendarState();
const syncWeeksAhead = await getSyncWeeksAhead();
const autoSyncWeekly = await getAutoSyncWeekly();
return {
configured: isGoogleCalendarConfigured(),
connected: !!(state.refreshToken || state.accessToken),
lastSyncAt: state.lastSyncAt,
lastWeeklySyncAt: state.lastWeeklySyncAt,
lastSyncOrigin: state.lastSyncOrigin,
syncWeeksAhead,
autoSyncWeekly,
};
}
@@ -222,6 +239,7 @@ export async function handleGoogleCalendarConnect(): Promise<GoogleCalendarSyncR
}
export async function handleGoogleCalendarDisconnect(): Promise<{ success: boolean }> {
await clearWeeklySyncAlarm();
await clearGoogleCalendarState();
return { success: true };
}
@@ -298,4 +316,50 @@ export function registerGoogleCalendarMessageHandlers(
});
return true;
};
handlers.googleCalendarEnsureWeeklyAlarm = (_req, sendResponse, sender) => {
if (rejectUntrusted(sendResponse, sender)) return false;
void ensureWeeklySyncAlarm()
.then(() => sendResponse({ success: true }))
.catch((err) => {
sendResponse({
success: false,
error: err instanceof Error ? err.message : "Could not schedule weekly sync",
});
});
return true;
};
handlers.googleCalendarUpdateSyncSettings = (request, sendResponse, sender) => {
if (rejectUntrusted(sendResponse, sender)) return false;
void (async () => {
const body = request as {
syncWeeksAhead?: number;
autoSyncWeekly?: boolean;
};
const patch: Record<string, unknown> = {};
if (body.syncWeeksAhead != null) {
patch.syncWeeksAhead = clampSyncWeeks(body.syncWeeksAhead);
}
if (body.autoSyncWeekly != null) {
patch.autoSyncWeekly = !!body.autoSyncWeekly;
}
if (Object.keys(patch).length > 0) {
await writeGoogleCalendarState(patch);
}
await ensureWeeklySyncAlarm();
sendResponse({ success: true, ...(await getGoogleCalendarStatus()) });
})().catch((err) => {
sendResponse({
success: false,
error: err instanceof Error ? err.message : "Could not update sync settings",
});
});
return true;
};
}
export function initGoogleCalendarBackground(): void {
registerWeeklySyncAlarmListener();
void ensureWeeklySyncAlarm();
}
+74
View File
@@ -0,0 +1,74 @@
import browser from "webextension-polyfill";
import { verboseLog } from "@/utils/verboseLog";
import {
getAutoSyncWeekly,
GOOGLE_CALENDAR_WEEKLY_ALARM,
markWeeklySyncPending,
} from "@/seqta/utils/googleCalendar/syncSettings";
import { readGoogleCalendarState } from "@/seqta/utils/googleCalendar/storage";
const WEEKLY_PERIOD_MINUTES = 7 * 24 * 60;
function isSeqtaTab(tab: browser.Tabs.Tab): boolean {
const title = tab.title ?? "";
return title.includes("SEQTA Learn") || title.includes("SEQTA Engage");
}
async function isCalendarConnected(): Promise<boolean> {
const state = await readGoogleCalendarState();
return !!(state.refreshToken || state.accessToken);
}
export async function ensureWeeklySyncAlarm(): Promise<void> {
const connected = await isCalendarConnected();
const enabled = await getAutoSyncWeekly();
if (!connected || !enabled) {
await browser.alarms.clear(GOOGLE_CALENDAR_WEEKLY_ALARM);
return;
}
const existing = await browser.alarms.get(GOOGLE_CALENDAR_WEEKLY_ALARM);
if (!existing) {
await browser.alarms.create(GOOGLE_CALENDAR_WEEKLY_ALARM, {
periodInMinutes: WEEKLY_PERIOD_MINUTES,
});
}
}
export async function clearWeeklySyncAlarm(): Promise<void> {
await browser.alarms.clear(GOOGLE_CALENDAR_WEEKLY_ALARM);
}
export async function triggerWeeklySyncOnSeqtaTabs(): Promise<boolean> {
const tabs = await browser.tabs.query({});
const seqtaTabs = tabs.filter((tab) => tab.id != null && isSeqtaTab(tab));
if (seqtaTabs.length === 0) return false;
let delivered = false;
for (const tab of seqtaTabs) {
if (tab.id == null) continue;
try {
await browser.tabs.sendMessage(tab.id, { type: "googleCalendarRunWeeklySync" });
delivered = true;
} catch (err) {
verboseLog("[BetterSEQTA+] Weekly calendar sync message failed for tab:", tab.id, err);
}
}
return delivered;
}
export async function handleWeeklySyncAlarm(): Promise<void> {
if (!(await isCalendarConnected()) || !(await getAutoSyncWeekly())) return;
const delivered = await triggerWeeklySyncOnSeqtaTabs();
if (!delivered) {
await markWeeklySyncPending();
}
}
export function registerWeeklySyncAlarmListener(): void {
browser.alarms.onAlarm.addListener((alarm) => {
if (alarm.name !== GOOGLE_CALENDAR_WEEKLY_ALARM) return;
void handleWeeklySyncAlarm();
});
}