In App Notifications
CI / ci (push) Canceled after 0s

Policies section (business-details-form.tsx)

Late Arrival Policy, Booking Terms and Privacy Policy URL are gone. The section now has Cancellation Policy plus the Terms & Privacy name and email dropdowns.
Their database columns stay, so nothing is lost. Saving no longer touches them.

Email and push only

One switch, IN_APP_NOTIFICATIONS_VISIBLE in in-app.ts, is set to false and controls everything below.

Bell icon: gone for customers, staff and owners, including the unread badge.
Notification pages: /notifications, /staff/notifications and /owner/notifications now return 404.

Settings: Settings → Notifications shows only Email and Push toggles.
Testing: I checked all of this in a local test app as the owner.
This commit is contained in:
Stefan Lazic committed 2026-10-17 11:34:30 +01:00
1 parent 766a0808ab
commit 31c48296d9
19 files changed
+263 -40

No files matched your search

+46 -9
View File
@@ -5,8 +5,8 @@
// balances) — caching that would risk serving stale or, worse, another
// user's data. So this file does NOT try to make the booking flow work
// offline. What it does do:
// - Push notifications (`push`/`notificationclick` — unchanged from the
// original Phase 7 push-notifications work).
// - Push notifications (`push`/`notificationclick`), plus an app-icon
// badge number counting the ones still unread (see updateAppBadge).
// - Precache + serve a small static app shell (offline fallback page,
// manifest, icons) so a lost connection shows a real "you're offline"
// screen instead of the browser's own dinosaur/error page.
@@ -92,6 +92,34 @@ self.addEventListener("message", (event) => {
if (event.data?.type === "SKIP_WAITING") self.skipWaiting();
});
/**
* App-icon badge = how many of this app's push notifications are still
* sitting in the notification shelf. Counting what the OS is actually
* showing (instead of a stored counter) means nothing needs persisting —
* a service worker can be stopped at any moment, losing in-memory state.
* The page clears the badge and the shelf when the app is opened (see
* service-worker-manager.tsx).
*
* Needs the Badging API: installed PWAs on Android/desktop Chrome and
* Home Screen web apps on iOS 16.4+. Everywhere else this is a no-op.
*/
async function updateAppBadge() {
if (!("setAppBadge" in self.navigator)) return;
try {
const count = (await self.registration.getNotifications()).length;
if (count > 0) await self.navigator.setAppBadge(count);
else await self.navigator.clearAppBadge();
} catch {
// Badging is cosmetic — never let it break showing the notification.
}
}
/** True when the app is open and in front of the person — a badge on the icon is pointless (and stale the moment they leave) then. */
async function appIsInForeground() {
const windows = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
return windows.some((client) => client.visibilityState === "visible");
}
self.addEventListener("push", (event) => {
if (!event.data) return;
@@ -114,7 +142,13 @@ self.addEventListener("push", (event) => {
badge: "/icons/icon-192.png",
};
event.waitUntil(self.registration.showNotification(title, options));
event.waitUntil(
self.registration
.showNotification(title, options)
.then(async () => {
if (!(await appIsInForeground())) await updateAppBadge();
}),
);
});
self.addEventListener("notificationclick", (event) => {
@@ -122,11 +156,14 @@ self.addEventListener("notificationclick", (event) => {
const url = event.notification.data?.url || "/";
event.waitUntil(
self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
for (const client of clientList) {
if (client.url.includes(url) && "focus" in client) return client.focus();
}
if (self.clients.openWindow) return self.clients.openWindow(url);
}),
Promise.all([
updateAppBadge(),
self.clients.matchAll({ type: "window", includeUncontrolled: true }).then((clientList) => {
for (const client of clientList) {
if (client.url.includes(url) && "focus" in client) return client.focus();
}
if (self.clients.openWindow) return self.clients.openWindow(url);
}),
]),
);
});