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

78 lines
2.6 KiB
Markdown

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