diff options
| author | Fuwn <[email protected]> | 2026-03-28 06:20:36 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-03-28 06:20:36 +0000 |
| commit | 78bf2502cbf1e26980abf6c0ffb7f0c74b917a3b (patch) | |
| tree | 9f4a84c90fa7fcfa945f1c9e0e637ba80b9cca79 | |
| parent | fix(cache): preserve hydrated client state (diff) | |
| download | due.moe-78bf2502cbf1e26980abf6c0ffb7f0c74b917a3b.tar.xz due.moe-78bf2502cbf1e26980abf6c0ffb7f0c74b917a3b.zip | |
fix(notifications): support per-device push subscriptions
| -rw-r--r-- | src/lib/Database/SB/User/notifications.ts | 41 | ||||
| -rw-r--r-- | src/lib/Utility/fingerprint.ts | 2 | ||||
| -rw-r--r-- | supabase/notifications.sql | 9 | ||||
| -rw-r--r-- | supabase/schema.sql | 5 |
4 files changed, 50 insertions, 7 deletions
diff --git a/src/lib/Database/SB/User/notifications.ts b/src/lib/Database/SB/User/notifications.ts index 058171a9..21ad827b 100644 --- a/src/lib/Database/SB/User/notifications.ts +++ b/src/lib/Database/SB/User/notifications.ts @@ -8,6 +8,16 @@ export interface UserNotifications { fingerprint: string; } +const subscriptionEndpoint = (subscription: JSON) => { + if (typeof subscription !== "object" || subscription === null) return null; + + return "endpoint" in subscription && + typeof subscription.endpoint === "string" && + subscription.endpoint.length + ? subscription.endpoint + : null; +}; + export const getUserSubscription = async (userId: number) => await sb.from("user_notifications").select("*").eq("user_id", userId); @@ -33,13 +43,38 @@ export const setUserSubscription = async ( userId: number, subscription: JSON, fingerprint: string, -) => - await sb.from("user_notifications").upsert( +) => { + const endpoint = subscriptionEndpoint(subscription); + + if (endpoint) { + const { data } = await sb + .from("user_notifications") + .select("fingerprint, subscription") + .eq("user_id", userId); + + const staleFingerprints = + data + ?.filter( + (existing) => + existing.fingerprint !== fingerprint && + subscriptionEndpoint(existing.subscription as JSON) === endpoint, + ) + .map((existing) => existing.fingerprint) ?? []; + + await Promise.all( + staleFingerprints.map(async (staleFingerprint) => { + await deleteUserSubscription(userId, staleFingerprint); + }), + ); + } + + return await sb.from("user_notifications").upsert( { user_id: userId, updated_at: new Date().toISOString(), subscription: subscription, fingerprint, }, - { onConflict: "user_id" }, + { onConflict: "user_id,fingerprint" }, ); +}; diff --git a/src/lib/Utility/fingerprint.ts b/src/lib/Utility/fingerprint.ts index 73a9a978..5af258d0 100644 --- a/src/lib/Utility/fingerprint.ts +++ b/src/lib/Utility/fingerprint.ts @@ -14,5 +14,5 @@ export const getFingerprint = () => navigator === null || navigator === void 0 ? void 0 : navigator.hardwareConcurrency - }-${new Date().getTimezoneOffset()}`, + }`, ); diff --git a/supabase/notifications.sql b/supabase/notifications.sql new file mode 100644 index 00000000..81905a29 --- /dev/null +++ b/supabase/notifications.sql @@ -0,0 +1,9 @@ +ALTER TABLE public.user_notifications + DROP CONSTRAINT IF EXISTS user_notifications_user_id_key; + +ALTER TABLE public.user_notifications + DROP CONSTRAINT IF EXISTS user_notifications_user_id_fingerprint_key; + +ALTER TABLE public.user_notifications + ADD CONSTRAINT user_notifications_user_id_fingerprint_key + UNIQUE (user_id, fingerprint); diff --git a/supabase/schema.sql b/supabase/schema.sql index 7648b7e4..9dc0934a 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -489,11 +489,11 @@ ALTER TABLE ONLY "public"."user_notifications" -- --- Name: user_notifications user_notifications_user_id_key; Type: CONSTRAINT; Schema: public; Owner: postgres +-- Name: user_notifications user_notifications_user_id_fingerprint_key; Type: CONSTRAINT; Schema: public; Owner: postgres -- ALTER TABLE ONLY "public"."user_notifications" - ADD CONSTRAINT "user_notifications_user_id_key" UNIQUE ("user_id"); + ADD CONSTRAINT "user_notifications_user_id_fingerprint_key" UNIQUE ("user_id", "fingerprint"); -- @@ -1481,4 +1481,3 @@ ALTER DEFAULT PRIVILEGES FOR ROLE "postgres" IN SCHEMA "public" GRANT SELECT,INS -- -- \unrestrict ayCRchpk9QYAkH0FR7MzsPfEkgyq5Y02kQfarpa01RE4AwEybP0seWKhOVsHiST - |