mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
feat(notifications): reminders for todos with upcoming due dates
Todos already support a due_date field but nothing notifies the user when a deadline is approaching — you'd only remember if you happened to look at the Lists tab. This wires a reminder into the existing notification pipeline so due-date todos behave like trip-start reminders. Details: - New `todo_due` event type alongside trip_reminder; all four channels (in-app, email, webhook, ntfy) supported and toggleable per user in Settings > Notifications. - New daily scheduler task (9 AM local TZ) queries unchecked todos whose due_date is within the next 3 days. Each todo gets at most one reminder per 24 hours, tracked via a new todo_items.reminded_at column (migration 116). - If the todo has an assigned user, only that user is reminded; if not, every member of the trip gets the notification. - Strings added in all 15 UI languages and for all notification carriers. - Gated by app_settings.notify_todo_due (default on) so admins can disable it globally.
This commit is contained in:
@@ -25,6 +25,7 @@ const EVENT_LABEL_KEYS: Record<string, string> = {
|
|||||||
trip_invite: 'settings.notifyTripInvite',
|
trip_invite: 'settings.notifyTripInvite',
|
||||||
booking_change: 'settings.notifyBookingChange',
|
booking_change: 'settings.notifyBookingChange',
|
||||||
trip_reminder: 'settings.notifyTripReminder',
|
trip_reminder: 'settings.notifyTripReminder',
|
||||||
|
todo_due: 'settings.notifyTodoDue',
|
||||||
vacay_invite: 'settings.notifyVacayInvite',
|
vacay_invite: 'settings.notifyVacayInvite',
|
||||||
photos_shared: 'settings.notifyPhotosShared',
|
photos_shared: 'settings.notifyPhotosShared',
|
||||||
collab_message: 'settings.notifyCollabMessage',
|
collab_message: 'settings.notifyCollabMessage',
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'دعوات الرحلات',
|
'settings.notifyTripInvite': 'دعوات الرحلات',
|
||||||
'settings.notifyBookingChange': 'تغييرات الحجز',
|
'settings.notifyBookingChange': 'تغييرات الحجز',
|
||||||
'settings.notifyTripReminder': 'تذكيرات الرحلات',
|
'settings.notifyTripReminder': 'تذكيرات الرحلات',
|
||||||
|
'settings.notifyTodoDue': 'مهمة مستحقة',
|
||||||
'settings.notifyVacayInvite': 'دعوات دمج الإجازات',
|
'settings.notifyVacayInvite': 'دعوات دمج الإجازات',
|
||||||
'settings.notifyPhotosShared': 'صور مشتركة (Immich)',
|
'settings.notifyPhotosShared': 'صور مشتركة (Immich)',
|
||||||
'settings.notifyCollabMessage': 'رسائل الدردشة (Collab)',
|
'settings.notifyCollabMessage': 'رسائل الدردشة (Collab)',
|
||||||
@@ -1995,6 +1996,8 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} حدّث حجزاً في {trip}',
|
'notif.booking_change.text': '{actor} حدّث حجزاً في {trip}',
|
||||||
'notif.trip_reminder.title': 'تذكير بالرحلة',
|
'notif.trip_reminder.title': 'تذكير بالرحلة',
|
||||||
'notif.trip_reminder.text': 'رحلتك {trip} تقترب!',
|
'notif.trip_reminder.text': 'رحلتك {trip} تقترب!',
|
||||||
|
'notif.todo_due.title': 'مهمة مستحقة',
|
||||||
|
'notif.todo_due.text': '{todo} في {trip} مستحقة في {due}',
|
||||||
'notif.vacay_invite.title': 'دعوة دمج الإجازة',
|
'notif.vacay_invite.title': 'دعوة دمج الإجازة',
|
||||||
'notif.vacay_invite.text': '{actor} يدعوك لدمج خطط الإجازة',
|
'notif.vacay_invite.text': '{actor} يدعوك لدمج خطط الإجازة',
|
||||||
'notif.photos_shared.title': 'تمت مشاركة الصور',
|
'notif.photos_shared.title': 'تمت مشاركة الصور',
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const br: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'Convites de viagem',
|
'settings.notifyTripInvite': 'Convites de viagem',
|
||||||
'settings.notifyBookingChange': 'Alterações de reserva',
|
'settings.notifyBookingChange': 'Alterações de reserva',
|
||||||
'settings.notifyTripReminder': 'Lembretes de viagem',
|
'settings.notifyTripReminder': 'Lembretes de viagem',
|
||||||
|
'settings.notifyTodoDue': 'Tarefa com vencimento',
|
||||||
'settings.notifyVacayInvite': 'Convites de fusão Vacay',
|
'settings.notifyVacayInvite': 'Convites de fusão Vacay',
|
||||||
'settings.notifyPhotosShared': 'Fotos compartilhadas (Immich)',
|
'settings.notifyPhotosShared': 'Fotos compartilhadas (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Mensagens de chat (Colab)',
|
'settings.notifyCollabMessage': 'Mensagens de chat (Colab)',
|
||||||
@@ -1935,6 +1936,8 @@ const br: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} atualizou uma reserva em {trip}',
|
'notif.booking_change.text': '{actor} atualizou uma reserva em {trip}',
|
||||||
'notif.trip_reminder.title': 'Lembrete de viagem',
|
'notif.trip_reminder.title': 'Lembrete de viagem',
|
||||||
'notif.trip_reminder.text': 'Sua viagem {trip} está chegando!',
|
'notif.trip_reminder.text': 'Sua viagem {trip} está chegando!',
|
||||||
|
'notif.todo_due.title': 'Tarefa com vencimento',
|
||||||
|
'notif.todo_due.text': '{todo} em {trip} vence em {due}',
|
||||||
'notif.vacay_invite.title': 'Convite Vacay Fusion',
|
'notif.vacay_invite.title': 'Convite Vacay Fusion',
|
||||||
'notif.vacay_invite.text': '{actor} convidou você para fundir planos de férias',
|
'notif.vacay_invite.text': '{actor} convidou você para fundir planos de férias',
|
||||||
'notif.photos_shared.title': 'Fotos compartilhadas',
|
'notif.photos_shared.title': 'Fotos compartilhadas',
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'Pozvánky na cesty',
|
'settings.notifyTripInvite': 'Pozvánky na cesty',
|
||||||
'settings.notifyBookingChange': 'Změny rezervací',
|
'settings.notifyBookingChange': 'Změny rezervací',
|
||||||
'settings.notifyTripReminder': 'Připomínky cest',
|
'settings.notifyTripReminder': 'Připomínky cest',
|
||||||
|
'settings.notifyTodoDue': 'Úkol se blíží',
|
||||||
'settings.notifyVacayInvite': 'Pozvánky k propojení Vacay',
|
'settings.notifyVacayInvite': 'Pozvánky k propojení Vacay',
|
||||||
'settings.notifyPhotosShared': 'Sdílené fotky (Immich)',
|
'settings.notifyPhotosShared': 'Sdílené fotky (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Zprávy v chatu (Collab)',
|
'settings.notifyCollabMessage': 'Zprávy v chatu (Collab)',
|
||||||
@@ -1940,6 +1941,8 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} aktualizoval rezervaci v {trip}',
|
'notif.booking_change.text': '{actor} aktualizoval rezervaci v {trip}',
|
||||||
'notif.trip_reminder.title': 'Připomínka výletu',
|
'notif.trip_reminder.title': 'Připomínka výletu',
|
||||||
'notif.trip_reminder.text': 'Váš výlet {trip} se blíží!',
|
'notif.trip_reminder.text': 'Váš výlet {trip} se blíží!',
|
||||||
|
'notif.todo_due.title': 'Úkol se blíží',
|
||||||
|
'notif.todo_due.text': '{todo} ve výletě {trip} má termín {due}',
|
||||||
'notif.vacay_invite.title': 'Pozvánka Vacay Fusion',
|
'notif.vacay_invite.title': 'Pozvánka Vacay Fusion',
|
||||||
'notif.vacay_invite.text': '{actor} vás pozval ke spojení dovolenkových plánů',
|
'notif.vacay_invite.text': '{actor} vás pozval ke spojení dovolenkových plánů',
|
||||||
'notif.photos_shared.title': 'Fotky sdíleny',
|
'notif.photos_shared.title': 'Fotky sdíleny',
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ const de: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'Trip-Einladungen',
|
'settings.notifyTripInvite': 'Trip-Einladungen',
|
||||||
'settings.notifyBookingChange': 'Buchungsänderungen',
|
'settings.notifyBookingChange': 'Buchungsänderungen',
|
||||||
'settings.notifyTripReminder': 'Trip-Erinnerungen',
|
'settings.notifyTripReminder': 'Trip-Erinnerungen',
|
||||||
|
'settings.notifyTodoDue': 'Aufgabe bald fällig',
|
||||||
'settings.notifyVacayInvite': 'Vacay Fusion-Einladungen',
|
'settings.notifyVacayInvite': 'Vacay Fusion-Einladungen',
|
||||||
'settings.notifyPhotosShared': 'Geteilte Fotos (Immich)',
|
'settings.notifyPhotosShared': 'Geteilte Fotos (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Chat-Nachrichten (Collab)',
|
'settings.notifyCollabMessage': 'Chat-Nachrichten (Collab)',
|
||||||
@@ -1945,6 +1946,8 @@ const de: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} hat eine Buchung in {trip} aktualisiert',
|
'notif.booking_change.text': '{actor} hat eine Buchung in {trip} aktualisiert',
|
||||||
'notif.trip_reminder.title': 'Reiseerinnerung',
|
'notif.trip_reminder.title': 'Reiseerinnerung',
|
||||||
'notif.trip_reminder.text': 'Deine Reise {trip} steht bald an!',
|
'notif.trip_reminder.text': 'Deine Reise {trip} steht bald an!',
|
||||||
|
'notif.todo_due.title': 'Aufgabe fällig',
|
||||||
|
'notif.todo_due.text': '{todo} in {trip} ist am {due} fällig',
|
||||||
'notif.vacay_invite.title': 'Vacay Fusion-Einladung',
|
'notif.vacay_invite.title': 'Vacay Fusion-Einladung',
|
||||||
'notif.vacay_invite.text': '{actor} hat dich zum Fusionieren von Urlaubsplänen eingeladen',
|
'notif.vacay_invite.text': '{actor} hat dich zum Fusionieren von Urlaubsplänen eingeladen',
|
||||||
'notif.photos_shared.title': 'Fotos geteilt',
|
'notif.photos_shared.title': 'Fotos geteilt',
|
||||||
|
|||||||
@@ -204,6 +204,7 @@ const en: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'Trip invitations',
|
'settings.notifyTripInvite': 'Trip invitations',
|
||||||
'settings.notifyBookingChange': 'Booking changes',
|
'settings.notifyBookingChange': 'Booking changes',
|
||||||
'settings.notifyTripReminder': 'Trip reminders',
|
'settings.notifyTripReminder': 'Trip reminders',
|
||||||
|
'settings.notifyTodoDue': 'Todo due soon',
|
||||||
'settings.notifyVacayInvite': 'Vacay fusion invitations',
|
'settings.notifyVacayInvite': 'Vacay fusion invitations',
|
||||||
'settings.notifyPhotosShared': 'Shared photos (Immich)',
|
'settings.notifyPhotosShared': 'Shared photos (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Chat messages (Collab)',
|
'settings.notifyCollabMessage': 'Chat messages (Collab)',
|
||||||
@@ -1948,6 +1949,8 @@ const en: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} updated a booking in {trip}',
|
'notif.booking_change.text': '{actor} updated a booking in {trip}',
|
||||||
'notif.trip_reminder.title': 'Trip Reminder',
|
'notif.trip_reminder.title': 'Trip Reminder',
|
||||||
'notif.trip_reminder.text': 'Your trip {trip} is coming up soon!',
|
'notif.trip_reminder.text': 'Your trip {trip} is coming up soon!',
|
||||||
|
'notif.todo_due.title': 'To-do due',
|
||||||
|
'notif.todo_due.text': '{todo} in {trip} is due on {due}',
|
||||||
'notif.vacay_invite.title': 'Vacay Fusion Invite',
|
'notif.vacay_invite.title': 'Vacay Fusion Invite',
|
||||||
'notif.vacay_invite.text': '{actor} invited you to fuse vacation plans',
|
'notif.vacay_invite.text': '{actor} invited you to fuse vacation plans',
|
||||||
'notif.photos_shared.title': 'Photos Shared',
|
'notif.photos_shared.title': 'Photos Shared',
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ const es: Record<string, string> = {
|
|||||||
'settings.notifyTripInvite': 'Invitaciones de viaje',
|
'settings.notifyTripInvite': 'Invitaciones de viaje',
|
||||||
'settings.notifyBookingChange': 'Cambios en reservas',
|
'settings.notifyBookingChange': 'Cambios en reservas',
|
||||||
'settings.notifyTripReminder': 'Recordatorios de viaje',
|
'settings.notifyTripReminder': 'Recordatorios de viaje',
|
||||||
|
'settings.notifyTodoDue': 'Tarea próxima',
|
||||||
'settings.notifyVacayInvite': 'Invitaciones de fusión Vacay',
|
'settings.notifyVacayInvite': 'Invitaciones de fusión Vacay',
|
||||||
'settings.notifyPhotosShared': 'Fotos compartidas (Immich)',
|
'settings.notifyPhotosShared': 'Fotos compartidas (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Mensajes de chat (Collab)',
|
'settings.notifyCollabMessage': 'Mensajes de chat (Collab)',
|
||||||
@@ -1945,6 +1946,8 @@ const es: Record<string, string> = {
|
|||||||
'notif.booking_change.text': '{actor} actualizó una reserva en {trip}',
|
'notif.booking_change.text': '{actor} actualizó una reserva en {trip}',
|
||||||
'notif.trip_reminder.title': 'Recordatorio de viaje',
|
'notif.trip_reminder.title': 'Recordatorio de viaje',
|
||||||
'notif.trip_reminder.text': '¡Tu viaje {trip} se acerca!',
|
'notif.trip_reminder.text': '¡Tu viaje {trip} se acerca!',
|
||||||
|
'notif.todo_due.title': 'Tarea pendiente',
|
||||||
|
'notif.todo_due.text': '{todo} en {trip} vence el {due}',
|
||||||
'notif.vacay_invite.title': 'Invitación Vacay Fusion',
|
'notif.vacay_invite.title': 'Invitación Vacay Fusion',
|
||||||
'notif.vacay_invite.text': '{actor} te invitó a fusionar planes de vacaciones',
|
'notif.vacay_invite.text': '{actor} te invitó a fusionar planes de vacaciones',
|
||||||
'notif.photos_shared.title': 'Fotos compartidas',
|
'notif.photos_shared.title': 'Fotos compartidas',
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const fr: Record<string, string> = {
|
|||||||
'settings.notifyTripInvite': 'Invitations de voyage',
|
'settings.notifyTripInvite': 'Invitations de voyage',
|
||||||
'settings.notifyBookingChange': 'Modifications de réservation',
|
'settings.notifyBookingChange': 'Modifications de réservation',
|
||||||
'settings.notifyTripReminder': 'Rappels de voyage',
|
'settings.notifyTripReminder': 'Rappels de voyage',
|
||||||
|
'settings.notifyTodoDue': 'Tâche à échéance',
|
||||||
'settings.notifyVacayInvite': 'Invitations de fusion Vacay',
|
'settings.notifyVacayInvite': 'Invitations de fusion Vacay',
|
||||||
'settings.notifyPhotosShared': 'Photos partagées (Immich)',
|
'settings.notifyPhotosShared': 'Photos partagées (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Messages de chat (Collab)',
|
'settings.notifyCollabMessage': 'Messages de chat (Collab)',
|
||||||
@@ -1939,6 +1940,8 @@ const fr: Record<string, string> = {
|
|||||||
'notif.booking_change.text': '{actor} a mis à jour une réservation dans {trip}',
|
'notif.booking_change.text': '{actor} a mis à jour une réservation dans {trip}',
|
||||||
'notif.trip_reminder.title': 'Rappel de voyage',
|
'notif.trip_reminder.title': 'Rappel de voyage',
|
||||||
'notif.trip_reminder.text': 'Votre voyage {trip} approche !',
|
'notif.trip_reminder.text': 'Votre voyage {trip} approche !',
|
||||||
|
'notif.todo_due.title': 'Tâche à échéance',
|
||||||
|
'notif.todo_due.text': '{todo} dans {trip} est due le {due}',
|
||||||
'notif.vacay_invite.title': 'Invitation Vacay Fusion',
|
'notif.vacay_invite.title': 'Invitation Vacay Fusion',
|
||||||
'notif.vacay_invite.text': '{actor} vous invite à fusionner les plans de vacances',
|
'notif.vacay_invite.text': '{actor} vous invite à fusionner les plans de vacances',
|
||||||
'notif.photos_shared.title': 'Photos partagées',
|
'notif.photos_shared.title': 'Photos partagées',
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'Utazási meghívók',
|
'settings.notifyTripInvite': 'Utazási meghívók',
|
||||||
'settings.notifyBookingChange': 'Foglalási változások',
|
'settings.notifyBookingChange': 'Foglalási változások',
|
||||||
'settings.notifyTripReminder': 'Utazási emlékeztetők',
|
'settings.notifyTripReminder': 'Utazási emlékeztetők',
|
||||||
|
'settings.notifyTodoDue': 'Teendő esedékes',
|
||||||
'settings.notifyVacayInvite': 'Vacay összevonási meghívók',
|
'settings.notifyVacayInvite': 'Vacay összevonási meghívók',
|
||||||
'settings.notifyPhotosShared': 'Megosztott fotók (Immich)',
|
'settings.notifyPhotosShared': 'Megosztott fotók (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Csevegés üzenetek (Collab)',
|
'settings.notifyCollabMessage': 'Csevegés üzenetek (Collab)',
|
||||||
@@ -1937,6 +1938,8 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} frissített egy foglalást a(z) {trip} utazásban',
|
'notif.booking_change.text': '{actor} frissített egy foglalást a(z) {trip} utazásban',
|
||||||
'notif.trip_reminder.title': 'Utazás emlékeztető',
|
'notif.trip_reminder.title': 'Utazás emlékeztető',
|
||||||
'notif.trip_reminder.text': 'A(z) {trip} utazás hamarosan kezdődik!',
|
'notif.trip_reminder.text': 'A(z) {trip} utazás hamarosan kezdődik!',
|
||||||
|
'notif.todo_due.title': 'Teendő esedékes',
|
||||||
|
'notif.todo_due.text': '{todo} ({trip}) határideje: {due}',
|
||||||
'notif.vacay_invite.title': 'Vacay Fusion meghívó',
|
'notif.vacay_invite.title': 'Vacay Fusion meghívó',
|
||||||
'notif.vacay_invite.text': '{actor} meghívott a nyaralási tervek összevonásához',
|
'notif.vacay_invite.text': '{actor} meghívott a nyaralási tervek összevonásához',
|
||||||
'notif.photos_shared.title': 'Fotók megosztva',
|
'notif.photos_shared.title': 'Fotók megosztva',
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ const id: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'Undangan perjalanan',
|
'settings.notifyTripInvite': 'Undangan perjalanan',
|
||||||
'settings.notifyBookingChange': 'Perubahan pemesanan',
|
'settings.notifyBookingChange': 'Perubahan pemesanan',
|
||||||
'settings.notifyTripReminder': 'Pengingat perjalanan',
|
'settings.notifyTripReminder': 'Pengingat perjalanan',
|
||||||
|
'settings.notifyTodoDue': 'Tugas jatuh tempo',
|
||||||
'settings.notifyVacayInvite': 'Undangan Vacay fusion',
|
'settings.notifyVacayInvite': 'Undangan Vacay fusion',
|
||||||
'settings.notifyPhotosShared': 'Foto dibagikan (Immich)',
|
'settings.notifyPhotosShared': 'Foto dibagikan (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Pesan chat (Collab)',
|
'settings.notifyCollabMessage': 'Pesan chat (Collab)',
|
||||||
@@ -1946,6 +1947,8 @@ const id: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} memperbarui pemesanan di {trip}',
|
'notif.booking_change.text': '{actor} memperbarui pemesanan di {trip}',
|
||||||
'notif.trip_reminder.title': 'Pengingat Perjalanan',
|
'notif.trip_reminder.title': 'Pengingat Perjalanan',
|
||||||
'notif.trip_reminder.text': 'Perjalananmu {trip} akan segera dimulai!',
|
'notif.trip_reminder.text': 'Perjalananmu {trip} akan segera dimulai!',
|
||||||
|
'notif.todo_due.title': 'Tugas jatuh tempo',
|
||||||
|
'notif.todo_due.text': '{todo} di {trip} jatuh tempo pada {due}',
|
||||||
'notif.vacay_invite.title': 'Undangan Vacay Fusion',
|
'notif.vacay_invite.title': 'Undangan Vacay Fusion',
|
||||||
'notif.vacay_invite.text': '{actor} mengundangmu untuk menggabungkan rencana liburan',
|
'notif.vacay_invite.text': '{actor} mengundangmu untuk menggabungkan rencana liburan',
|
||||||
'notif.photos_shared.title': 'Foto Dibagikan',
|
'notif.photos_shared.title': 'Foto Dibagikan',
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const it: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'Inviti di viaggio',
|
'settings.notifyTripInvite': 'Inviti di viaggio',
|
||||||
'settings.notifyBookingChange': 'Modifiche alle prenotazioni',
|
'settings.notifyBookingChange': 'Modifiche alle prenotazioni',
|
||||||
'settings.notifyTripReminder': 'Promemoria di viaggio',
|
'settings.notifyTripReminder': 'Promemoria di viaggio',
|
||||||
|
'settings.notifyTodoDue': 'Attività in scadenza',
|
||||||
'settings.notifyVacayInvite': 'Inviti fusione Vacay',
|
'settings.notifyVacayInvite': 'Inviti fusione Vacay',
|
||||||
'settings.notifyPhotosShared': 'Foto condivise (Immich)',
|
'settings.notifyPhotosShared': 'Foto condivise (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Messaggi chat (Collab)',
|
'settings.notifyCollabMessage': 'Messaggi chat (Collab)',
|
||||||
@@ -1940,6 +1941,8 @@ const it: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} ha aggiornato una prenotazione in {trip}',
|
'notif.booking_change.text': '{actor} ha aggiornato una prenotazione in {trip}',
|
||||||
'notif.trip_reminder.title': 'Promemoria viaggio',
|
'notif.trip_reminder.title': 'Promemoria viaggio',
|
||||||
'notif.trip_reminder.text': 'Il tuo viaggio {trip} si avvicina!',
|
'notif.trip_reminder.text': 'Il tuo viaggio {trip} si avvicina!',
|
||||||
|
'notif.todo_due.title': 'Attività in scadenza',
|
||||||
|
'notif.todo_due.text': '{todo} in {trip} scade il {due}',
|
||||||
'notif.vacay_invite.title': 'Invito Vacay Fusion',
|
'notif.vacay_invite.title': 'Invito Vacay Fusion',
|
||||||
'notif.vacay_invite.text': '{actor} ti ha invitato a fondere i piani vacanza',
|
'notif.vacay_invite.text': '{actor} ti ha invitato a fondere i piani vacanza',
|
||||||
'notif.photos_shared.title': 'Foto condivise',
|
'notif.photos_shared.title': 'Foto condivise',
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const nl: Record<string, string> = {
|
|||||||
'settings.notifyTripInvite': 'Reisuitnodigingen',
|
'settings.notifyTripInvite': 'Reisuitnodigingen',
|
||||||
'settings.notifyBookingChange': 'Boekingswijzigingen',
|
'settings.notifyBookingChange': 'Boekingswijzigingen',
|
||||||
'settings.notifyTripReminder': 'Reisherinneringen',
|
'settings.notifyTripReminder': 'Reisherinneringen',
|
||||||
|
'settings.notifyTodoDue': 'Taak verloopt',
|
||||||
'settings.notifyVacayInvite': 'Vacay-fusieuitnodigingen',
|
'settings.notifyVacayInvite': 'Vacay-fusieuitnodigingen',
|
||||||
'settings.notifyPhotosShared': 'Gedeelde foto\'s (Immich)',
|
'settings.notifyPhotosShared': 'Gedeelde foto\'s (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Chatberichten (Collab)',
|
'settings.notifyCollabMessage': 'Chatberichten (Collab)',
|
||||||
@@ -1939,6 +1940,8 @@ const nl: Record<string, string> = {
|
|||||||
'notif.booking_change.text': '{actor} heeft een boeking bijgewerkt in {trip}',
|
'notif.booking_change.text': '{actor} heeft een boeking bijgewerkt in {trip}',
|
||||||
'notif.trip_reminder.title': 'Reisherinnering',
|
'notif.trip_reminder.title': 'Reisherinnering',
|
||||||
'notif.trip_reminder.text': 'Je reis {trip} komt eraan!',
|
'notif.trip_reminder.text': 'Je reis {trip} komt eraan!',
|
||||||
|
'notif.todo_due.title': 'Taak verloopt',
|
||||||
|
'notif.todo_due.text': '{todo} in {trip} verloopt op {due}',
|
||||||
'notif.vacay_invite.title': 'Vacay Fusion-uitnodiging',
|
'notif.vacay_invite.title': 'Vacay Fusion-uitnodiging',
|
||||||
'notif.vacay_invite.text': '{actor} nodigt je uit om vakantieplannen te fuseren',
|
'notif.vacay_invite.text': '{actor} nodigt je uit om vakantieplannen te fuseren',
|
||||||
'notif.photos_shared.title': 'Foto\'s gedeeld',
|
'notif.photos_shared.title': 'Foto\'s gedeeld',
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ const pl: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'settings.notifyTripInvite': 'Zaproszenia do podróży',
|
'settings.notifyTripInvite': 'Zaproszenia do podróży',
|
||||||
'settings.notifyBookingChange': 'Zmiany w rezerwacjach',
|
'settings.notifyBookingChange': 'Zmiany w rezerwacjach',
|
||||||
'settings.notifyTripReminder': 'Przypomnienia o podróżach',
|
'settings.notifyTripReminder': 'Przypomnienia o podróżach',
|
||||||
|
'settings.notifyTodoDue': 'Zadanie z terminem',
|
||||||
'settings.notifyVacayInvite': 'Zaproszenia do połączenia kalendarzy',
|
'settings.notifyVacayInvite': 'Zaproszenia do połączenia kalendarzy',
|
||||||
'settings.notifyPhotosShared': 'Udostępnione zdjęcia (Immich)',
|
'settings.notifyPhotosShared': 'Udostępnione zdjęcia (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Wiadomości czatu (Collab)',
|
'settings.notifyCollabMessage': 'Wiadomości czatu (Collab)',
|
||||||
@@ -1929,6 +1930,8 @@ const pl: Record<string, string | { name: string; category: string }[]> = {
|
|||||||
'notif.booking_change.text': '{actor} zaktualizował rezerwację w {trip}',
|
'notif.booking_change.text': '{actor} zaktualizował rezerwację w {trip}',
|
||||||
'notif.trip_reminder.title': 'Przypomnienie o podróży',
|
'notif.trip_reminder.title': 'Przypomnienie o podróży',
|
||||||
'notif.trip_reminder.text': 'Twoja podróż {trip} zbliża się!',
|
'notif.trip_reminder.text': 'Twoja podróż {trip} zbliża się!',
|
||||||
|
'notif.todo_due.title': 'Zadanie z terminem',
|
||||||
|
'notif.todo_due.text': '{todo} w {trip} — termin {due}',
|
||||||
'notif.vacay_invite.title': 'Zaproszenie Vacay Fusion',
|
'notif.vacay_invite.title': 'Zaproszenie Vacay Fusion',
|
||||||
'notif.vacay_invite.text': '{actor} zaprosił Cię do połączenia planów urlopowych',
|
'notif.vacay_invite.text': '{actor} zaprosił Cię do połączenia planów urlopowych',
|
||||||
'notif.photos_shared.title': 'Zdjęcia udostępnione',
|
'notif.photos_shared.title': 'Zdjęcia udostępnione',
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const ru: Record<string, string> = {
|
|||||||
'settings.notifyTripInvite': 'Приглашения в поездку',
|
'settings.notifyTripInvite': 'Приглашения в поездку',
|
||||||
'settings.notifyBookingChange': 'Изменения бронирований',
|
'settings.notifyBookingChange': 'Изменения бронирований',
|
||||||
'settings.notifyTripReminder': 'Напоминания о поездке',
|
'settings.notifyTripReminder': 'Напоминания о поездке',
|
||||||
|
'settings.notifyTodoDue': 'Задача к сроку',
|
||||||
'settings.notifyVacayInvite': 'Приглашения слияния Vacay',
|
'settings.notifyVacayInvite': 'Приглашения слияния Vacay',
|
||||||
'settings.notifyPhotosShared': 'Общие фото (Immich)',
|
'settings.notifyPhotosShared': 'Общие фото (Immich)',
|
||||||
'settings.notifyCollabMessage': 'Сообщения чата (Collab)',
|
'settings.notifyCollabMessage': 'Сообщения чата (Collab)',
|
||||||
@@ -1936,6 +1937,8 @@ const ru: Record<string, string> = {
|
|||||||
'notif.booking_change.text': '{actor} обновил бронирование в {trip}',
|
'notif.booking_change.text': '{actor} обновил бронирование в {trip}',
|
||||||
'notif.trip_reminder.title': 'Напоминание о поездке',
|
'notif.trip_reminder.title': 'Напоминание о поездке',
|
||||||
'notif.trip_reminder.text': 'Ваша поездка {trip} скоро начнётся!',
|
'notif.trip_reminder.text': 'Ваша поездка {trip} скоро начнётся!',
|
||||||
|
'notif.todo_due.title': 'Задача к сроку',
|
||||||
|
'notif.todo_due.text': '{todo} в {trip} — срок {due}',
|
||||||
'notif.vacay_invite.title': 'Приглашение Vacay Fusion',
|
'notif.vacay_invite.title': 'Приглашение Vacay Fusion',
|
||||||
'notif.vacay_invite.text': '{actor} приглашает вас объединить планы отпуска',
|
'notif.vacay_invite.text': '{actor} приглашает вас объединить планы отпуска',
|
||||||
'notif.photos_shared.title': 'Фото опубликованы',
|
'notif.photos_shared.title': 'Фото опубликованы',
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const zh: Record<string, string> = {
|
|||||||
'settings.notifyTripInvite': '旅行邀请',
|
'settings.notifyTripInvite': '旅行邀请',
|
||||||
'settings.notifyBookingChange': '预订变更',
|
'settings.notifyBookingChange': '预订变更',
|
||||||
'settings.notifyTripReminder': '旅行提醒',
|
'settings.notifyTripReminder': '旅行提醒',
|
||||||
|
'settings.notifyTodoDue': '待办事项即将到期',
|
||||||
'settings.notifyVacayInvite': 'Vacay 融合邀请',
|
'settings.notifyVacayInvite': 'Vacay 融合邀请',
|
||||||
'settings.notifyPhotosShared': '共享照片 (Immich)',
|
'settings.notifyPhotosShared': '共享照片 (Immich)',
|
||||||
'settings.notifyCollabMessage': '聊天消息 (Collab)',
|
'settings.notifyCollabMessage': '聊天消息 (Collab)',
|
||||||
@@ -1936,6 +1937,8 @@ const zh: Record<string, string> = {
|
|||||||
'notif.booking_change.text': '{actor} 更新了 {trip} 中的预订',
|
'notif.booking_change.text': '{actor} 更新了 {trip} 中的预订',
|
||||||
'notif.trip_reminder.title': '旅行提醒',
|
'notif.trip_reminder.title': '旅行提醒',
|
||||||
'notif.trip_reminder.text': '您的旅行 {trip} 即将开始!',
|
'notif.trip_reminder.text': '您的旅行 {trip} 即将开始!',
|
||||||
|
'notif.todo_due.title': '待办事项即将到期',
|
||||||
|
'notif.todo_due.text': '{trip} 中的 {todo} 将于 {due} 到期',
|
||||||
'notif.vacay_invite.title': 'Vacay 融合邀请',
|
'notif.vacay_invite.title': 'Vacay 融合邀请',
|
||||||
'notif.vacay_invite.text': '{actor} 邀请您合并假期计划',
|
'notif.vacay_invite.text': '{actor} 邀请您合并假期计划',
|
||||||
'notif.photos_shared.title': '照片已分享',
|
'notif.photos_shared.title': '照片已分享',
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ const zhTw: Record<string, string> = {
|
|||||||
'settings.notifyTripInvite': '旅行邀請',
|
'settings.notifyTripInvite': '旅行邀請',
|
||||||
'settings.notifyBookingChange': '預訂變更',
|
'settings.notifyBookingChange': '預訂變更',
|
||||||
'settings.notifyTripReminder': '旅行提醒',
|
'settings.notifyTripReminder': '旅行提醒',
|
||||||
|
'settings.notifyTodoDue': '待辦事項即將到期',
|
||||||
'settings.notifyVacayInvite': 'Vacay 融合邀請',
|
'settings.notifyVacayInvite': 'Vacay 融合邀請',
|
||||||
'settings.notifyPhotosShared': '共享照片 (Immich)',
|
'settings.notifyPhotosShared': '共享照片 (Immich)',
|
||||||
'settings.notifyCollabMessage': '聊天訊息 (Collab)',
|
'settings.notifyCollabMessage': '聊天訊息 (Collab)',
|
||||||
@@ -2195,6 +2196,8 @@ const zhTw: Record<string, string> = {
|
|||||||
'notif.booking_change.text': '{actor} 更新了 {trip} 中的預訂',
|
'notif.booking_change.text': '{actor} 更新了 {trip} 中的預訂',
|
||||||
'notif.trip_reminder.title': '旅行提醒',
|
'notif.trip_reminder.title': '旅行提醒',
|
||||||
'notif.trip_reminder.text': '你的旅行 {trip} 即將開始!',
|
'notif.trip_reminder.text': '你的旅行 {trip} 即將開始!',
|
||||||
|
'notif.todo_due.title': '待辦事項即將到期',
|
||||||
|
'notif.todo_due.text': '{trip} 中的 {todo} 將於 {due} 到期',
|
||||||
'notif.vacay_invite.title': 'Vacay Fusion 邀請',
|
'notif.vacay_invite.title': 'Vacay Fusion 邀請',
|
||||||
'notif.vacay_invite.text': '{actor} 邀請你合併假期計畫',
|
'notif.vacay_invite.text': '{actor} 邀請你合併假期計畫',
|
||||||
'notif.photos_shared.title': '照片已分享',
|
'notif.photos_shared.title': '照片已分享',
|
||||||
|
|||||||
@@ -1792,6 +1792,13 @@ function runMigrations(db: Database.Database): void {
|
|||||||
CREATE INDEX IF NOT EXISTS idx_prt_hash ON password_reset_tokens(token_hash);
|
CREATE INDEX IF NOT EXISTS idx_prt_hash ON password_reset_tokens(token_hash);
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
|
// Migration: todo due-date reminders — track when we last sent a
|
||||||
|
// reminder for each todo so we don't spam the same notification
|
||||||
|
// every day the scheduler runs.
|
||||||
|
() => {
|
||||||
|
try { db.exec('ALTER TABLE todo_items ADD COLUMN reminded_at DATETIME'); }
|
||||||
|
catch (err: any) { if (!err.message?.includes('duplicate column name')) throw err; }
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
if (currentVersion < migrations.length) {
|
if (currentVersion < migrations.length) {
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ const server = app.listen(PORT, () => {
|
|||||||
}
|
}
|
||||||
scheduler.start();
|
scheduler.start();
|
||||||
scheduler.startTripReminders();
|
scheduler.startTripReminders();
|
||||||
|
scheduler.startTodoReminders();
|
||||||
scheduler.startVersionCheck();
|
scheduler.startVersionCheck();
|
||||||
scheduler.startDemoReset();
|
scheduler.startDemoReset();
|
||||||
scheduler.startIdempotencyCleanup();
|
scheduler.startIdempotencyCleanup();
|
||||||
|
|||||||
+76
-1
@@ -207,6 +207,81 @@ function startTripReminders(): void {
|
|||||||
}, { timezone: tz });
|
}, { timezone: tz });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Todo due-date reminders: daily check at 9 AM for unchecked todos
|
||||||
|
// whose due_date falls within the next TODO_REMINDER_LEAD_DAYS days.
|
||||||
|
// Each todo gets reminded at most once per 24 h (tracked via
|
||||||
|
// todo_items.reminded_at) so the scheduler doesn't spam the user every
|
||||||
|
// morning leading up to the deadline.
|
||||||
|
const TODO_REMINDER_LEAD_DAYS = 3;
|
||||||
|
let todoReminderTask: ScheduledTask | null = null;
|
||||||
|
|
||||||
|
function startTodoReminders(): void {
|
||||||
|
if (todoReminderTask) { todoReminderTask.stop(); todoReminderTask = null; }
|
||||||
|
|
||||||
|
const { db } = require('./db/database');
|
||||||
|
const getSetting = (key: string) => (db.prepare('SELECT value FROM app_settings WHERE key = ?').get(key) as { value: string } | undefined)?.value;
|
||||||
|
const enabled = getSetting('notify_todo_due') !== 'false';
|
||||||
|
if (!enabled) {
|
||||||
|
const { logInfo: li } = require('./services/auditLog');
|
||||||
|
li('Todo due reminders: disabled in settings');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { logInfo: liSetup } = require('./services/auditLog');
|
||||||
|
liSetup(`Todo due reminders: enabled (lead ${TODO_REMINDER_LEAD_DAYS}d)`);
|
||||||
|
|
||||||
|
const tz = process.env.TZ || 'UTC';
|
||||||
|
todoReminderTask = cron.schedule('0 9 * * *', async () => {
|
||||||
|
try {
|
||||||
|
const { send } = require('./services/notificationService');
|
||||||
|
|
||||||
|
// Select unchecked todos with a due date inside the lead window
|
||||||
|
// that haven't been reminded in the last 24 hours. `due_date` is
|
||||||
|
// stored as a YYYY-MM-DD text; SQLite date() handles it directly.
|
||||||
|
const todos = db.prepare(`
|
||||||
|
SELECT ti.id, ti.trip_id, ti.name, ti.due_date, ti.assigned_user_id,
|
||||||
|
t.title AS trip_title, t.user_id AS trip_owner_id
|
||||||
|
FROM todo_items ti
|
||||||
|
JOIN trips t ON t.id = ti.trip_id
|
||||||
|
WHERE ti.checked = 0
|
||||||
|
AND ti.due_date IS NOT NULL
|
||||||
|
AND ti.due_date <> ''
|
||||||
|
AND date(ti.due_date) <= date('now', '+' || ? || ' days')
|
||||||
|
AND date(ti.due_date) >= date('now')
|
||||||
|
AND (ti.reminded_at IS NULL OR ti.reminded_at <= datetime('now', '-20 hours'))
|
||||||
|
`).all(TODO_REMINDER_LEAD_DAYS) as {
|
||||||
|
id: number; trip_id: number; name: string; due_date: string;
|
||||||
|
assigned_user_id: number | null; trip_title: string; trip_owner_id: number;
|
||||||
|
}[];
|
||||||
|
|
||||||
|
for (const todo of todos) {
|
||||||
|
const targetScope: 'user' | 'trip' = todo.assigned_user_id ? 'user' : 'trip';
|
||||||
|
const targetId = todo.assigned_user_id ?? todo.trip_id;
|
||||||
|
await send({
|
||||||
|
event: 'todo_due',
|
||||||
|
actorId: null,
|
||||||
|
scope: targetScope,
|
||||||
|
targetId,
|
||||||
|
params: {
|
||||||
|
todo: todo.name,
|
||||||
|
trip: todo.trip_title,
|
||||||
|
tripId: String(todo.trip_id),
|
||||||
|
due: todo.due_date,
|
||||||
|
},
|
||||||
|
}).catch(() => {});
|
||||||
|
db.prepare('UPDATE todo_items SET reminded_at = CURRENT_TIMESTAMP WHERE id = ?').run(todo.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { logInfo: li } = require('./services/auditLog');
|
||||||
|
if (todos.length > 0) {
|
||||||
|
li(`Todo reminders sent for ${todos.length} item(s)`);
|
||||||
|
}
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const { logError: le } = require('./services/auditLog');
|
||||||
|
le(`Todo reminder check failed: ${err instanceof Error ? err.message : err}`);
|
||||||
|
}
|
||||||
|
}, { timezone: tz });
|
||||||
|
}
|
||||||
|
|
||||||
// Version check: daily at 9 AM — notify admins if a new TREK release is available
|
// Version check: daily at 9 AM — notify admins if a new TREK release is available
|
||||||
let versionCheckTask: ScheduledTask | null = null;
|
let versionCheckTask: ScheduledTask | null = null;
|
||||||
|
|
||||||
@@ -280,4 +355,4 @@ function stop(): void {
|
|||||||
if (trekPhotoCacheTask) { trekPhotoCacheTask.stop(); trekPhotoCacheTask = null; }
|
if (trekPhotoCacheTask) { trekPhotoCacheTask.stop(); trekPhotoCacheTask = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export { start, stop, startDemoReset, startTripReminders, startVersionCheck, startIdempotencyCleanup, startTrekPhotoCacheCleanup, loadSettings, saveSettings, VALID_INTERVALS };
|
export { start, stop, startDemoReset, startTripReminders, startTodoReminders, startVersionCheck, startIdempotencyCleanup, startTrekPhotoCacheCleanup, loadSettings, saveSettings, VALID_INTERVALS };
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export type NotifEventType =
|
|||||||
| 'trip_invite'
|
| 'trip_invite'
|
||||||
| 'booking_change'
|
| 'booking_change'
|
||||||
| 'trip_reminder'
|
| 'trip_reminder'
|
||||||
|
| 'todo_due'
|
||||||
| 'vacay_invite'
|
| 'vacay_invite'
|
||||||
| 'photos_shared'
|
| 'photos_shared'
|
||||||
| 'collab_message'
|
| 'collab_message'
|
||||||
@@ -29,6 +30,7 @@ const IMPLEMENTED_COMBOS: Record<NotifEventType, NotifChannel[]> = {
|
|||||||
trip_invite: ['inapp', 'email', 'webhook', 'ntfy'],
|
trip_invite: ['inapp', 'email', 'webhook', 'ntfy'],
|
||||||
booking_change: ['inapp', 'email', 'webhook', 'ntfy'],
|
booking_change: ['inapp', 'email', 'webhook', 'ntfy'],
|
||||||
trip_reminder: ['inapp', 'email', 'webhook', 'ntfy'],
|
trip_reminder: ['inapp', 'email', 'webhook', 'ntfy'],
|
||||||
|
todo_due: ['inapp', 'email', 'webhook', 'ntfy'],
|
||||||
vacay_invite: ['inapp', 'email', 'webhook', 'ntfy'],
|
vacay_invite: ['inapp', 'email', 'webhook', 'ntfy'],
|
||||||
photos_shared: ['inapp', 'email', 'webhook', 'ntfy'],
|
photos_shared: ['inapp', 'email', 'webhook', 'ntfy'],
|
||||||
collab_message: ['inapp', 'email', 'webhook', 'ntfy'],
|
collab_message: ['inapp', 'email', 'webhook', 'ntfy'],
|
||||||
|
|||||||
@@ -82,6 +82,13 @@ const EVENT_NOTIFICATION_CONFIG: Record<string, EventNotifConfig> = {
|
|||||||
navigateTextKey: 'notif.action.view_trip',
|
navigateTextKey: 'notif.action.view_trip',
|
||||||
navigateTarget: p => (p.tripId ? `/trips/${p.tripId}` : null),
|
navigateTarget: p => (p.tripId ? `/trips/${p.tripId}` : null),
|
||||||
},
|
},
|
||||||
|
todo_due: {
|
||||||
|
inAppType: 'navigate',
|
||||||
|
titleKey: 'notif.todo_due.title',
|
||||||
|
textKey: 'notif.todo_due.text',
|
||||||
|
navigateTextKey: 'notif.action.view_trip',
|
||||||
|
navigateTarget: p => (p.tripId ? `/trips/${p.tripId}` : null),
|
||||||
|
},
|
||||||
vacay_invite: {
|
vacay_invite: {
|
||||||
inAppType: 'navigate',
|
inAppType: 'navigate',
|
||||||
titleKey: 'notif.vacay_invite.title',
|
titleKey: 'notif.vacay_invite.title',
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Trip invite: "${p.trip}"`, body: `${p.actor} invited ${p.invitee || 'a member'} to the trip "${p.trip}".` }),
|
trip_invite: p => ({ title: `Trip invite: "${p.trip}"`, body: `${p.actor} invited ${p.invitee || 'a member'} to the trip "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `New booking: ${p.booking}`, body: `${p.actor} added a new ${p.type} "${p.booking}" to "${p.trip}".` }),
|
booking_change: p => ({ title: `New booking: ${p.booking}`, body: `${p.actor} added a new ${p.type} "${p.booking}" to "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Trip reminder: ${p.trip}`, body: `Your trip "${p.trip}" is coming up soon!` }),
|
trip_reminder: p => ({ title: `Trip reminder: ${p.trip}`, body: `Your trip "${p.trip}" is coming up soon!` }),
|
||||||
|
todo_due: p => ({ title: `To-do due: ${p.todo}`, body: `"${p.todo}" in "${p.trip}" is due on ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Vacay Fusion Invite', body: `${p.actor} invited you to fuse vacation plans. Open TREK to accept or decline.` }),
|
vacay_invite: p => ({ title: 'Vacay Fusion Invite', body: `${p.actor} invited you to fuse vacation plans. Open TREK to accept or decline.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} photos shared`, body: `${p.actor} shared ${p.count} photo(s) in "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} photos shared`, body: `${p.actor} shared ${p.count} photo(s) in "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `New message in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `New message in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -111,6 +112,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Einladung zu "${p.trip}"`, body: `${p.actor} hat ${p.invitee || 'ein Mitglied'} zur Reise "${p.trip}" eingeladen.` }),
|
trip_invite: p => ({ title: `Einladung zu "${p.trip}"`, body: `${p.actor} hat ${p.invitee || 'ein Mitglied'} zur Reise "${p.trip}" eingeladen.` }),
|
||||||
booking_change: p => ({ title: `Neue Buchung: ${p.booking}`, body: `${p.actor} hat eine neue Buchung "${p.booking}" (${p.type}) zu "${p.trip}" hinzugefügt.` }),
|
booking_change: p => ({ title: `Neue Buchung: ${p.booking}`, body: `${p.actor} hat eine neue Buchung "${p.booking}" (${p.type}) zu "${p.trip}" hinzugefügt.` }),
|
||||||
trip_reminder: p => ({ title: `Reiseerinnerung: ${p.trip}`, body: `Deine Reise "${p.trip}" steht bald an!` }),
|
trip_reminder: p => ({ title: `Reiseerinnerung: ${p.trip}`, body: `Deine Reise "${p.trip}" steht bald an!` }),
|
||||||
|
todo_due: p => ({ title: `Aufgabe fällig: ${p.todo}`, body: `"${p.todo}" in "${p.trip}" ist am ${p.due} fällig.` }),
|
||||||
vacay_invite: p => ({ title: 'Vacay Fusion-Einladung', body: `${p.actor} hat dich eingeladen, Urlaubspläne zu fusionieren. Öffne TREK um anzunehmen oder abzulehnen.` }),
|
vacay_invite: p => ({ title: 'Vacay Fusion-Einladung', body: `${p.actor} hat dich eingeladen, Urlaubspläne zu fusionieren. Öffne TREK um anzunehmen oder abzulehnen.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} Fotos geteilt`, body: `${p.actor} hat ${p.count} Foto(s) in "${p.trip}" geteilt.` }),
|
photos_shared: p => ({ title: `${p.count} Fotos geteilt`, body: `${p.actor} hat ${p.count} Foto(s) in "${p.trip}" geteilt.` }),
|
||||||
collab_message: p => ({ title: `Neue Nachricht in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Neue Nachricht in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -122,6 +124,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Invitation à "${p.trip}"`, body: `${p.actor} a invité ${p.invitee || 'un membre'} au voyage "${p.trip}".` }),
|
trip_invite: p => ({ title: `Invitation à "${p.trip}"`, body: `${p.actor} a invité ${p.invitee || 'un membre'} au voyage "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Nouvelle réservation : ${p.booking}`, body: `${p.actor} a ajouté une réservation "${p.booking}" (${p.type}) à "${p.trip}".` }),
|
booking_change: p => ({ title: `Nouvelle réservation : ${p.booking}`, body: `${p.actor} a ajouté une réservation "${p.booking}" (${p.type}) à "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Rappel de voyage : ${p.trip}`, body: `Votre voyage "${p.trip}" approche !` }),
|
trip_reminder: p => ({ title: `Rappel de voyage : ${p.trip}`, body: `Votre voyage "${p.trip}" approche !` }),
|
||||||
|
todo_due: p => ({ title: `Tâche à échéance : ${p.todo}`, body: `"${p.todo}" dans "${p.trip}" est due le ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Invitation Vacay Fusion', body: `${p.actor} vous invite à fusionner les plans de vacances. Ouvrez TREK pour accepter ou refuser.` }),
|
vacay_invite: p => ({ title: 'Invitation Vacay Fusion', body: `${p.actor} vous invite à fusionner les plans de vacances. Ouvrez TREK pour accepter ou refuser.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} photos partagées`, body: `${p.actor} a partagé ${p.count} photo(s) dans "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} photos partagées`, body: `${p.actor} a partagé ${p.count} photo(s) dans "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Nouveau message dans "${p.trip}"`, body: `${p.actor} : ${p.preview}` }),
|
collab_message: p => ({ title: `Nouveau message dans "${p.trip}"`, body: `${p.actor} : ${p.preview}` }),
|
||||||
@@ -133,6 +136,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Invitación a "${p.trip}"`, body: `${p.actor} invitó a ${p.invitee || 'un miembro'} al viaje "${p.trip}".` }),
|
trip_invite: p => ({ title: `Invitación a "${p.trip}"`, body: `${p.actor} invitó a ${p.invitee || 'un miembro'} al viaje "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Nueva reserva: ${p.booking}`, body: `${p.actor} añadió una reserva "${p.booking}" (${p.type}) a "${p.trip}".` }),
|
booking_change: p => ({ title: `Nueva reserva: ${p.booking}`, body: `${p.actor} añadió una reserva "${p.booking}" (${p.type}) a "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Recordatorio: ${p.trip}`, body: `¡Tu viaje "${p.trip}" se acerca!` }),
|
trip_reminder: p => ({ title: `Recordatorio: ${p.trip}`, body: `¡Tu viaje "${p.trip}" se acerca!` }),
|
||||||
|
todo_due: p => ({ title: `Tarea pendiente: ${p.todo}`, body: `"${p.todo}" en "${p.trip}" vence el ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Invitación Vacay Fusion', body: `${p.actor} te invitó a fusionar planes de vacaciones. Abre TREK para aceptar o rechazar.` }),
|
vacay_invite: p => ({ title: 'Invitación Vacay Fusion', body: `${p.actor} te invitó a fusionar planes de vacaciones. Abre TREK para aceptar o rechazar.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} fotos compartidas`, body: `${p.actor} compartió ${p.count} foto(s) en "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} fotos compartidas`, body: `${p.actor} compartió ${p.count} foto(s) en "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Nuevo mensaje en "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Nuevo mensaje en "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -144,6 +148,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Uitnodiging voor "${p.trip}"`, body: `${p.actor} heeft ${p.invitee || 'een lid'} uitgenodigd voor de reis "${p.trip}".` }),
|
trip_invite: p => ({ title: `Uitnodiging voor "${p.trip}"`, body: `${p.actor} heeft ${p.invitee || 'een lid'} uitgenodigd voor de reis "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Nieuwe boeking: ${p.booking}`, body: `${p.actor} heeft een boeking "${p.booking}" (${p.type}) toegevoegd aan "${p.trip}".` }),
|
booking_change: p => ({ title: `Nieuwe boeking: ${p.booking}`, body: `${p.actor} heeft een boeking "${p.booking}" (${p.type}) toegevoegd aan "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Reisherinnering: ${p.trip}`, body: `Je reis "${p.trip}" komt eraan!` }),
|
trip_reminder: p => ({ title: `Reisherinnering: ${p.trip}`, body: `Je reis "${p.trip}" komt eraan!` }),
|
||||||
|
todo_due: p => ({ title: `Taak verloopt: ${p.todo}`, body: `"${p.todo}" in "${p.trip}" verloopt op ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Vacay Fusion uitnodiging', body: `${p.actor} nodigt je uit om vakantieplannen te fuseren. Open TREK om te accepteren of af te wijzen.` }),
|
vacay_invite: p => ({ title: 'Vacay Fusion uitnodiging', body: `${p.actor} nodigt je uit om vakantieplannen te fuseren. Open TREK om te accepteren of af te wijzen.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} foto's gedeeld`, body: `${p.actor} heeft ${p.count} foto('s) gedeeld in "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} foto's gedeeld`, body: `${p.actor} heeft ${p.count} foto('s) gedeeld in "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Nieuw bericht in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Nieuw bericht in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -155,6 +160,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Приглашение в "${p.trip}"`, body: `${p.actor} пригласил ${p.invitee || 'участника'} в поездку "${p.trip}".` }),
|
trip_invite: p => ({ title: `Приглашение в "${p.trip}"`, body: `${p.actor} пригласил ${p.invitee || 'участника'} в поездку "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Новое бронирование: ${p.booking}`, body: `${p.actor} добавил бронирование "${p.booking}" (${p.type}) в "${p.trip}".` }),
|
booking_change: p => ({ title: `Новое бронирование: ${p.booking}`, body: `${p.actor} добавил бронирование "${p.booking}" (${p.type}) в "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Напоминание: ${p.trip}`, body: `Ваша поездка "${p.trip}" скоро начнётся!` }),
|
trip_reminder: p => ({ title: `Напоминание: ${p.trip}`, body: `Ваша поездка "${p.trip}" скоро начнётся!` }),
|
||||||
|
todo_due: p => ({ title: `Задача к сроку: ${p.todo}`, body: `"${p.todo}" в поездке "${p.trip}" — срок ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Приглашение Vacay Fusion', body: `${p.actor} приглашает вас объединить планы отпуска. Откройте TREK для подтверждения.` }),
|
vacay_invite: p => ({ title: 'Приглашение Vacay Fusion', body: `${p.actor} приглашает вас объединить планы отпуска. Откройте TREK для подтверждения.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} фото`, body: `${p.actor} поделился ${p.count} фото в "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} фото`, body: `${p.actor} поделился ${p.count} фото в "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Новое сообщение в "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Новое сообщение в "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -166,6 +172,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `邀请加入"${p.trip}"`, body: `${p.actor} 邀请了 ${p.invitee || '成员'} 加入旅行"${p.trip}"。` }),
|
trip_invite: p => ({ title: `邀请加入"${p.trip}"`, body: `${p.actor} 邀请了 ${p.invitee || '成员'} 加入旅行"${p.trip}"。` }),
|
||||||
booking_change: p => ({ title: `新预订:${p.booking}`, body: `${p.actor} 在"${p.trip}"中添加了预订"${p.booking}"(${p.type})。` }),
|
booking_change: p => ({ title: `新预订:${p.booking}`, body: `${p.actor} 在"${p.trip}"中添加了预订"${p.booking}"(${p.type})。` }),
|
||||||
trip_reminder: p => ({ title: `旅行提醒:${p.trip}`, body: `你的旅行"${p.trip}"即将开始!` }),
|
trip_reminder: p => ({ title: `旅行提醒:${p.trip}`, body: `你的旅行"${p.trip}"即将开始!` }),
|
||||||
|
todo_due: p => ({ title: `待办事项即将到期:${p.todo}`, body: `"${p.trip}" 中的"${p.todo}"将于 ${p.due} 到期。` }),
|
||||||
vacay_invite: p => ({ title: 'Vacay 融合邀请', body: `${p.actor} 邀请你合并假期计划。打开 TREK 接受或拒绝。` }),
|
vacay_invite: p => ({ title: 'Vacay 融合邀请', body: `${p.actor} 邀请你合并假期计划。打开 TREK 接受或拒绝。` }),
|
||||||
photos_shared: p => ({ title: `${p.count} 张照片已分享`, body: `${p.actor} 在"${p.trip}"中分享了 ${p.count} 张照片。` }),
|
photos_shared: p => ({ title: `${p.count} 张照片已分享`, body: `${p.actor} 在"${p.trip}"中分享了 ${p.count} 张照片。` }),
|
||||||
collab_message: p => ({ title: `"${p.trip}"中的新消息`, body: `${p.actor}:${p.preview}` }),
|
collab_message: p => ({ title: `"${p.trip}"中的新消息`, body: `${p.actor}:${p.preview}` }),
|
||||||
@@ -177,6 +184,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `邀請加入「${p.trip}」`, body: `${p.actor} 邀請了 ${p.invitee || '成員'} 加入行程「${p.trip}」。` }),
|
trip_invite: p => ({ title: `邀請加入「${p.trip}」`, body: `${p.actor} 邀請了 ${p.invitee || '成員'} 加入行程「${p.trip}」。` }),
|
||||||
booking_change: p => ({ title: `新預訂:${p.booking}`, body: `${p.actor} 在「${p.trip}」中新增了預訂「${p.booking}」(${p.type})。` }),
|
booking_change: p => ({ title: `新預訂:${p.booking}`, body: `${p.actor} 在「${p.trip}」中新增了預訂「${p.booking}」(${p.type})。` }),
|
||||||
trip_reminder: p => ({ title: `行程提醒:${p.trip}`, body: `您的行程「${p.trip}」即將開始!` }),
|
trip_reminder: p => ({ title: `行程提醒:${p.trip}`, body: `您的行程「${p.trip}」即將開始!` }),
|
||||||
|
todo_due: p => ({ title: `待辦事項即將到期:${p.todo}`, body: `「${p.trip}」中的「${p.todo}」將於 ${p.due} 到期。` }),
|
||||||
vacay_invite: p => ({ title: 'Vacay 融合邀請', body: `${p.actor} 邀請您合併假期計畫。開啟 TREK 以接受或拒絕。` }),
|
vacay_invite: p => ({ title: 'Vacay 融合邀請', body: `${p.actor} 邀請您合併假期計畫。開啟 TREK 以接受或拒絕。` }),
|
||||||
photos_shared: p => ({ title: `已分享 ${p.count} 張照片`, body: `${p.actor} 在「${p.trip}」中分享了 ${p.count} 張照片。` }),
|
photos_shared: p => ({ title: `已分享 ${p.count} 張照片`, body: `${p.actor} 在「${p.trip}」中分享了 ${p.count} 張照片。` }),
|
||||||
collab_message: p => ({ title: `「${p.trip}」中的新訊息`, body: `${p.actor}:${p.preview}` }),
|
collab_message: p => ({ title: `「${p.trip}」中的新訊息`, body: `${p.actor}:${p.preview}` }),
|
||||||
@@ -188,6 +196,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `دعوة إلى "${p.trip}"`, body: `${p.actor} دعا ${p.invitee || 'عضو'} إلى الرحلة "${p.trip}".` }),
|
trip_invite: p => ({ title: `دعوة إلى "${p.trip}"`, body: `${p.actor} دعا ${p.invitee || 'عضو'} إلى الرحلة "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `حجز جديد: ${p.booking}`, body: `${p.actor} أضاف حجز "${p.booking}" (${p.type}) إلى "${p.trip}".` }),
|
booking_change: p => ({ title: `حجز جديد: ${p.booking}`, body: `${p.actor} أضاف حجز "${p.booking}" (${p.type}) إلى "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `تذكير: ${p.trip}`, body: `رحلتك "${p.trip}" تقترب!` }),
|
trip_reminder: p => ({ title: `تذكير: ${p.trip}`, body: `رحلتك "${p.trip}" تقترب!` }),
|
||||||
|
todo_due: p => ({ title: `مهمة مستحقة: ${p.todo}`, body: `"${p.todo}" في "${p.trip}" مستحقة في ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'دعوة دمج الإجازة', body: `${p.actor} يدعوك لدمج خطط الإجازة. افتح TREK للقبول أو الرفض.` }),
|
vacay_invite: p => ({ title: 'دعوة دمج الإجازة', body: `${p.actor} يدعوك لدمج خطط الإجازة. افتح TREK للقبول أو الرفض.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} صور مشتركة`, body: `${p.actor} شارك ${p.count} صورة في "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} صور مشتركة`, body: `${p.actor} شارك ${p.count} صورة في "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `رسالة جديدة في "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `رسالة جديدة في "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -199,6 +208,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Convite para "${p.trip}"`, body: `${p.actor} convidou ${p.invitee || 'um membro'} para a viagem "${p.trip}".` }),
|
trip_invite: p => ({ title: `Convite para "${p.trip}"`, body: `${p.actor} convidou ${p.invitee || 'um membro'} para a viagem "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Nova reserva: ${p.booking}`, body: `${p.actor} adicionou uma reserva "${p.booking}" (${p.type}) em "${p.trip}".` }),
|
booking_change: p => ({ title: `Nova reserva: ${p.booking}`, body: `${p.actor} adicionou uma reserva "${p.booking}" (${p.type}) em "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Lembrete: ${p.trip}`, body: `Sua viagem "${p.trip}" está chegando!` }),
|
trip_reminder: p => ({ title: `Lembrete: ${p.trip}`, body: `Sua viagem "${p.trip}" está chegando!` }),
|
||||||
|
todo_due: p => ({ title: `Tarefa com vencimento: ${p.todo}`, body: `"${p.todo}" em "${p.trip}" vence em ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Convite Vacay Fusion', body: `${p.actor} convidou você para fundir planos de férias. Abra o TREK para aceitar ou recusar.` }),
|
vacay_invite: p => ({ title: 'Convite Vacay Fusion', body: `${p.actor} convidou você para fundir planos de férias. Abra o TREK para aceitar ou recusar.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} fotos compartilhadas`, body: `${p.actor} compartilhou ${p.count} foto(s) em "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} fotos compartilhadas`, body: `${p.actor} compartilhou ${p.count} foto(s) em "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Nova mensagem em "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Nova mensagem em "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -210,6 +220,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Pozvánka do "${p.trip}"`, body: `${p.actor} pozval ${p.invitee || 'člena'} na výlet "${p.trip}".` }),
|
trip_invite: p => ({ title: `Pozvánka do "${p.trip}"`, body: `${p.actor} pozval ${p.invitee || 'člena'} na výlet "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Nová rezervace: ${p.booking}`, body: `${p.actor} přidal rezervaci "${p.booking}" (${p.type}) k "${p.trip}".` }),
|
booking_change: p => ({ title: `Nová rezervace: ${p.booking}`, body: `${p.actor} přidal rezervaci "${p.booking}" (${p.type}) k "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Připomínka výletu: ${p.trip}`, body: `Váš výlet "${p.trip}" se blíží!` }),
|
trip_reminder: p => ({ title: `Připomínka výletu: ${p.trip}`, body: `Váš výlet "${p.trip}" se blíží!` }),
|
||||||
|
todo_due: p => ({ title: `Úkol se blíží: ${p.todo}`, body: `"${p.todo}" ve výletě "${p.trip}" má termín ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Pozvánka Vacay Fusion', body: `${p.actor} vás pozval ke spojení dovolenkových plánů. Otevřete TREK pro přijetí nebo odmítnutí.` }),
|
vacay_invite: p => ({ title: 'Pozvánka Vacay Fusion', body: `${p.actor} vás pozval ke spojení dovolenkových plánů. Otevřete TREK pro přijetí nebo odmítnutí.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} sdílených fotek`, body: `${p.actor} sdílel ${p.count} foto v "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} sdílených fotek`, body: `${p.actor} sdílel ${p.count} foto v "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Nová zpráva v "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Nová zpráva v "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -221,6 +232,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Meghívó a(z) "${p.trip}" utazásra`, body: `${p.actor} meghívta ${p.invitee || 'egy tagot'} a(z) "${p.trip}" utazásra.` }),
|
trip_invite: p => ({ title: `Meghívó a(z) "${p.trip}" utazásra`, body: `${p.actor} meghívta ${p.invitee || 'egy tagot'} a(z) "${p.trip}" utazásra.` }),
|
||||||
booking_change: p => ({ title: `Új foglalás: ${p.booking}`, body: `${p.actor} hozzáadott egy "${p.booking}" (${p.type}) foglalást a(z) "${p.trip}" utazáshoz.` }),
|
booking_change: p => ({ title: `Új foglalás: ${p.booking}`, body: `${p.actor} hozzáadott egy "${p.booking}" (${p.type}) foglalást a(z) "${p.trip}" utazáshoz.` }),
|
||||||
trip_reminder: p => ({ title: `Utazás emlékeztető: ${p.trip}`, body: `A(z) "${p.trip}" utazás hamarosan kezdődik!` }),
|
trip_reminder: p => ({ title: `Utazás emlékeztető: ${p.trip}`, body: `A(z) "${p.trip}" utazás hamarosan kezdődik!` }),
|
||||||
|
todo_due: p => ({ title: `Teendő esedékes: ${p.todo}`, body: `"${p.todo}" (${p.trip}) határideje: ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Vacay Fusion meghívó', body: `${p.actor} meghívott a nyaralási tervek összevonásához. Nyissa meg a TREK-et az elfogadáshoz vagy elutasításhoz.` }),
|
vacay_invite: p => ({ title: 'Vacay Fusion meghívó', body: `${p.actor} meghívott a nyaralási tervek összevonásához. Nyissa meg a TREK-et az elfogadáshoz vagy elutasításhoz.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} fotó megosztva`, body: `${p.actor} ${p.count} fotót osztott meg a(z) "${p.trip}" utazásban.` }),
|
photos_shared: p => ({ title: `${p.count} fotó megosztva`, body: `${p.actor} ${p.count} fotót osztott meg a(z) "${p.trip}" utazásban.` }),
|
||||||
collab_message: p => ({ title: `Új üzenet a(z) "${p.trip}" utazásban`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Új üzenet a(z) "${p.trip}" utazásban`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -232,6 +244,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Invito a "${p.trip}"`, body: `${p.actor} ha invitato ${p.invitee || 'un membro'} al viaggio "${p.trip}".` }),
|
trip_invite: p => ({ title: `Invito a "${p.trip}"`, body: `${p.actor} ha invitato ${p.invitee || 'un membro'} al viaggio "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Nuova prenotazione: ${p.booking}`, body: `${p.actor} ha aggiunto una prenotazione "${p.booking}" (${p.type}) a "${p.trip}".` }),
|
booking_change: p => ({ title: `Nuova prenotazione: ${p.booking}`, body: `${p.actor} ha aggiunto una prenotazione "${p.booking}" (${p.type}) a "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Promemoria viaggio: ${p.trip}`, body: `Il tuo viaggio "${p.trip}" si avvicina!` }),
|
trip_reminder: p => ({ title: `Promemoria viaggio: ${p.trip}`, body: `Il tuo viaggio "${p.trip}" si avvicina!` }),
|
||||||
|
todo_due: p => ({ title: `Attività in scadenza: ${p.todo}`, body: `"${p.todo}" in "${p.trip}" scade il ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Invito Vacay Fusion', body: `${p.actor} ti ha invitato a fondere i piani vacanza. Apri TREK per accettare o rifiutare.` }),
|
vacay_invite: p => ({ title: 'Invito Vacay Fusion', body: `${p.actor} ti ha invitato a fondere i piani vacanza. Apri TREK per accettare o rifiutare.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} foto condivise`, body: `${p.actor} ha condiviso ${p.count} foto in "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} foto condivise`, body: `${p.actor} ha condiviso ${p.count} foto in "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Nuovo messaggio in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Nuovo messaggio in "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -243,6 +256,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Zaproszenie do "${p.trip}"`, body: `${p.actor} zaprosił ${p.invitee || 'członka'} do podróży "${p.trip}".` }),
|
trip_invite: p => ({ title: `Zaproszenie do "${p.trip}"`, body: `${p.actor} zaprosił ${p.invitee || 'członka'} do podróży "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Nowa rezerwacja: ${p.booking}`, body: `${p.actor} dodał rezerwację "${p.booking}" (${p.type}) do "${p.trip}".` }),
|
booking_change: p => ({ title: `Nowa rezerwacja: ${p.booking}`, body: `${p.actor} dodał rezerwację "${p.booking}" (${p.type}) do "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Przypomnienie o podróży: ${p.trip}`, body: `Twoja podróż "${p.trip}" zbliża się!` }),
|
trip_reminder: p => ({ title: `Przypomnienie o podróży: ${p.trip}`, body: `Twoja podróż "${p.trip}" zbliża się!` }),
|
||||||
|
todo_due: p => ({ title: `Zadanie z terminem: ${p.todo}`, body: `"${p.todo}" w "${p.trip}" — termin ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Zaproszenie Vacay Fusion', body: `${p.actor} zaprosił Cię do połączenia planów urlopowych. Otwórz TREK, aby zaakceptować lub odrzucić.` }),
|
vacay_invite: p => ({ title: 'Zaproszenie Vacay Fusion', body: `${p.actor} zaprosił Cię do połączenia planów urlopowych. Otwórz TREK, aby zaakceptować lub odrzucić.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} zdjęć udostępnionych`, body: `${p.actor} udostępnił ${p.count} zdjęcie/zdjęcia w "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} zdjęć udostępnionych`, body: `${p.actor} udostępnił ${p.count} zdjęcie/zdjęcia w "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Nowa wiadomość w "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Nowa wiadomość w "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
@@ -254,6 +268,7 @@ const EVENT_TEXTS: Record<string, Record<NotifEventType, EventTextFn>> = {
|
|||||||
trip_invite: p => ({ title: `Undangan perjalanan: "${p.trip}"`, body: `${p.actor} mengundang ${p.invitee || 'seorang anggota'} ke perjalanan "${p.trip}".` }),
|
trip_invite: p => ({ title: `Undangan perjalanan: "${p.trip}"`, body: `${p.actor} mengundang ${p.invitee || 'seorang anggota'} ke perjalanan "${p.trip}".` }),
|
||||||
booking_change: p => ({ title: `Pemesanan baru: ${p.booking}`, body: `${p.actor} menambahkan "${p.booking}" (${p.type}) baru ke "${p.trip}".` }),
|
booking_change: p => ({ title: `Pemesanan baru: ${p.booking}`, body: `${p.actor} menambahkan "${p.booking}" (${p.type}) baru ke "${p.trip}".` }),
|
||||||
trip_reminder: p => ({ title: `Pengingat perjalanan: ${p.trip}`, body: `Perjalanan Anda "${p.trip}" akan segera tiba!` }),
|
trip_reminder: p => ({ title: `Pengingat perjalanan: ${p.trip}`, body: `Perjalanan Anda "${p.trip}" akan segera tiba!` }),
|
||||||
|
todo_due: p => ({ title: `Tugas jatuh tempo: ${p.todo}`, body: `"${p.todo}" di "${p.trip}" jatuh tempo pada ${p.due}.` }),
|
||||||
vacay_invite: p => ({ title: 'Undangan Penggabungan Vacay', body: `${p.actor} mengundang Anda untuk menggabungkan rencana liburan. Buka TREK untuk menerima atau menolak.` }),
|
vacay_invite: p => ({ title: 'Undangan Penggabungan Vacay', body: `${p.actor} mengundang Anda untuk menggabungkan rencana liburan. Buka TREK untuk menerima atau menolak.` }),
|
||||||
photos_shared: p => ({ title: `${p.count} foto dibagikan`, body: `${p.actor} membagikan ${p.count} foto di "${p.trip}".` }),
|
photos_shared: p => ({ title: `${p.count} foto dibagikan`, body: `${p.actor} membagikan ${p.count} foto di "${p.trip}".` }),
|
||||||
collab_message: p => ({ title: `Pesan baru di "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
collab_message: p => ({ title: `Pesan baru di "${p.trip}"`, body: `${p.actor}: ${p.preview}` }),
|
||||||
|
|||||||
Reference in New Issue
Block a user