teecup/frontend/lib/pwa-install.ts

49 lines
1.6 KiB
TypeScript
Raw Permalink Normal View History

Update Todos Bygg PWA-installasjonsoppfordring (Android beforeinstallprompt + iOS instruksjonsbanner) Design og bygg flere flighter i én frittstående runde (løs gruppering, retning 1) Design og bygg scramble/greensome-statistikk (valgt utslag per spiller) Bygg push-varsler til telefonens OS (VAPID, push_subscription, service worker) Scratch-verifiser backend for alle fire (27/27 sjekker gjort) Browserverifiser alle fire i nettleser mot scratch-backend Rull ut mot ekte systemer (migrasjoner + containere), med bekreftelse Oppdater CLAUDE.md/FEATURE_BACKLOG.md/ARCHITECTURE_DECISIONS.md Alle fire er ferdigstilt, verifisert og live. Kort oppsummert: PWA-installasjonsoppfordring — ekte beforeinstallprompt-fanging, Android-knapp + iOS-instruksjonsbanner, vist på dashbordet. Bekreftet at Chrome faktisk fyrte eventet i testøkten. Flere flighter i én frittstående runde — løs gruppering av separate runder (delt UUID), "+ Legg til en flight til" gjenbruker hele opprett-flyten forhåndsutfylt, nytt kombinert leaderboard på tvers av flighter. Testet fra knappetrykk til ferdig runde, bekreftet i database. Scramble/greensome: valgt utslag per spiller — nytt felt i hull-registreringen (helt valgfritt), "Utslag brukt"-oppsummering på statistikksiden. Bygget for frittstående runder; org-turneringer bevisst utenfor omfang denne runden (notert i backloggen). Push-varsler til telefonens OS — Web Push/VAPID, ny av/på-bryter i kontoinnstillinger, dekker alle fire varseltyper automatisk. VAPID-nøkler generert og lagt i .env uten å vises i chatten. Én ærlig begrensning: ekte OS-varsel-levering med innvilget tillatelse kunne ikke testes i denne automatiserte nettleserøkten (tillatelsen var forhåndsblokkert av miljøet) — verdt å teste selv på en ekte enhet. 27/27 scratch-sjekker, full nettleser-gjennomgang uten konsollfeil, migrasjoner 035–037 kjørt mot ekte teecup_db, begge containere redeployet og verifisert, teeoff.no upåvirket. Alle tre .md-filene er oppdatert med full detalj.
2026-07-28 22:34:16 +02:00
// Deler ett globalt "beforeinstallprompt"-event mellom root layout (som
// fanger det så tidlig som mulig via SwRegister, ADR-028) og
// InstallPrompt-komponenten på dashbordet (som faktisk viser UI-et).
// Nettleseren fyrer eventet KUN én gang per side-liv og kun hvis ingen har
// kalt preventDefault() før -- må derfor fanges globalt, ikke lokalt i en
// komponent som kan mountes for sent.
export type BeforeInstallPromptEvent = Event & {
prompt: () => Promise<void>
userChoice: Promise<{ outcome: "accepted" | "dismissed"; platform: string }>
}
type Listener = (event: BeforeInstallPromptEvent | null) => void
let deferredEvent: BeforeInstallPromptEvent | null = null
const listeners = new Set<Listener>()
export function initInstallPromptCapture() {
if (typeof window === "undefined") return
const w = window as typeof window & { __teecupInstallCaptureInitialized?: boolean }
if (w.__teecupInstallCaptureInitialized) return
w.__teecupInstallCaptureInitialized = true
window.addEventListener("beforeinstallprompt", (event) => {
event.preventDefault()
deferredEvent = event as BeforeInstallPromptEvent
listeners.forEach((l) => l(deferredEvent))
})
window.addEventListener("appinstalled", () => {
deferredEvent = null
listeners.forEach((l) => l(null))
})
}
export function getDeferredInstallPrompt() {
return deferredEvent
}
export function clearDeferredInstallPrompt() {
deferredEvent = null
}
export function subscribeInstallPrompt(listener: Listener) {
listeners.add(listener)
return () => {
listeners.delete(listener)
}
}