Files
familysync/.planning/quick/260611-tfc-fix-wr-01-sw-ts-notificationclick-openwi/260611-tfc-SUMMARY.md
T

2.6 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
quick-260611-tfc 01 pwa-sw
service-worker
push-notifications
bug-fix
wr-01
requires provides affects
reachable-openWindow-fallback-notificationclick
apps/pwa/src/sw.ts
added patterns
promise-chain-catch-fallback
created modified
apps/pwa/src/sw.ts
Canonical fix shape: .then(navigated => openWindow if null) + .catch(() => openWindow) on the focus/navigate chain — keeps the returned promise intact for no-floating-promises
duration completed
~6 min 2026-06-12

Quick Task 260611-tfc: Fix WR-01 sw.ts notificationclick openWindow Fallback

One-liner: notificationclick handler extended with .then(navigated===null → openWindow) + .catch(→ openWindow) so the new-window fallback is reachable on both focus rejection and null navigate.

What Was Done

Fixed WR-01 from .planning/phases/13-real-lint-gate-eslint/13-REVIEW.md.

The focusable-client return inside the for (const client of clientList) loop was:

return client.focus().then(() => client.navigate(url));

Two failure modes made the openWindow fallback unreachable:

  1. navigate() resolving null (browser rejected the navigation) — the chain resolved successfully, so the post-loop fallback was never reached.
  2. focus() or navigate() rejecting (window closed between matchAll and focus) — the rejection propagated into event.waitUntil, marking the click as handled without opening a new window.

Applied the canonical replacement per the review:

return client
  .focus()
  .then(() => client.navigate(url))
  .then((navigated) => {
    if (navigated === null && self.clients.openWindow)
      return self.clients.openWindow(url);
  })
  .catch(() => {
    if (self.clients.openWindow) return self.clients.openWindow(url);
  });

All surrounding behaviour preserved: event.notification.close(), url extraction + eslint-disable comments, matchAll options, and the post-loop no-focusable-client fallback.

Verification — Exit Codes

Command Exit
pnpm lint 0
pnpm typecheck 0
pnpm --filter @familysync/pwa test 0 (191/191 pass)
pnpm --filter @familysync/pwa build 0 (dist/sw.js emitted)

Deviations from Plan

None — plan executed exactly as written.

Self-Check: PASSED

  • apps/pwa/src/sw.ts modified: confirmed
  • Commit af78ccc exists: confirmed
  • git diff --diff-filter=D HEAD~1 HEAD: no deletions
  • Only apps/pwa/src/sw.ts changed (git diff --stat = 1 file, 10 insertions, 1 deletion)