Commit Graph
9832 Commits
Author SHA1 Message Date
Marc-André Bombeck 188d21d6ac Merge branch 'fix/v1474-offline' into release/v1.47.4
# Conflicts:
#	src/__tests__/baselines/version-stamp-comments.json
2026-11-11 21:32:11 +01:00
Marc-André Bombeck 59f94683ae Merge branch 'fix/v1474-push' into release/v1.47.4 2026-11-11 21:32:03 +01:00
Marc-André Bombeck ee04c5bcf4 test: register the push unsubscribe DELETE by hand
The DELETE behind "Disable push" moved into the shared
unsubscribeWebPush helper under lib/, which the destructive-control
detectors do not scan. The card's entry stays and says where the
request goes out now.
2026-11-11 20:55:08 +01:00
Marc-André Bombeck af623d12db fix(push): send the test push to every device, in the user's language
The Web Push test went to the newest subscription only. With a phone
and a laptop subscribed, Test pressed on the laptop buzzed the phone
and the laptop looked broken. The text was English in every locale,
and the push was dialled through the library's own transport, past the
pinned safeFetch that real sends use.

The route now calls sendViaWebPush, so the test reaches every
subscription, reaps endpoints the push service reports gone, and goes
out through the same dial as a reminder. Title and body come from the
account's locale. The response keeps ok and latencyMs and gains reason;
sent and perEndpoint are gone, nothing read them.
2026-11-11 20:52:36 +01:00
Marc-André Bombeck 37aa6052c6 test(pwa): tap a visible dashboard link in the offline journey
The desktop shell renders the bottom bar's links too; pick the first
one that is visible so the tap lands on the sidebar.
2026-11-11 20:47:38 +01:00
Marc-André Bombeck 7f8ba0ae98 fix(push): tell iPhone Safari users to open the Home Screen app
iOS offers Web Push only to a web app opened from the Home Screen. In a
Safari tab the push card said the browser does not support push
notifications, and the step that fixes it was only in the docs. On an
iPhone or iPad outside the installed app the card now says to add
HealthLog to the Home Screen, open it from there and turn push on.

WebPushCard grows by two lines for the second sentence; its baseline
goes from 201 to 203, still below the 215 it had before the previous
change.
2026-11-11 20:46:58 +01:00
Marc-André Bombeck 32911fea37 fix(push): remove this browser's subscription on sign-out
Signing out ended the session and wiped the caches but left the Web
Push subscription in place, in the browser and on the server. The
browser kept showing the signed-out account's medication reminders,
also after someone else signed in on it, and the next account's
Settings showed push as already on.

