diff --git a/src/app/api/devices/__tests__/route.test.ts b/src/app/api/devices/__tests__/route.test.ts index d82648c3b..220b605c9 100644 --- a/src/app/api/devices/__tests__/route.test.ts +++ b/src/app/api/devices/__tests__/route.test.ts @@ -221,21 +221,8 @@ describe("POST /api/devices", () => { }; expect(args.data.apnsToken).toBe("deadbeef".repeat(8)); expect(args.data.apnsEnvironment).toBe("sandbox"); - }); - - it("ends a pause that was waiting for a paired iPhone", async () => { // A reminder with no iPhone paired rests the APNs channel for an hour; - // pairing one must not leave the next reminder waiting it out. - vi.mocked(getSession).mockResolvedValue(SESSION_OK as never); - const res = await POST( - req({ - token: "abcd1234efgh5678", - bundleId: "io.healthlog.app", - apnsToken: "deadbeef".repeat(8), - apnsEnvironment: "sandbox", - }), - ); - expect(res.status).toBe(201); + // pairing one ends that rest, so the next reminder does not wait it out. expect(prisma.notificationChannel.updateMany).toHaveBeenCalledWith({ where: { userId: "user-1", diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx index 89eae5a58..579209b0d 100644 --- a/src/app/auth/login/page.tsx +++ b/src/app/auth/login/page.tsx @@ -24,7 +24,7 @@ import { OIDC_MFA_COOKIE, OIDC_MFA_COOKIE_PATH } from "@/lib/auth/oidc-cookie"; import { isDashboardSnapshotEnabled } from "@/lib/dashboard/snapshot-flag"; import { prefetchDashboardSnapshot } from "@/lib/queries/use-dashboard-snapshot"; import { clearOfflineCachesForSessionEnd } from "@/lib/pwa/query-persister"; -import { leaveForSingleSignOn } from "@/lib/auth/leave-for-single-sign-on"; +import { clearOfflineCachesBeforeLeaving } from "@/lib/pwa/clear-before-leaving"; import { sanitizeSameOriginPath } from "@/lib/url-safety"; import { localizedSignInError } from "@/lib/api/localized-error"; import { formatDate } from "@/lib/format"; @@ -184,7 +184,9 @@ export default function LoginPage() { params.set("client_proto", clientTransport.protocol); params.set("client_host", clientTransport.host); } - await leaveForSingleSignOn(`/api/auth/oidc/login?${params}`); + // The session starts on the way back; the last account's copies go first. + await clearOfflineCachesBeforeLeaving(); + loadDocument(`/api/auth/oidc/login?${params}`); } function getRedirectTarget(): string { diff --git a/src/components/auth/claim-profile.tsx b/src/components/auth/claim-profile.tsx index 695bf2ef5..360d5a1db 100644 --- a/src/components/auth/claim-profile.tsx +++ b/src/components/auth/claim-profile.tsx @@ -21,7 +21,8 @@ import { import { ApiError } from "@/lib/api/api-fetch"; import { showErrorToast } from "@/lib/errors/error-toast"; import { useFormatters, useTranslations } from "@/lib/i18n/context"; -import { leaveForSingleSignOn } from "@/lib/auth/leave-for-single-sign-on"; +import { loadDocument } from "@/lib/navigation/load-document"; +import { clearOfflineCachesBeforeLeaving } from "@/lib/pwa/clear-before-leaving"; import { useClaimProfile, useClaimProfileSso, @@ -110,7 +111,9 @@ export function ClaimProfile({ next.searchParams.set("client_proto", clientTransport.protocol); next.searchParams.set("client_host", clientTransport.host); } - await leaveForSingleSignOn(`${next.pathname}${next.search}`); + // The session starts on the way back; the last account's copies go first. + await clearOfflineCachesBeforeLeaving(); + loadDocument(`${next.pathname}${next.search}`); }, }, ); diff --git a/src/lib/auth/leave-for-single-sign-on.ts b/src/lib/auth/leave-for-single-sign-on.ts deleted file mode 100644 index 07b2bf363..000000000 --- a/src/lib/auth/leave-for-single-sign-on.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { settleWithin } from "@/lib/async/settle-within"; -import { loadDocument } from "@/lib/navigation/load-document"; -import { clearOfflineCachesForSessionEnd } from "@/lib/pwa/query-persister"; - -/** - * Leave for the identity provider. The session starts on the way back, so - * the last account's offline copies go first. The wait for them is capped: - * WebKit can leave an IndexedDB open hanging after the installed app returns - * from the background, and the button must still work then. - */ -export async function leaveForSingleSignOn(url: string): Promise { - await settleWithin(clearOfflineCachesForSessionEnd(), 1_500, undefined); - loadDocument(url); -} diff --git a/src/lib/pwa/clear-before-leaving.ts b/src/lib/pwa/clear-before-leaving.ts new file mode 100644 index 000000000..460114f1c --- /dev/null +++ b/src/lib/pwa/clear-before-leaving.ts @@ -0,0 +1,12 @@ +import { settleWithin } from "@/lib/async/settle-within"; +import { clearOfflineCachesForSessionEnd } from "@/lib/pwa/query-persister"; + +/** + * The session-end wipe before the page leaves for the identity provider, + * where the next session starts. The wait is capped: WebKit can leave an + * IndexedDB open hanging after the installed app returns from the + * background, and the button must still work then. + */ +export function clearOfflineCachesBeforeLeaving(): Promise { + return settleWithin(clearOfflineCachesForSessionEnd(), 1_500, undefined); +}