Skip to content
FeaturesPricingAffiliateBlogHelpAboutContact
Get StartedSign In
Back to Blog
industry2026-05-2411 min read

The order-tracker PWA opens a new tab on every push — focus-existing-tab bug (PR #639 IV F2)

Aanya (41) runs Curry Lane 56-cover modern South Indian on Birmingham Digbeth Custard Factory Quarter. 14 years family curry business, 18 months thMenu Pro tier. Customer Web Push (PR #309) feature letting diners get phone notifications as orders move preparing → ready → served. Tuesday morning Google Reviews check: **"⭐ 1 star — Daniel S.: Food was excellent. But don t use their order tracking app — every notification opens a NEW tab. Ordered dinner ended up with 6 tabs open my phone got laggy."** thMenu support reproduce iOS Safari + Android Chrome: Daniel opens /order/xyz tab, each push tap clients.openWindow opens new tab, 6 pushes = 6 tabs, iPhone Safari background tab heartbeats really did get laggy. **Wrong theory**: push payload URL different ?t= timestamp each time (cache-bust). Deliberate design but SW match logic should compare pathname-only. **Right theory**: apps/web-menu/src/app/sw.ts notificationclick handler built targetUrl as resolved.pathname + resolved.search + resolved.hash (e.g. /order/xyz?t=1747840000). Match compare: u.pathname === targetUrl → /order/xyz === /order/xyz?t=1747840000 → **FALSE** (pathname doesn t include query); client.url === targetUrl → full URL different timestamp → **FALSE**. Both FALSE → openWindow new tab. **PR #639 batch IV F2** fix single concept: pathname-only compare + reserve full URL for openWindow only. `let targetPath = resolved.pathname; let targetUrl = resolved.pathname + resolved.search + resolved.hash; for (const client of allClients) { const u = new URL(client.url); if (u.pathname === targetPath) return client.focus(); } return clients.openWindow(targetUrl);` Now u.pathname (/order/xyz) matches targetPath (/order/xyz) correctly. Existing tab focuses. Query/hash differences don t break match. **Web-admin SW unaffected** because different match shape: `if (client.url.includes("/dashboard")) { await client.focus(); await client.navigate(targetUrl); }` — substring check naturally ignores query/hash + navigate re-sets full URL. Customer-facing SPA router pushes state via postMessage not SW navigate; architecture decision sound but match-logic compare broken. Aanya replied directly on Daniel s Google Business review + drink-on-house offer. Daniel: "Thanks for fixing it. Still 5 stars on the food." A week later he ordered again — push focused his existing tab. Review updated to **⭐⭐⭐⭐⭐ "Push bug fixed, smooth UX now."** Aanya s Google Business avg 4.6 → 4.7. Daniel s updated review became concrete confidence signal for new customers. Pattern: **PWA service worker notificationclick handler must EXCLUDE query string + hash from compare; pathname-only compare; reserve full URL for openWindow arg.** Implementation checklist: (1) build targetPath + targetUrl separately; (2) match u.pathname === targetPath only; (3) openWindow(targetUrl) only when no match; (4) origin check first resolved.origin === self.location.origin; (5) multi-tab support; (6) postMessage to focused client SPA router state animation smooth. Berfin Eskisehir Anadolu Doner version with same 6-tab spiral 1-star Google review.

th

thMenu Team

thmenu.com

Found this helpful? Share it.