mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
fix(push): send the browser's subscription again when the card opens
The Web Push card reads its state from the browser only. When the server had lost the row, or the channel had been switched off, the card said enabled and no reminder came. Opening the card now posts the subscription the browser holds, which stores the row and turns the channel back on. A refused post (demo, push off instance-wide) changes nothing on screen.
This commit is contained in:
1 parent
d5c0e759b8
commit
6914f844be
2 files changed
+87
-14
No files matched your search
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* The Web Push card with a subscription the browser already holds.
|
||||
*
|
||||
* The card reads its state from the browser alone. When the server row was
|
||||
* lost, or the channel had been switched off by failed sends, the card said
|
||||
* "enabled" and no reminder came; opening it now sends the subscription
|
||||
* again, which stores the row and turns the channel back on.
|
||||
*/
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { stubFetch } from "../../../../tests/browser/fetch-stub";
|
||||
import { renderWithApp } from "../../../../tests/browser/render";
|
||||
|
||||
import { WebPushCard } from "../web-push-card";
|
||||
|
||||
const SUBSCRIPTION = {
|
||||
endpoint: "https://push.example.test/send/abc",
|
||||
keys: { p256dh: "p256dh-key", auth: "auth-key" },
|
||||
};
|
||||
|
||||
function holdSubscription(): void {
|
||||
const subscription = { toJSON: () => SUBSCRIPTION };
|
||||
// Headless Chromium answers "denied" without a prompt.
|
||||
vi.spyOn(Notification, "permission", "get").mockReturnValue("granted");
|
||||
vi.spyOn(navigator.serviceWorker, "getRegistration").mockResolvedValue({
|
||||
pushManager: { getSubscription: async () => subscription },
|
||||
} as unknown as ServiceWorkerRegistration);
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("WebPushCard", () => {
|
||||
it("sends the browser's existing subscription to the server when it opens", async () => {
|
||||
holdSubscription();
|
||||
const api = stubFetch().on("POST", "/api/notifications/web-push", {
|
||||
data: { subscribed: true },
|
||||
});
|
||||
|
||||
const { screen } = await renderWithApp(<WebPushCard />);
|
||||
|
||||
await expect
|
||||
.poll(() => api.calls("POST", "/api/notifications/web-push").length)
|
||||
.toBe(1);
|
||||
expect(api.calls("POST", "/api/notifications/web-push")[0].body).toEqual(
|
||||
SUBSCRIPTION,
|
||||
);
|
||||
await expect.element(screen.getByText("Disable push")).toBeVisible();
|
||||
});
|
||||
|
||||
it("keeps showing push as on when that request fails", async () => {
|
||||
holdSubscription();
|
||||
stubFetch().on("POST", "/api/notifications/web-push", {
|
||||
status: 403,
|
||||
error: "Demo mode: modifications are disabled",
|
||||
});
|
||||
|
||||
const { screen } = await renderWithApp(<WebPushCard />);
|
||||
|
||||
await expect.element(screen.getByText("Disable push")).toBeVisible();
|
||||
expect(document.querySelector('[role="alert"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -38,6 +38,27 @@ export function needsHomeScreen(): boolean {
|
||||
return appleTouch && !window.matchMedia("(display-mode: standalone)").matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store this browser's subscription for the signed-in account. Also run for a
|
||||
* subscription the browser already holds, so a row the server lost or gave
|
||||
* to another account on this browser comes back, and a channel switched off
|
||||
* by failed sends is turned on again.
|
||||
*/
|
||||
function saveSubscription(subscription: PushSubscription): Promise<Response> {
|
||||
const subJson = subscription.toJSON();
|
||||
return apiFetchRaw("/api/notifications/web-push", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
endpoint: subJson.endpoint,
|
||||
keys: {
|
||||
p256dh: subJson.keys?.p256dh ?? "",
|
||||
auth: subJson.keys?.auth ?? "",
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
type Unsupported = "browser" | "install" | null;
|
||||
|
||||
export function WebPushCard() {
|
||||
@@ -73,6 +94,7 @@ export function WebPushCard() {
|
||||
if (registration) {
|
||||
const subscription = await registration.pushManager.getSubscription();
|
||||
setIsSubscribed(!!subscription);
|
||||
if (subscription) void saveSubscription(subscription).catch(() => {});
|
||||
}
|
||||
} catch {
|
||||
// Ignore errors during check
|
||||
@@ -111,20 +133,7 @@ export function WebPushCard() {
|
||||
applicationServerKey: urlBase64ToArrayBuffer(vapidPublicKey),
|
||||
});
|
||||
|
||||
const subJson = subscription.toJSON();
|
||||
|
||||
const res = await apiFetchRaw("/api/notifications/web-push", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
endpoint: subJson.endpoint,
|
||||
keys: {
|
||||
p256dh: subJson.keys?.p256dh ?? "",
|
||||
auth: subJson.keys?.auth ?? "",
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const res = await saveSubscription(subscription);
|
||||
if (res.ok) {
|
||||
setIsSubscribed(true);
|
||||
setMsg(t("settings.webPushSubscribed"));
|
||||
|
||||
Reference in new issue
Block a user