feat: push upozornění na doručení objednávky Bolt Food
CI / Generate TypeScript types (push) Successful in 12s
CI / Server unit tests (push) Successful in 22s
CI / Build server (push) Successful in 28s
CI / Build client (push) Successful in 41s
CI / Playwright E2E tests (push) Successful in 1m48s
CI / Build and push Docker image (push) Successful in 53s
CI / Notify (push) Successful in 2s
CI / Generate TypeScript types (push) Successful in 12s
CI / Server unit tests (push) Successful in 22s
CI / Build server (push) Successful in 28s
CI / Build client (push) Successful in 41s
CI / Playwright E2E tests (push) Successful in 1m48s
CI / Build and push Docker image (push) Successful in 53s
CI / Notify (push) Successful in 2s
Nové zaškrtávátko v Nastavení → Notifikace pod výběrem času připomínky. Když skupinová objednávka sledovaná přes Bolt Food přejde do stavu delivered/finished, dostanou push členové skupiny, kteří si to zapnuli. Zakladatel skupiny notifikaci nedostává — ten je informován aplikací Bolt. Push subscription byla dosud svázaná s časem připomínky (subscribe endpoint bez reminderTime vracel 400), takže by nový přepínač samostatně nefungoval. Čas připomínky je nově v registru volitelný a scheduler připomínek záznamy bez času přeskakuje. Service worker nemá natvrdo tag a akci "Mám vlastní/neobědvám" — obojí se řídí payloadem, aby je doručovací notifikace nedědila.
This commit is contained in:
+10
-7
@@ -1,4 +1,4 @@
|
||||
// Service Worker pro Web Push notifikace (připomínka výběru oběda)
|
||||
// Service Worker pro Web Push notifikace (připomínka výběru oběda, doručení objednávky)
|
||||
|
||||
self.addEventListener('push', (event) => {
|
||||
const data = event.data?.json() ?? { title: 'Luncher', body: 'Ještě nemáte zvolený oběd!' };
|
||||
@@ -6,11 +6,12 @@ self.addEventListener('push', (event) => {
|
||||
self.registration.showNotification(data.title, {
|
||||
body: data.body,
|
||||
icon: '/favicon.ico',
|
||||
tag: 'lunch-reminder',
|
||||
data: { login: data.login, token: data.token },
|
||||
actions: [
|
||||
{ action: 'neobedvam', title: 'Mám vlastní/neobědvám' },
|
||||
],
|
||||
tag: data.tag ?? 'lunch-reminder',
|
||||
data: { login: data.login, token: data.token, url: data.url },
|
||||
// Token posílá jen připomínka oběda — ostatní notifikace tlačítko nemají.
|
||||
actions: data.token
|
||||
? [{ action: 'neobedvam', title: 'Mám vlastní/neobědvám' }]
|
||||
: [],
|
||||
})
|
||||
);
|
||||
});
|
||||
@@ -32,14 +33,16 @@ self.addEventListener('notificationclick', (event) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = event.notification.data?.url ?? '/';
|
||||
event.waitUntil(
|
||||
self.clients.matchAll({ type: 'window' }).then((clientList) => {
|
||||
for (const client of clientList) {
|
||||
if (client.url.includes(self.location.origin) && 'focus' in client) {
|
||||
if ('navigate' in client) client.navigate(url).catch(() => {});
|
||||
return client.focus();
|
||||
}
|
||||
}
|
||||
return self.clients.openWindow('/');
|
||||
return self.clients.openWindow(url);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
|
||||
const themeRef = useRef<HTMLSelectElement>(null);
|
||||
|
||||
const reminderTimeRef = useRef<HTMLInputElement>(null);
|
||||
const boltDeliveredRef = useRef<HTMLInputElement>(null);
|
||||
const ntfyTopicRef = useRef<HTMLInputElement>(null);
|
||||
const discordWebhookRef = useRef<HTMLInputElement>(null);
|
||||
const teamsWebhookRef = useRef<HTMLInputElement>(null);
|
||||
@@ -47,22 +48,29 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
|
||||
const handleSave = async () => {
|
||||
const newReminderTime = reminderTimeRef.current?.value || undefined;
|
||||
const oldReminderTime = notifSettings.reminderTime;
|
||||
const newBoltDelivered = boltDeliveredRef.current?.checked ?? false;
|
||||
const oldBoltDelivered = notifSettings.boltDeliveredPush ?? false;
|
||||
|
||||
// Uložení notifikačních nastavení na server
|
||||
await updateNotificationSettings({
|
||||
body: {
|
||||
ntfyTopic: ntfyTopicRef.current?.value || undefined,
|
||||
discordWebhookUrl: discordWebhookRef.current?.value || undefined,
|
||||
teamsWebhookUrl: teamsWebhookRef.current?.value || undefined,
|
||||
enabledEvents,
|
||||
reminderTime: newReminderTime,
|
||||
}
|
||||
}).catch(() => {});
|
||||
const newSettings: NotificationSettings = {
|
||||
ntfyTopic: ntfyTopicRef.current?.value || undefined,
|
||||
discordWebhookUrl: discordWebhookRef.current?.value || undefined,
|
||||
teamsWebhookUrl: teamsWebhookRef.current?.value || undefined,
|
||||
enabledEvents,
|
||||
reminderTime: newReminderTime,
|
||||
boltDeliveredPush: newBoltDelivered,
|
||||
};
|
||||
await updateNotificationSettings({ body: newSettings }).catch(() => {});
|
||||
setNotifSettings(newSettings);
|
||||
|
||||
// Správa push subscription pro připomínky
|
||||
if (newReminderTime && newReminderTime !== oldReminderTime) {
|
||||
// Správa push subscription — drží ji naživu kterákoli z push funkcí.
|
||||
// Záměrně bez await: subscribeToPush si vyžádá oprávnění prohlížeče a modal
|
||||
// by na tu dobu zůstal otevřený.
|
||||
const wantsPush = !!newReminderTime || newBoltDelivered;
|
||||
const hadPush = !!oldReminderTime || oldBoltDelivered;
|
||||
if (wantsPush && (newReminderTime !== oldReminderTime || newBoltDelivered !== oldBoltDelivered)) {
|
||||
subscribeToPush(newReminderTime);
|
||||
} else if (!newReminderTime && oldReminderTime) {
|
||||
} else if (!wantsPush && hadPush) {
|
||||
unsubscribeFromPush();
|
||||
}
|
||||
|
||||
@@ -128,6 +136,21 @@ export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Prop
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Check
|
||||
id="boltDeliveredCheckbox"
|
||||
ref={boltDeliveredRef}
|
||||
type="checkbox"
|
||||
label="Upozornění na doručení objednávky (Bolt Food)"
|
||||
defaultChecked={notifSettings.boltDeliveredPush ?? false}
|
||||
key={`bolt-delivered-${notifSettings.boltDeliveredPush ?? false}`}
|
||||
/>
|
||||
<Form.Text className="text-muted">
|
||||
Až bude skupinová objednávka sledovaná přes Bolt Food doručena, přijde vám push notifikace.
|
||||
Zakladateli skupiny se neposílá — ten dostane upozornění přímo z aplikace Bolt.
|
||||
</Form.Text>
|
||||
</Form.Group>
|
||||
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Label>ntfy téma (topic)</Form.Label>
|
||||
<Form.Control
|
||||
|
||||
@@ -27,9 +27,10 @@ async function pushApiFetch(path: string, options: RequestInit = {}): Promise<Re
|
||||
|
||||
/**
|
||||
* Zaregistruje service worker, přihlásí se k push notifikacím
|
||||
* a odešle subscription na server.
|
||||
* a odešle subscription na server. `reminderTime` je volitelný — bez něj
|
||||
* uživatel odebírá jen ostatní push notifikace (např. doručení objednávky).
|
||||
*/
|
||||
export async function subscribeToPush(reminderTime: string): Promise<boolean> {
|
||||
export async function subscribeToPush(reminderTime?: string): Promise<boolean> {
|
||||
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
|
||||
console.warn('Push notifikace nejsou v tomto prohlížeči podporovány');
|
||||
return false;
|
||||
@@ -75,7 +76,7 @@ export async function subscribeToPush(reminderTime: string): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('Push notifikace: úspěšně přihlášeno k připomínkám v', reminderTime);
|
||||
console.log('Push notifikace: úspěšně přihlášeno', reminderTime ? `(připomínka v ${reminderTime})` : '(bez připomínky oběda)');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Push notifikace: chyba při registraci', error);
|
||||
@@ -101,7 +102,7 @@ export async function unsubscribeFromPush(): Promise<void> {
|
||||
}
|
||||
|
||||
await pushApiFetch('/unsubscribe', { method: 'POST' });
|
||||
console.log('Push notifikace: úspěšně odhlášeno z připomínek');
|
||||
console.log('Push notifikace: úspěšně odhlášeno');
|
||||
} catch (error) {
|
||||
console.error('Push notifikace: chyba při odhlášení', error);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user