Sign-out now runs the same steps as "Disable push" before the logout
call: DELETE the server row while the session is still valid, then
unsubscribe in the browser. Both callers share one helper. A failure
there does not block signing out. With the dispatcher no longer
counting "no subscription" as a failed send, signing out does not
start the five-failure countdown on the account's channel.
2026-11-11 20:44:24 +01:00
Marc-André Bombeck 00c56b0dd5 fix(pwa): keep the status bar readable in the light theme
The installed iOS app uses the `black-translucent` status bar, which
always draws white text, and the shell painted the strip behind it in
the card colour. In the light theme that is near-white, so the clock
and battery vanished. The light theme now paints the strip in its dark
ink; the dark theme is unchanged.
2026-11-11 20:43:46 +01:00
Marc-André Bombeck 72dcd32f7a fix(pwa): drop the Medications home-screen shortcut
The manifest is one static file for every account, so the Medications
shortcut was offered with the module switched off and opened its
"switched off" notice. Only shortcuts into pages no module can turn off
remain: Add measurement and Today. The `source=shortcut` parameter goes
too; nothing read it, and the measurements filter discarded it as an
unknown data source. The install `id` keeps its `source=pwa`, which is
an identity string only.
2026-11-11 20:43:23 +01:00
Marc-André Bombeck 51827f43a6 chore(pwa): cut the service worker's comments to what a reader needs
The worker is served as written, so every browser downloaded its
release history: more than half of the file was comments, many of them
dated notes about earlier states of the code. The comments now say what
the code does and why, without release numbers. No code changes; the
fallback version literal and its rewrite marker are untouched.
2026-11-11 20:42:32 +01:00
Marc-André Bombeck 6d8f533deb fix(auth): keep the page when sign-out is refused
The sign-out mutation took every HTTP answer as success. A refused
logout (403 from the demo guard, a 429, a 5xx) wiped the client caches
and showed the sign-in page while the session cookie stayed valid, so
the record pages opened again on the next visit. The call now goes
through apiFetch, which throws on a non-2xx answer, and the error toast
says the person is still signed in instead of naming a network error.
2026-11-11 20:42:25 +01:00
Marc-André Bombeck a824484d4a refactor(pwa): drop the unreachable precached-shell fallback
Without a connection a navigation also looked for itself in the static
cache, which only ever holds icons and catalogs; `/` is never
precached, and the install step deleted a leftover root document only
so that this lookup could not find it. Both go; the page cache and the
offline page remain.
2026-11-11 20:41:05 +01:00
Marc-André Bombeck 09f3280c1c fix(pwa): keep signed-in onboarding pages out of the page cache
The proxy treats /onboarding/* as public, so those pages are not sent
no-store, and the worker stored the documents a signed-in account saw
during setup, against its own rule that only public pages land in the
page cache. The worker now skips them by path, like the share view.
2026-11-11 20:40:32 +01:00
Marc-André Bombeck 61f9447887 fix(pwa): focus the open window on a notification tap instead of reloading it
Tapping a reminder called `navigate()` on the first app window even
when it already showed the target page: a full document load that
dropped the query cache and any open sheet. The rejection of an
unawaited `navigate()` on an uncontrolled window went unhandled.

A window already on the target is now only focused, and preferred over
the first window. Otherwise the window is focused, then navigated; if
that fails a new window opens.
2026-11-11 20:39:59 +01:00
Marc-André Bombeck 25d087aaa2 fix(push): stop handling the clear push in the service worker
The worker answered a `type: "clear"` push by closing the dose
reminder for that slot and showing nothing. Browsers do not allow a push
without a visible notification on a subscription that promised one:
Safari removes the subscription after a few, Chromium shows its own
"updated in the background" notice. The server no longer sends it, so
the branch and its tests go; a reminder stays until it is tapped or the
next reminder with the same tag replaces it.
2026-11-11 20:39:08 +01:00
Marc-André Bombeck 31f296a03e fix(push): stop the invisible push sent for every recorded dose
Each taken or skipped dose sent a `clear` push to every subscription of
the account. The service worker closed the pending reminder and showed
nothing. A subscription made with userVisibleOnly may not receive
pushes that show no notification: Safari revokes it after a few, the
next reminder meets a 410 and the channel is switched off, with nothing
in the app saying why.

The clear push, its three call sites and the badge count only it fed
are removed. A reminder already on screen now stays until it is tapped,
dismissed or replaced by the next reminder for the same dose. The
sender's badge field went too: nothing else ever set it. The parity
snapshots lose the two recorded effects.
2026-11-11 20:39:07 +01:00
Marc-André Bombeck b3b4c9684e fix(pwa): keep the daily briefing text out of IndexedDB
The dashboard snapshot carries the daily briefing and its recall line,
and the persister wrote the snapshot to IndexedDB for up to a day,
although its own comment said narrative text never reaches the disk.

The persister now nulls both fields before the write. The web
dashboard reads the briefing from the Today digest, which is never
persisted, so the restored snapshot paints exactly what it did before.
2026-11-11 20:38:41 +01:00
Marc-André Bombeck 70295f45cb fix(push): bring Web Push back after turning it on again
A channel switched off because the last browser's endpoint answered 410
(app removed from the Home Screen, site data cleared) stayed off when push
was turned on again: the subscription was stored and the test push
arrived, but no reminder ever did. Subscribing now re-enables a disabled
channel through reEnableChannel, the same reset the status card's button
runs.

Turning push off on the last browser deleted the subscription and left
the channel on, and each later reminder was booked as a failed send
(web_push_no_subscriptions) until the channel gave up after five. The
dispatcher now leaves channel state alone when a sender had nobody to
send to; APNs without a paired iPhone and email without an address or
SMTP had the same problem and are covered by the same rule.
2026-11-11 20:38:39 +01:00
Marc-André Bombeck 960f3c4878 fix(pwa): keep the static cache across sign-in and sign-out
The session wipe deleted every HealthLog cache, the static one too,
although it holds only locale catalogs and icons. Each sign-in and
sign-out threw away the 190 KB catalog and refetched it.

The wipe now deletes the page cache and the API caches older releases
left behind, current and legacy versions, and keeps the static cache.
Function name and signature are unchanged.
2026-11-11 20:37:14 +01:00
Marc-André Bombeck 433b92a67f fix(pwa): restore the dashboard snapshot only for its own account
On a cold start the IndexedDB snapshot was restored before
/api/auth/me had answered, and the owner check was skipped while the
account was unknown. On a shared browser the previous account's
dashboard could be put into the next account's cache until the refetch
landed.

The restore now waits until the signed-in account is known and runs
again when it changes. A snapshot whose owner is not that account, or
that was written before any account was known, is discarded and wiped.

Because the restore now lands after the page has mounted, it fills
queries that have no data yet instead of skipping every query the cache
already knows; data the page holds is still never overwritten.
2026-11-11 20:36:25 +01:00
Marc-André Bombeck 0df65ce396 fix(pwa): keep the open page when a link is tapped offline
Without a connection the strip said "Showing your last synced data",
but one tap on a tab or link traded the readable page for the worker's
bare offline page: signed-in pages are no-store, so the document
navigation Next falls back to has nothing to load.

While the browser reports offline, a tap on a link to another page of
the app now stays on the open page and repeats the hint in a toast. The
strip says what is true: this page stays readable, other pages open once
the connection is back. The README stops promising offline support.

A cold start or a typed URL without a connection still gets the offline
page; the e2e spec keeps that and adds the tap.
2026-11-11 20:33:57 +01:00
Marc-André Bombeck 1d7169b266 refactor(pwa): drop the service worker's API data cache
The worker buffered every allowlisted API read, the 60-second version
poll included, and wrote it to CacheStorage before the page got it. No
page could ever be opened offline to read those answers back, since
every signed-in document is no-store. What the cache did do: keep one
account's readings on a shared device, answer a refetch that failed
right after a save with the copy from before it, and let a late write
recreate the cache after the sign-out wipe.

API requests and the hashed /_next/static/ chunks now go to the network
untouched; the HTTP cache already holds the immutable chunks. The
cache-name pattern keeps `data`, so caches older releases left behind
are still deleted on activate and by the session wipe.

The TypeScript copy of the allowlist and its test go with it, and the
record-switch e2e spec now checks that nothing under /api/ is kept on
disk before or after the switch.
2026-11-11 20:31:15 +01:00
Marc-André Bombeck aae029d94e Merge pull request #1365 from MBombeck/release/v1.47.2
release: v1.47.2
v1.47.2
2026-11-11 07:21:17 +01:00
Marc-André Bombeck 0a0df4946a chore(release): v1.47.2 2026-11-11 07:01:25 +01:00
Marc-André Bombeck 9ef3331f85 test(pwa): give the sw-version script tests time to start node 2026-11-11 01:56:44 +01:00
Marc-André Bombeck a1cfb570c1 test(browser): wait for the file's own iframe before the first command
Every browser test iframe is named "vitest-iframe" and the Playwright
provider looks the frame up by that name. Once a page runs a second
file, the first file's iframe can still be found until its detach event
arrives, so the second file's first click lands in a frame that is going
away and fails with "Frame was detached". With fifteen browser files and
at most twelve pages, three pages now run two files each, and a full
local run failed this way in four of five tries. The setup now waits
until the frame found by name is its own.
2026-11-11 01:47:48 +01:00
Marc-André Bombeck 13fd7edfd8 docs(changelog): do not promise a unit change on a (calc) marker 2026-11-11 01:20:25 +01:00
Marc-André Bombeck 3745a0207d test(gki): check the chart's series colour without a browser
In the full run the browser version made a neighbouring browser file
fail with a detached frame, while each passed alone. Render the chart in
Node with Recharts stubbed, as the coach result chart tests do.
2026-11-11 01:11:27 +01:00
Marc-André Bombeck 8b607ab501 test: drive the revoke race against Postgres and type the new fixtures
The revoke audit is now asserted in the integration race test where a
rotation commits while the turn-off waits, instead of a route test that
mocked the handler and the client. The other new tests type their
fixtures rather than casting them away.
2026-11-11 01:00:23 +01:00
Marc-André Bombeck 8aa8379861 fix(gki): draw the index chart in a chart token
The custom metric chart drew its line in --primary, which put the index
in the action colour right under the ketone series drawn in a chart
token. Give the chart an optional series colour, default primary so a
custom metric looks as before, and draw the index in --chart-4.
2026-11-11 00:52:41 +01:00
Marc-André Bombeck 35b4e75a53 refactor(reminders): share the satisfiable select with the Telegram button
The select is checked against SatisfiableReminder, so a column the
reschedule needs cannot be left out of a narrowed read again, and the
handler is back under its size baseline.
2026-11-11 00:52:40 +01:00
Marc-André Bombeck 0b3c013af7 fix(i18n): name the period in the forecast style hints
Only the selected style's hint is shown, so "it may start" and "they
may fall" had no noun on screen to point back to. Name the period in
every locale that left it implied, and put the Korean hints in the same
register as the rest of the card.
2026-11-11 00:49:02 +01:00
Marc-André Bombeck b4a5442fb5 fix(labs): leave "/" to the browser when no search field is on screen
The shortcut called preventDefault before checking for the field, so on
a labs or documents page still loading, in error, or with nothing
recorded, "/" did nothing and the browser lost its own use of the key.
2026-11-11 00:48:16 +01:00
Marc-André Bombeck ee1eb921a9 fix(ui): announce a filter bar's result count
The count beside every filter bar was a plain span, so a screen reader
heard nothing while a list narrowed, only the empty state once nothing
was left. Make it a polite status region.
2026-11-11 00:47:27 +01:00
Marc-André Bombeck 128cf55ab6 fix(day): fit the GKI tile's pair line in its column
The single-reading line spelled out both readings with their names and
units and was cut off after the glucose in every layout. Show the two
numbers with one unit, which fits the tile, and keep the worded pair for
assistive tech.
2026-11-11 00:46:30 +01:00
Marc-André Bombeck a7e98b980e fix(cycle): audit the feeds a revoke actually turned off
DELETE audited the feed it read before its two revoke passes. When a
rotation committed in between, that feed was already revoked by the
rotation and the second pass revoked its successor, so the trail showed
the old feed revoked twice and the new one never. Read back the rows
carrying this request's revokedAt stamp and audit each.
2026-11-11 00:45:22 +01:00
Marc-André Bombeck 5ebfac1259 fix(gki): build the index from the glucose shown beside it
A reading entered in mmol/L is stored as whole mg/dL, and the index
divided the unrounded conversion, so a 5.2 / 0.3 pair printed beside a
17.4 rather than 17.3. Round the glucose to the decimal it is shown with
first and divide that. The tiles also printed a two-decimal ketone
reading to one decimal; show it as recorded.
2026-11-11 00:44:29 +01:00
Marc-André Bombeck c8b32ccaea fix(documents): judge the review flag on the unit as stored
A "1 (calc)" unit is stored as "1", the explicit no-unit value, but the
dimensionless check ran on the raw string and missed it, so the read
went straight through instead of to review.
2026-11-11 00:42:05 +01:00
Marc-André Bombeck 6c8458d911 fix(reminders): keep a course window's end through every reschedule
The first due slot of a Coach course honoured endsOn, but the tick's
roll-on, satisfy and skip all built their schedule input without it, so
a seven-day twice-daily course kept rolling on indefinitely and never
reached the null nextDueAt the expired-course cleanup waits for.
Carry endsOn on RollableReminder and SatisfiableReminder and select it
wherever a caller narrows the row.
2026-11-11 00:41:31 +01:00
Marc-André Bombeck f521cdbbaf docs(changelog): correct the checkup repeat wording and note the zlib update 2026-11-11 00:27:46 +01:00
Marc-André Bombeck f20efca500 Merge PR #1370: biomarker filtering and search in the labs list 2026-11-11 00:25:12 +01:00
Marc-André Bombeck ba3ee968c7 style(labs): format the filter rail files 2026-11-11 00:22:26 +01:00
Marc-André Bombeck c59f4d7e65 docs(changelog): describe the labs search and filters 2026-11-11 00:18:50 +01:00
Marc-André Bombeck 142307acf5 fix(i18n): put the Korean biomarker count after the noun
"바이오마커 {count}개" reads as a count of biomarkers; the counter
before the noun read like a fragment.
2026-11-11 00:18:27 +01:00
Marc-André Bombeck cc9cbc4640 fix(labs): read odd filter links kindly and match names without accents
A link with the date bounds reversed showed an empty list; it now reads
as the range it means. The search ignores accents as well as case, so
"hamoglobin" finds "Hämoglobin". The category pill only appears when
there are categories to choose between, or when a link already filters
by the only one, so the filter can still be cleared where it shows.
2026-11-11 00:18:17 +01:00
Marc-André Bombeck a40c9e4ea4 fix(labs): keep keyboard focus where it belongs around the search field
Clearing the search with its button removed the button under the
focus and dropped focus onto the page; it now stays in the field. The
/ shortcut no longer pulls focus out of an open dialog, menu or list,
and ignores a key another handler already took or one typed while an
input method is composing.
2026-11-11 00:16:39 +01:00
Marc-André Bombeck 74464184c5 fix(labs): keep what is typed in the search field when the URL catches up
The labs search writes its trimmed query to the URL after a short pause,
and every change of the URL query replaced the field's text. The
field's own write came back as well: a trailing space vanished, so
"vitamin " followed by "d" searched for "vitamind", and a letter
typed while the write landed was lost. A query the field wrote itself
now leaves the field alone; one from back/forward, a link or Reset
still replaces it.
2026-11-11 00:15:24 +01:00
Marc-André Bombeck bcf411f38b chore(deps): move node-forge to 1.4.3
The override in pnpm-workspace.yaml now floors node-forge at 1.4.3, which
clears the five advisories against 1.4.2 and leaves pnpm audit --prod
clean. It arrives only under @parse/node-apn, which pins 1.4.2 exactly;
the APNs sender uses token auth, so the certificate path never runs.
2026-11-11 00:14:35 +01:00
Marc-André Bombeck 04a4353e50 fix(labs): put the search field beside the filter rail, not in it
Inside the rail, the full-width search field wrapped on phones and left
the filter icon alone on a line above it, and the shared FilterBar grew
a class override and a second layout rule to make room. The search
field now sits next to the unchanged rail: above it on phones, where
the pills keep the usual scroll row, and in one line with it from sm
up. FilterBar and docs/ui-guidelines.md are back as they were, so the
filter-bar grammar stays the one ui-standards.md defers to.
2026-11-11 00:13:41 +01:00
Marc-André Bombeck 2c63bf7a3a chore(labs): tidy the filter rail follow-ups
Drop a local-only ignore line, end the search placeholder with the
ellipsis character the other placeholders use, and put the
UrlFilterSyncOptions interface above the hook's doc comment so that
comment documents the hook.
2026-11-11 00:12:04 +01:00