The feature-branch CI does not run the formatting check, so the conflict-resolved
files (AboutTab, useAccountSettings, useTripExport), the native shell and iap
module, and the i18n files touched by the translation scripts were never run
through Prettier. Format them all; no behaviour changes.
The discriminated union did not narrow under the native-shell tsconfig, so the
failure code read as absent on the whole type. Use a flat result with optional
instanceUrl and code instead: both are always present on the type, no narrowing
needed, and the ok plus instanceUrl guard still routes success from refusal.
The full suite, which the feature-branch CI does not run, catches four things the
native work still had: a non-narrowed union in the claim flow (tsc), a helper
named useFake that the react-hooks rule read as a hook (eslint), two iap types
exported but used only inside the file (knip), and the Hebrew native strings
present as a file but never spread into he's merged bundle (the aggregated key
parity test). All four fixed.
The he locale had every domain but native.ts, which broke i18n parity, the shared
coverage specs and the client parity test once the native strings existed. Add
he/native.ts with all native keys translated. Also correct the LoginPage size
baseline to its current line count.
After picking a plan, the app now carries the whole purchase itself when the build
can take one: choose your address (checked for free as you type, against the same
endpoint the website uses), your email, and subscribe. The receipt goes to
/api/iap/redeem, and a Setting up your TREK screen waits on the new address and
loads it the moment it answers.
The purchase sits behind an IapClient seam (src/native/iap.ts): a fake drives the
whole flow in development and preview, the native client reports itself
unavailable until the store plugin and products exist, and until then the plan
picker still finishes on the web, so nothing half-wired faces a customer. New
claim and provisioning strings across all locales (en, de and the five largest
European languages translated).
The address screen sits on the near-black surface, so set the status bar to draw
its clock and icons in light while it is up; the server's own UI takes over the
style once it loads. The address field's on-screen return key now reads Go. Both
fail quietly where there is no native status bar (web, the simulator).
The Get TREK Cloud path now leads to a plan picker that reads the live plans
from the site (/api/plans), so the prices and quotas always match the website.
Monthly and yearly in a segmented toggle, the year marked with its saving, each
plan a card with its price and what it includes. Subscribe is wired to finish on
the web order for now; it becomes an Apple/Google in-app purchase once the store
products exist. New strings across all locales (en and de translated).
The app's own screen now opens on a welcome: the TREK mark, a line about what
TREK is, and two clear paths — connect the TREK you already open in the browser,
or get TREK Cloud. Both lead to polished screens: the address field with live
validation, a shake and haptic on a bad address and a success tick before the
server takes over; and a TREK Cloud intro with what it offers and a way in.
Screens slide and stagger in, buttons give a tap, and it all respects the safe
areas, light and dark, and reduced motion. Self-hosters are unaffected: cloud is
its own path, never in the way. New native strings are added across all locales
(English placeholders outside en/de, to translate).
A booking dropped against another booking's time went back to the nearest
slot its own time allowed. With a 06:30 flight listed below the day's
untimed stops, every slot among them was against the flight, so a 13:00
transit dropped between two stops went back behind the flight on every
drop, and the flight's legs cannot be dragged out of the way.
The dropped row now stays where it was dropped and the other timed
bookings it puts out of order move next to it, the earlier ones ahead of
it and the later ones behind. Timed stops and notes never move: against
one of those the row is still seated by its time as before (#2631). The
phone's move applies the same rule before it refuses a move.
Closes#2983
Array.prototype.at is past the client's lib target, so the journey open-at
rule indexes the array. The packing row test expects the title that names
every bringer, a carousel test acts without a floating promise, and the
lint baseline drops to the counts this branch reaches.
A common item brought by several members showed the first bringer's avatar
and a bare "+2". The tooltip only named the first person, and on the phone
neither the row nor the item sheet said who else brings it (discussion
#2782).
packingBringerNames lists the bringer and every contributor; the desktop
row, the phone row and the phone item sheet use it for "brought by".
The public journey page on a phone shows the map with a card carousel, which
always opened on the first entry. The owner's "newest first" choice for the
link was ignored there, so readers following a running trip had to swipe
through every day to reach the latest one (discussion #2851).
The rule for where a journey opens moves out of the journal hook into
journeyOpenAtEntryId, which the public page now uses as well: a newest-first
link opens on the latest day, otherwise a running journey opens on today as
the journal already does.
The carousel also settles on the card at the end it is scrolled to. The last
card can never reach the centre, so opening on the latest day used to
activate the day before it, in the journal as well.
An assistant writing expenses through the MCP was told "e.g. Accommodation, Food,
Transport" and stored exactly that, so "Food" and "food" sat side by side in the
MCP totals while the Costs tab already read both as one. A typed category in any
case, or a common name like Hotel or Flight, is now stored as its fixed key on
every path (REST, MCP, linked booking costs), the totals the MCP reads group by
that key, and the tools name the real keys. The client reads categories through
the same shared mapping. Any other text is still kept as given.
The journal tools said that a private entry stays off the share link. It never
did: the share link shows every entry that is not a draft, whatever its
visibility, and the web editor has no visibility at all. The tools now say so
and point to is_draft, which is what keeps an entry to the contributors.
The wiki explains why the location button needs HTTPS and how to run TREK behind
an HTTPS tunnel while still using it over plain HTTP at home, and that Android's
photo picker drops a photo's location while Immich and Synology keep it.
With a room kept for the whole trip and a second stay for a night away, the day
after the side trip took the first stay in the list as the hotel it woke up in.
The Days view then drew a drive from the kept room, often hundreds of kilometres
away, to the side trip's first stop or airport, next to the flight back. The
morning now belongs to the stay checked into last, and the evening to the stay
that runs on past the day. The map also passes a booking's own point at the edge
of the day, as the sidebar already did, so a taxi leaving from the hotel adds no
zero-length road from the hotel to itself.
The Tour booking type read like the Tours tab, and a booked food tour left people
looking at an empty Tours tab. It is now called Guided tour in every language,
the empty Tours tab says that booked tours go under Bookings, and the help
center and wiki use the new name. The stored type stays 'tour'.
Moves the Leaflet map's event bridges (click, context menu, viewport, POI drop
and the longitude-frame follower) out of MapView into leafletMapBridges, and
shares one POI drop target between the Leaflet and GL maps, so both map files
shrink instead of growing. The repair migration does its arithmetic in one
UPDATE per table, and the new tests drop their non-null assertions and loops.
A right-click, click or drag on a repeated world copy reported the longitude it
has on that copy (274.9 for -85.1). The place was saved there, the geocoder
found nothing, and a route to a place on the real world ran across the whole
map (#2976). Longitudes are now brought back onto [-180, 180] where the map
hands them out (Leaflet and GL), where they are stored (places, booking
endpoints, collection places, so REST, MCP, plugin RPC and importers alike)
and before reverse geocoding. A migration moves the ones already stored.
Leaflet draws a layer at its literal longitude only, so a trip from Los
Angeles to Auckland had its pins at opposite ends of the world while the
flight ran off the edge to the next copy (#2975). A trip that crosses the
antimeridian is now drawn in one longitude frame, seamed in the widest
stretch it leaves empty, so pins, routes, flights and every overlay lie on
one copy and the map opens across the Pacific. Every other trip has no frame
and draws exactly as before. The GL day fit also takes the short way.
A new install now starts with Journey and Collections switched on, next to
the trip addons (all but Road trip), Atlas, Vacay and the Naver import that
already were. Existing installs keep their addons as they are.
Closes#2975Closes#2976
The admin reset link is a component of its own instead of one made during render, the version line of the plugin panels moved into small modules, the new settings rows hand their save on as void, the date picker takes an accessible name for fields without a visible label (the Vacay hire date), the Vacay and poll tests type through the shared picker, and the theme, size and lint baselines are lowered to what the files hold now.
Every date in TREK was written in the interface language's format, and
English is en-US, so an English interface always showed and read
mm/dd/yyyy with no way to change it. A few native date inputs followed
the browser instead, so one screen could mix two formats.
date_format is now a user setting with an admin default, next to the
time format: automatic (the language's format, as before), YYYY-MM-DD,
YYYY/MM/DD, DD.MM.YYYY, DD/MM/YYYY or MM/DD/YYYY. The date picker shows,
pre-fills, hints and reads typed dates in it, and every date written in
numbers across the app (lists, cards, admin panels, CSV and PDF exports)
goes through one formatter. Dates written with a month name keep the
language's wording. The remaining native date inputs are the shared
picker now. The display settings MCP tool reads and writes the key.
Closes#2971
The display-currency check is its own helper (inDisplayCurrency), used by the Costs model and the shared trip page. The tests that pinned #2525's reading of a display-currency bill through the trip currency now pin the as-typed reading, which the balances use too; read in the trip currency, such a bill and its repayment may stay a euro cent apart.
On a yen trip read in euros, an expense entered in euros was booked through the yen at the rate frozen on entry and shown back at today's rate, so 1,081.09 EUR read 1,078.86 EUR a day later, and repaying exactly what was typed left 2.23 EUR owed.
Rows in the display currency now net apart in whole display cents, as entered, and the lists show them as typed. A payment in the display currency first pays what is owed between the two in such rows; only the rest goes through the trip currency at its frozen rate, so a trip-currency debt settled in the display currency stays settled when rates move (#1445). Every other row still nets in trip cents and converts once (#1382), so the balances add up to zero in both halves.
Closes#2965
The places toast is a plural group in every language, the points import goes through tourRepo instead of src/api, the hook and dialog keep their complexity, and the tests that checked the old confirm call or the GPX left-out count follow the new behaviour.
With the Tours addon on, a file picked or dropped on the places column goes
into the Tours import as soon as it holds one line, and that import reads
lines only. A Sygic or Trip-o-matic export (a route plus its stops) came in
as one tour and every place was dropped without a word.
The preview now counts a GPX file's waypoints as it already counted a KML
file's pins, and the dialog offers to add those points as places of the
trip, on by default. Confirming runs the Tours import for the lines and the
places import (points only) for the same files. A file whose tracks the
trip already holds can still bring its points.
Closes#2969
An iPhone or iPad running TREK from the Home Screen ignores print(), in
the page and in an iframe alike, so "Save as PDF" in the preview did
nothing there. Mobile Safari prints the same preview fine.
In the installed iOS app the preview now shows "Open in Safari" in
place of the dead button, with a line saying why. The link opens the
trip with ?export=pdf, and both shells open their export dialog or
sheet for it, so the PDF is one tap away once Safari is up. The
parameter is dropped once read and survives the login redirect.
Everywhere else the preview prints as before.
The preview overlay moves out of TripPDF.tsx into pdfPreview.ts, which
TripPDF had no room left for under lint:size, and its backdrop and
stacking now use the --overlay and --z-modal tokens.
- MAdminOidcCard is markup over the settings section's state, so it is
listed under mobileOnly with that reason.
- The default user settings views take their presets from
useDefaultUserSettings now and import only a type from mapSettingsModel,
so that pair names the map settings views alone.
- The phone's OIDC card moves into MAdminOidcCard, so the settings section
is back within its complexity limit after the managed-mode conditions.
- enabledPhotoProviderIds reads the enabled ids straight from the
repository and has a test of its own, which brings the addons coverage
back to its threshold.
- The owner sign-in and the photo provider the suite runs with move into
tests/helpers/journeyDetail.ts, so the page test is back under its line
baseline, which drops to 4028.
- Drop help/registry.base.ts, an unused copy of the help registry whose key
builders the i18n key check could not resolve.
- enabledPhotoProviderIds skips rows without an id instead of widening the type.
- The listed events read each addon once, in parallel, not in a loop.
- The narrowing test reuses the suite's service and swaps the addon answers.
- PlaceContribPanel keeps its complexity by naming the standalone check.
On a managed install the operator runs place search and routing, the plugin
runtime, the integrations and the documentation, so the admin and the users
no longer see switches and guides for them:
- The place search and routing card, the transit provider and the search log
are gone from the admin settings (desktop and mobile). places_provider is a
managed-locked setting, and the transit provider, search log and wanderer
routes refuse writes there. Share place details stays the admin's, as a card
of its own, hidden when TREK_PLACES_CONTRIBUTE switches it off.
- File types and trip reminders move to the right column, so the columns stay
balanced.
- Plugins disappear from the admin and the user settings, wanderer from Tours.
- The Help & Docs page and its links are not offered. The help center stays and
leaves out guides about what the operator runs (keys, OIDC, backups, storage,
releases, plugins, document sync) and about integrations that are off.
- Mapbox GL is offered only with the operator's token, Amap tiles only where
the operator set Amap up.
- The mobile admin no longer shows OIDC and the WebAuthn relying party fields,
matching the desktop tab.
For every install:
- Notification matrices list only events the install can raise: no plugin row
without the plugin runtime, no Vacay, collection, photo, Synology, wanderer or
packing rows while those are off. On a managed install the release and
replica alerts are left out.
- The journey entry editor offers External photos only where a photo provider
is switched on, as the phone already did.
- app-config is asked again after a failed answer. A page opened while the
server restarted kept every default for the session, which on a managed
install brought back the operator's settings.
- sharp 0.35.5 (librsvg CVE), with the musl pins that ship in the image.
- follow-redirects 1.16.2, postcss-selector-parser 6.1.5 and
serialize-javascript 7.1.3, from the open advisories.
- GitHub Actions: github-script 9 and the minor-and-patch group.
- plugin-sdk: @types/update-notifier 6.0.9.
The grouped npm update is left out: with it the server build runs out of
memory in tsc, and the culprit is not found yet.
- The npm minor-and-patch group (#2952) without mapbox-gl: 3.34 drops
setRTLTextPlugin from its typings, and the RTL plugin is what joins Arabic,
Hebrew and Persian labels. react and react-dom move to 19.3.1 together,
overrides included, so the workspace keeps one React.
- Security floors from the open alerts: sharp 0.35.5, vite 8.3.5,
follow-redirects 1.16.2 (#2927), postcss-selector-parser 6.1.5 (#2865) and
serialize-javascript 7.1.3.
- GitHub Actions: github-script 9 (#2951) and the minor-and-patch group (#2950).
- plugin-sdk: @types/update-notifier 6.0.9 (#2948).
An installation now asks for its TREK services key a few minutes after
it starts, and the daily check registers a missing key as well as
renewing a due one. Until now nothing happened before someone searched
for a place or opened the admin settings, so "Share place details"
stayed unavailable on a fresh or updated instance.
When an admin agrees to share place details, what users typed into
places since TREK 5.1.0 goes out too, not only what they type from then
on. The origin of every field is recorded since 5.1.0 whatever the
decision, so a catch-up walks those places page by page before each
send run: only values that are still the ones a user typed, only for
fields switched on, only for places the service can key, and only
while the queue holds fewer than 200 values. It goes on where it
stopped after a restart, never replaces a newer value a save queued,
and ends on switching off, declining or withdrawing everything. A field
switched on later catches up as well, under an agreement given with the
new prompt sentence that says so; an agreement from 5.1.0 does not.
Queuing moves into PlaceContribQueue so a save and the catch-up take
the same path.