The tour detail gets a Maps button beside Navigation that hands the whole route
to the apps that follow one, in the tour's own mode (walking or cycling). The
stops are spread evenly along the line and capped at ten, the most a Google
Maps directions link opens. Ski, paddle and horse tours are not offered, since
a map app would route them by road; the GPX export stays the way for those.
On the road a stop's picture is often what decides whether it is worth the
detour. The phone's stop sheet shows the place's picture full width under its
name and the desktop stop dialog shows it in place of the kind tile; both open
the place's photos on tap. The picture is the place's own image, else the
provider photo for its Google or OSM id through the shared photo cache.
The journey map gets the planner's round satellite switch in its top corner,
on the desktop aside, the phone views and the public page. Leaflet takes the
vector basemap off while the imagery shows and reattaches it on the way back;
the GL renderer slides the imagery under the journey's own layers. The choice
is per view and opens on the map, so no setting is read or written.
A shared Immich or Synology album can hold hundreds of photos from other
trips. Once an album is open the picker now offers a from/to range that
filters the already loaded assets by the day the grid files them under, so
Select all only takes the photos in range. No server change.
The accept dialog opened on "not on a day yet" although the stay carries its
local date. It now picks the trip day whose date matches, on open and when the
trip is switched, and falls back to no day when the trip has none that matches.
Google Maps takes no list from outside, but My Maps imports a KML layer.
The trip export dialog and the phone export sheet get "Places for Google
My Maps" (GET places/export.kml, MCP export_trip_kml), and a list's export
menu gets KML next to GPX. The KML codec gains a pin writer; the list file
mapping moves to collections.helpers to keep the service within its size.
A read tool that answers one list a page at a time (100 by default, up to
500) as compact one-line JSON rows, filtered by status, label or "no label",
with the list's labels, the matching total and a cursor. The cursor is the
last place's position (sort_order, created_at, id), so labelling a page
while paging through unlabelled places skips none. getCollection orders by
the same keys, id last, so both tools agree on ties.
The update contract takes google_place_id, google_ftid, osm_id, website,
phone, price and currency like saving does, website with the same
normalisation and refusal. REST and update_collection_place share it, so an
agent can correct a place instead of re-saving it and losing its labels,
ratings, position and id. The assistant's change card names the new fields.
A fourth status after visited, for a place already seen that the user wants
to go back to. It joins the one-tap cycle, the filters, the pickers, the
assistant cards and the MCP status tool, and counts as visited wherever
visits are counted (isVisitedCollectionStatus), so "visited everywhere"
keeps it. The status pickers go two by two where four would not fit.
The phone toolbar gets a category chip beside the status and label chips,
fed by the same categoryOptions the desktop dropdown uses, and no longer
clears a category picked on desktop. Managing labels moves into the label
panel so the row keeps four chips at phone width.
Ctrl/Cmd+Enter in any field of a DialogShell presses its one primary footer
button, focusing it first so blur-committed fields keep their value. Dialogs
with no or several primaries leave the keys to the field, as does the journal
story editor, which opts out with data-keeps-ctrl-enter.
The print engine embedded every place photo at full resolution, so a trip of a
few hundred stops saved to a PDF of tens of megabytes. Same-origin photos are now
fetched six at a time, scaled to a 156 px short side and inlined as JPEG data
URLs; photos from other origins, failures and an aborted export keep the URL.
A third switch next to the page break and transport notes switches scales the
day content's type by 1.35 and darkens its greys, for readers who print the
plan. Off by default and remembered per browser like the other two; the day
content's sizes and greys now come from three CSS variables on <body>.
Mapbox Standard labels shops, restaurants and sights of its own; they look like trip
places but cannot be picked. A Hide Points of Interest switch next to the other Mapbox
options, on the desktop and the phone, turns them off on the trip map and the preview
without a rebuild. The Standard config handling moves into useStandardBasemapConfig.
A place saved with a typed address and no search pick, link or map point gets no
coordinates, so it never shows on the map, and nothing said why. The place form warns
under the address while that is so, and the inspector, the phone's place sheet and
both place lists mark such a place. No geocoding on save; the form stays unchanged
for a service stop, which explains a missing position itself.
On a landscape phone or a small tablet the inspector covered most of the map, and the
only way to clear it was closing it, which deselected the place. A chevron in the head
now folds the card to the avatar and name; the next place opens in full. The head band
moves to PlaceInspectorHead so the inspector stays inside its size budget.
The trip form was mounted in the planner but nothing opened it, so title, dates,
cover and currency could only be changed from My Trips. Trip settings now lists
Edit Trip at the foot of its areas for anyone with trip_edit; it swaps the dialog
for the trip form. The phone already has Edit Trip in the More sheet.
The category picker had no icon to tell a car park or a terminal apart from the
places a trip visits. Five transport icons join the shared table, which the admin
pickers, map pins and the PDF all read; the ship's tooltip now also says ferry.
Costs always converted to the display currency from Settings, so a trip
that moves on to another country could not be read in its new local
currency. A picker in the Costs bar (beside the Expenses heading on the
phone) now reads one trip in any currency; the server already settles in
any base. The pick is per trip and kept in this browser, nothing stored.
Seeing what a trip cost without, say, food meant adding it up by hand. A
click (or tap) on a category in the breakdown now leaves it out of the
total, your share and what you paid, dimmed in place with a line naming it
and Count all to undo. View only: nothing is saved, and what is owed, open
or settled stays whole. The desktop breakdown moves into its own component.
Travellers who track what a trip costs but never split it saw an
Unfinished warning on every expense and a settle-up they had no use for.
A trip now has a costs mode (appended migration, trips.costs_mode, split
by default); totals leaves payers, shares and settling up out of Costs on
desktop and phone through the same gate as a trip of one, and saves new
expenses without a payer. PUT /api/trips/:id and update_trip take it on
budget_edit. The split chips move into their own component.
With one traveler every share is the whole amount, yet Costs showed owe,
owed, outstanding, settle-up, balances and the final budget. A shared gate
(showsSplitting) now hides that side on desktop and phone, unless the ledger
still names someone else; the expense editor drops who paid and the split
while the expense holds nothing more. The payment dialog moves into its own
file to keep the panel under the size ratchet.
A booking confirmation that is also the invoice had to be uploaded a second
time to sit on the expense. The expense editor (desktop and phone) now lists
the trip's files to link, the booking's own first; the save sends the id in
receipt_file_ids, which the server already links. The desktop receipts block
moves into its own component.
A stay shared by people who slept a different number of nights had to be
worked out by hand. The custom split now takes shares next to amounts and
percent: five nights against four split the total 5/9 to 4/9, cent-exact by
largest remainder. Only the amounts are saved, so server and MCP are
unchanged; the phone sheet gains the same unit switch, percent included.
A new expense opened in the display currency, so every bill on the road
needed the trip currency picked from the full list. It now starts in the
trip's own currency (a scanned receipt keeps the one it read), and the
trip and display currencies lead the picker on desktop and phone.
The home assistant sat behind the More button on every page outside a
trip, so a dock with free slots showed "..." for a single entry. It is now
an entry of the dock list while its addon is on and it can run for the
user: an account that never sorted its dock gets it in a slot the addons
leave free, and Settings > Appearance > Mobile can move it between the bar
and More like any page.
A widget or Live Activity tap reached the UI as Capacitor's appUrlOpen, or
as the launch URL. When iOS had ended the web view's process in the
background, the event went to the old page's listeners and was lost, and
the reloaded page found the same link in getLaunchUrl that it had already
followed once and skipped it: the second tap on the packing widget opened
the start page.
The binary now takes com.liketrek.trek://navigate links itself and hands
them over like a quick action, held until the UI takes it and kept for a
page that is still loading. It reports the feature appLinks, and the UI
only follows the link through Capacitor for binaries without it.
"Obidos" did not find "Óbidos", nor "Sao Bras" "São Brás". The search of the
places list, a collection, the saved-place picker, the import from a trip
and the location picker now compare without case and Latin accents, with
letters such as ß, ø or ł folded to their plain spelling. Marks outside Latin
script stay, since there they change the word.
The place form guessed the category of a picked place from its kind, so a
place taken from a collection inside a trip lost the category it was saved
under, while adding it from the collection kept it. A picked place that
carries a category this server has now brings it along; a category chosen
in the form by hand still wins.
The server nests a saved place's category, while the markers and the hover
card read the flat fields a trip place carries, so every pin on a
collection's map was the grey default. The map now hands its places on with
the category's colour, icon and name where the markers look for them.
The check before a purchase read only the entitlements on the device. Those
can be empty while the subscription runs, so the app offered the plans and
the purchase changed the running subscription's plan in the App Store. The
status of the subscription group now decides when the device has none. A
renewal the device has not finished yet no longer counts as a purchase
waiting for its instance either, which also let the check through.
nativeApi goes to 3 with 5.1.4, whose UI follows a widget or Live Activity
tap that starts the app. The apps refuse an older server with the hint to
update, and check a server they remember once more.
Saving the AirTrail settings with a different URL and the key field left
masked or blank kept the stored key, so the next sync sent the old
instance's key to the new host. The key now goes with the host, as it
already did for the connection test and for Dawarich: a new origin without
a new key clears it, a path edit on the same origin keeps it. The key is
cleared before the new address is written.
A tap on a widget or a Live Activity hands the app
com.liketrek.trek://navigate/<path>. A running app gets it as appUrlOpen;
an app started by the tap gets it as its launch URL, which nothing read, so
the path was lost and the app opened its start page (the packing widget
landed on the trip's plan instead of its lists). The launch link is now
followed once per launch.
A flight created with a day and times but no endpoints (over MCP) showed
them in the form, but buildTransportPayload took a flight's span only from
rows with an airport, so a save sent day, end day and both times as null.
Like a train without stations, such a flight now takes its day, times,
airline, flight number and seat from the raw rows. Endpoints and legs still
come only from rows with an airport.
Fixes#3070
The edit dialog's component crossed the complexity limit with the section's
conditions; the section and its confirm step now live in SsoUnlinkSection,
which decides itself when it shows.
A sign-in the app starts runs through the server's handoff page in the
system browser. When the SSO callback failed (an expired or overwritten
state), the login page dropped the way back to that page, so the retry from
there was a plain web login and ended in the web app inside the app's
browser sheet instead of signing in the app. The login page now keeps a way
back that points at the handoff page, and only that, and renews its
fallback copy for a slow retry. State, nonce, PKCE and the handoff verifier
are unchanged; the handoff page still asks for a tap before it passes
anything on.
- The SSO unlink answers with a response contract (adminSsoUnlinkResponseSchema).
- It reuses linkOidcIdentity with nulls instead of a new repository method,
so UsersRepository keeps its method count and size.
- The client calls the route through apiClient, so api/client.ts does not
grow, and its confirm dialog takes no async callback.
- The pager's preview step and pagesFor keep their complexity.
A sign-in with another subject of the same provider is refused, so an
account whose provider re-issued the subject (a migrated or rebuilt IdP, a
changed sub format) was locked out: editing the email, as the wiki advised,
never touched the stored link. The edit dialog now offers "Remove SSO link"
for other accounts while SSO is set up (DELETE /api/admin/users/:id/sso,
audit admin.user_sso_unlink). The next SSO sign-in links again under the
rules of a first one, so it still needs a verified, not self-set email.
The log line names the remedy, and the wiki tells the two refusals apart.
Fixes#3065
Only the selected day and two on either side were drawn, and the selection
moves only once the strip comes to rest, which iOS takes a while for. A run
of fast swipes ran past the drawn pages into blank ones until it settled.
The drawn window now also follows the day the swipe is heading for, so the
next days are there before they slide in. At most two windows of five pages
are mounted, however long the trip.
Fixes#3067
The transport form re-seeded itself whenever the trip's costs arrived as a
new array, and the booking form whenever days, places or stays did. A
socket reconnect re-reads the whole trip, so behind a proxy that drops idle
sockets every 15 to 30 seconds everything typed so far was wiped. Both forms
now re-seed only for what opens a different form; the data is read when the
form is seeded, as before.
Fixes#3069
lint:i18n-keys took the spread in front of t() for a member call and did not
see native.provisioning.sub; the sentence is its own constant now. The
shell chrome test asks for the logical margins and their right-to-left swap.
- native-shell/amount.ts: the no-break spaces in the price pattern were
written out as characters; \s already matches them.
- widgets.ts: the Live Activity's lead time stays inside the module.
- i18n: the count strings of the new plan tiles replace the removed ones
on NOT_PLURAL, and the address placeholders of the non-Latin locales
are their own instead of English copies.
- mobile.css: the side safe-area margins of the floating bars use
margin-inline, swapped for right to left, instead of physical sides.
The screens the app ships itself (welcome, plans, address, own server and
the states between) move from the dark card layout to a light design on an
animated aerial landscape drawn by a WebGL shader (native-shell/scenery.ts):
an island, a coastal town, farmland and mountains in one world.
- Welcome: the landscape with the TREK wordmark, three perks in one card,
"Explore cloud plans" and "I already have a TREK" as two buttons.
- Plans: one tab per plan; choosing one flies the camera to its place,
further inland with every plan. Each plan reads as a kind of trip, the
price sits in a card with a monthly/yearly switch and counts to its new
amount in the store's own format (native-shell/amount.ts), and a yearly
price shows what it makes a month. Restore purchases, the renewal terms
and the links stay as the store rules require.
- Address: the address writes itself on a glass card over the landscape;
free, the password rules and the agreement sit on white cards.
- Own server: the server address on the glass card; a failed check turns
the card red with what went wrong, proxy sign-in and client certificate
panels in the same style.
- States: server not reachable, certificate needed, already subscribed,
setting up (with its steps, then "Arrived") and the slow setup.
- iPad, an unfolded phone and a phone on its side get two columns, the
landscape beside the content; on a dual-screen device each half takes
one screen. Arabic and Hebrew mirror through logical sides only.
- The shader caps the pixel ratio at 2, drops to a lower resolution on a
slow device, stops in the background and is one still frame with
reduced motion.
- The sign-in and error page bars over a proxy's page (iOS and Android)
take the same look: a white card and a dark button.
- Plus Jakarta Sans is bundled for the text, with Montserrat standing in
for Cyrillic; the wordmark fonts and the old preview images are gone.
- New strings in all 28 languages; the keys of the old screens removed.
- The dev sample plans follow the cloud's plans (start, basis, plus, pro).
The Live Activity started with the first sync of a travel day and stayed on
the lock screen all day, so a day whose first stop is an evening flight held
it from the early morning on. It now appears three hours before the day's
first timed stop and stays until the day is done; liveDayDue() decides.
A load that failed sent the app to the address screen with a general message only, and a tester's screenshot told nothing. The web view's own error (net::ERR_..., an HTTP status, NSURLErrorDomain and its code, a client certificate the proxy asked for) is kept natively and shown under the message once.