Push reminders: personal to the notes, generic without them
The daily reminder said one generic line to everyone, because the server
never sees a deck. Now the notification text is written in the browser
(lib/reminder.ts) and travels with the subscription:
- Personal: "3 cards are due in Sky deck", in the app's language, tapping
through to /review. Without notes (or with nothing due) it is the plain
nudge, translated in both languages — push.title / push.due.* / push.generic.
- POST /api/push accepts an optional message {title, body, url}; url must be
a path on this app, so a stolen subscription cannot make the notification
link elsewhere. The store keeps endpoint, keys, the message and createdAt
(a refresh no longer resets the timestamp).
- components/PushReminderSync.tsx refreshes the stored text when the app
opens and whenever the tab becomes visible (throttled to twice a day), so a
count is only ever sent while it is true.
- scripts/send-reminders.mjs sends each device its own text and falls back to
PUSH_REMINDER_TITLE/_BODY for a subscription that has none
(scripts/reminder-payload.mjs, unit-checked).
- /api/push/test previews the real reminder with a "Test:" prefix.
- deploy/freezmo-reminders.{service,timer}: the daily send as a systemd user
timer (18:00 local, Persistent, randomised), documented in the README.
- Settings copy, privacy page (en+cs) and .env.example say exactly what the
server now holds.
- phase2-test grew the whole path with a faked browser subscription: personal
text in en and cs, the generic fallback in both, the URL guard, and the
sender's preference order (81 checks).
Version 0.8.2, SW v104.
This commit is contained in:
parent
6036238bf0
commit
77819b5961
18 changed files with 498 additions and 49 deletions
29
deploy/freezmo-reminders.service
Normal file
29
deploy/freezmo-reminders.service
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
[Unit]
|
||||
# Freezmo — the daily review reminder.
|
||||
#
|
||||
# Reminders are personal: each browser composes the notification text from its
|
||||
# own notes (how many cards are due, in which deck, in the user's language) and
|
||||
# stores it with its push subscription. This oneshot sends exactly that text to
|
||||
# every device, falling back to the generic PUSH_REMINDER_TITLE/_BODY for a
|
||||
# subscription that has none. It reads the same shared env and data directory
|
||||
# as the production service, so it sees the subscriptions the app wrote.
|
||||
#
|
||||
# Install (per user, together with the timer):
|
||||
# cp ~/freezmo/deploy/freezmo-reminders.{service,timer} ~/.config/systemd/user/
|
||||
# systemctl --user daemon-reload
|
||||
# systemctl --user enable --now freezmo-reminders.timer
|
||||
# Logs: journalctl --user -u freezmo-reminders -n 20
|
||||
Description=Freezmo daily review reminder
|
||||
Documentation=file://%h/freezmo/README.md
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
# The release's server directory, so `.freezmo/push` is the exact directory the
|
||||
# running app writes subscriptions to (the standalone server has no .env file
|
||||
# handling of its own — the env comes from EnvironmentFile, like the service).
|
||||
WorkingDirectory=%h/freezmo-prod/current/server
|
||||
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
EnvironmentFile=-%h/freezmo-prod/shared/env.local
|
||||
ExecStart=/bin/node %h/freezmo-prod/current/src/scripts/send-reminders.mjs
|
||||
Loading…
Add table
Add a link
Reference in a new issue