Localize the archive, add reminders, named challenges and a funnel
Build and push image / build (push) Failing after 15m20s
Build and push image / build (push) Failing after 15m20s
Archive and answer pages now exist in all six languages and are prerendered instead of rendered per request, and each answer page carries a sentence of real context per player (club, league, nationality, former clubs, honours) plus Article and BreadcrumbList JSON-LD. The sitemap grows from 121 to 691 URLs, every one with hreflang alternates. Adds an optional display name to shared results, so a challenge link reads "<name> challenges you" in the page, the OG image and the head-to-head verdict; the name is sanitised both when written and when parsed back. Adds an opt-in daily reminder (Web Push without payload, so no content encryption) and a PWA install prompt shown once the day is finished, plus first-party event ingestion at /api/events that mirrors GA4 so the funnel survives ad blockers. Wires the missing N8N_PLAYS_WEBHOOK_URL into the deployment along with the new events, push and search-verification secrets, and ships the n8n workflows, the Postgres schema with funnel/retention views, and an IndexNow submitter. Refs #2 #3 #5 #6 #7 #8 #9 #10 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QvQ1ErZNRUcWgCEkJ9uyrn
This commit is contained in:
@@ -0,0 +1 @@
|
||||
21259b3f13afc0f590c2e2d6611d64d9
|
||||
@@ -0,0 +1,74 @@
|
||||
// hidden11 service worker.
|
||||
//
|
||||
// Scope is deliberately narrow: it exists to deliver the daily reminder, not to
|
||||
// cache the app. The push messages are sent WITHOUT a payload (the sender only
|
||||
// needs a VAPID header, no content encryption), so the notification copy is
|
||||
// written into the Cache API when the user subscribes and read back here — a
|
||||
// service worker cannot reach localStorage.
|
||||
|
||||
const MESSAGE_CACHE = "hidden11-push";
|
||||
const MESSAGE_KEY = "/__push-message";
|
||||
|
||||
const FALLBACK = {
|
||||
title: "hidden11",
|
||||
body: "Today's hidden XI is live.",
|
||||
url: "/",
|
||||
};
|
||||
|
||||
async function reminderMessage() {
|
||||
try {
|
||||
const cache = await caches.open(MESSAGE_CACHE);
|
||||
const stored = await cache.match(MESSAGE_KEY);
|
||||
if (!stored) return FALLBACK;
|
||||
const data = await stored.json();
|
||||
return {
|
||||
title: data.title || FALLBACK.title,
|
||||
body: data.body || FALLBACK.body,
|
||||
url: data.url || FALLBACK.url,
|
||||
};
|
||||
} catch {
|
||||
return FALLBACK;
|
||||
}
|
||||
}
|
||||
|
||||
self.addEventListener("install", () => {
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener("activate", (event) => {
|
||||
event.waitUntil(self.clients.claim());
|
||||
});
|
||||
|
||||
self.addEventListener("push", (event) => {
|
||||
event.waitUntil(
|
||||
(async () => {
|
||||
const message = await reminderMessage();
|
||||
await self.registration.showNotification(message.title, {
|
||||
body: message.body,
|
||||
icon: "/icon-192.png",
|
||||
badge: "/icon-192.png",
|
||||
tag: "hidden11-daily",
|
||||
renotify: true,
|
||||
data: { url: message.url },
|
||||
});
|
||||
})(),
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener("notificationclick", (event) => {
|
||||
event.notification.close();
|
||||
const target = event.notification.data?.url || FALLBACK.url;
|
||||
|
||||
event.waitUntil(
|
||||
(async () => {
|
||||
const allClients = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
|
||||
const existing = allClients.find((client) => client.url.includes(self.location.origin));
|
||||
if (existing) {
|
||||
await existing.focus();
|
||||
if ("navigate" in existing) await existing.navigate(target);
|
||||
return;
|
||||
}
|
||||
await self.clients.openWindow(target);
|
||||
})(),
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user