Web Push: goal, kickoff and full-time alerts for a followed team
Fully progressive: without VAPID keys in the environment the API says 404, the bell never renders, and the service worker addition (a push handler via workbox importScripts — no SW strategy change) is inert. With keys: a bell on each team page requests permission, subscribes (one team per device) and the server diffs fixture states on every broadcast to push Kickoff / Goal / Full time to that team's followers. Dead endpoints (404/410) self-clean. Round-trip verified locally with dev VAPID keys; disabled mode verified too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -93,6 +93,13 @@ CREATE TABLE IF NOT EXISTS prediction_snapshots (
|
||||
market_json TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS push_subs (
|
||||
endpoint TEXT PRIMARY KEY,
|
||||
json TEXT NOT NULL,
|
||||
team TEXT,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS inplay_history (
|
||||
fixture_num INTEGER NOT NULL,
|
||||
minute INTEGER NOT NULL,
|
||||
@@ -147,6 +154,31 @@ export function cacheSet(key: string, value: string, ttl: number): void {
|
||||
.run(key, value, Date.now(), ttl);
|
||||
}
|
||||
|
||||
// ---- Web Push subscriptions (goal/kickoff notifications, opt-in per team) ----
|
||||
export interface PushSub {
|
||||
endpoint: string;
|
||||
json: string;
|
||||
team: string | null;
|
||||
}
|
||||
|
||||
export function addPushSub(endpoint: string, json: string, team: string | null): void {
|
||||
db()
|
||||
.prepare('INSERT OR REPLACE INTO push_subs (endpoint, json, team, created_at) VALUES (?, ?, ?, ?)')
|
||||
.run(endpoint, json, team, Date.now());
|
||||
}
|
||||
|
||||
export function removePushSub(endpoint: string): void {
|
||||
db().prepare('DELETE FROM push_subs WHERE endpoint = ?').run(endpoint);
|
||||
}
|
||||
|
||||
export function pushSubsForTeams(teams: string[]): PushSub[] {
|
||||
if (teams.length === 0) return [];
|
||||
const marks = teams.map(() => '?').join(',');
|
||||
return db()
|
||||
.prepare(`SELECT endpoint, json, team FROM push_subs WHERE team IN (${marks})`)
|
||||
.all(...teams) as unknown as PushSub[];
|
||||
}
|
||||
|
||||
// ---- in-play win-probability history (the momentum curve on match pages) ----
|
||||
export interface InplayPoint {
|
||||
minute: number;
|
||||
|
||||
Reference in New Issue
Block a user