- The location shim runs at high accuracy only while a watcher wants it and
drops back once that watcher is gone, instead of keeping the 2 s GPS rate
for the rest of the session.
- The purchase client can hand over a Play subscription that was paid but
never delivered (a pending payment that cleared later), and the plans screen
redeems it at once, as a restore would, before Google Play refunds it after
three days.
The Android app asks the server once whether Google Wallet works there and
offers the button only after it says yes; a 404 from the pass route takes the
button away again. The wallet button's test mocks the new exports and covers
the Google case.
The new methods sit at the end of the interface and the restore's Android
answer has a type of its own, so the watch and large-screen branches, which
add their methods after storeRestore, merge without a conflict.
The UI now turns these on by feature on Android as well as iOS:
- widgets, widgetTrips and the travel day (liveActivity) write the same
snapshot to the Android app's Glance widgets; the travel day also sends
the next stop's minutes and the stops' colours for the Live Update.
- geolocation answers navigator.geolocation from the app's location stream.
- tileCache: Android keeps Mapbox's resources in its WebView client, so the
map is offline-capable there too, without the iOS trek-tile:// rewrite.
- playBilling: the purchase client buys through Google Play on Android,
with the purchase token as the receipt the control plane verifies.
- googleWallet: a booking's button adds it to Google Wallet on Android,
through the save link the server asks the TREK services for.
- The claim screen says, right above the buy button, that the TREK Cloud
terms govern the instance and links them (liketrek.com/terms?app=1, the
view without the site's order buttons); the plan screen links them too,
next to Apple's EULA, which is now named as Apple's.
- On Android the payment note and the empty restore say Google account and
Google Play's own path, not Apple ID and App Store.
- The redeem sends the app's language, so the contract confirmation mail
arrives in it.
- Orders closed at the cloud: the app shows the plans and that TREK Cloud
opens soon, and sells only in a test build (TestFlight, App Review), whose
sandbox purchases the cloud still sets up; a store purchase it would refuse
is never offered. The plans arrive without prices then, the store's own are
shown where it gives them.
- A purchase paid but never set up (Ask to Buy approved later, the app closed
mid-purchase) is picked up by Restore purchases and goes on to the claim with
that purchase, instead of failing; nothing is bought twice.
- The buy button names the period with the price (guideline 3.1.2).
- The privacy link opens the policy without the site's sales header.
- The privacy manifest declares what TREK Cloud keeps: email, name, the store
purchase, photos, other user content and saved places, all for the app to
work, linked to the account, never for tracking.
- The email hint promises messages about the contract, not receipts, which the
store sends.
- TREK_WALLET_PASS_URL read as a secret to the env check (the word PASS); it
is TREK_WALLET_SIGNER_URL now, everywhere it is documented
- findPassContext compared the TEXT accommodation_id with an integer id, which
Postgres refuses; it compares them as numbers now, as both databases accept
- the wallet-pass service test takes its subject from createTestModule instead
of building it by hand, and no longer names the DOM-only BodyInit type
- Reservations.repository.ts is formatted, so it leaves the format baseline
- the wallet button test reads the translated texts the render helper gives
"Add to Apple Wallet" on a booking in the iOS app turns it into a TREK pass:
a boarding pass from and to for flights, trains, buses and boats, check-in and
check-out for a stay, pickup and drop-off for a car, the time for anything
else, with the confirmation code as a QR code. Wallet brings it up on the lock
screen around its time and place: times carry the offset of the endpoint's zone
(looked up from the coordinates where the endpoint has none), stays and
restaurants their place's coordinates. The labels come from the booking forms'
own strings, so nothing new to translate on the pass.
Only liketrek holds the Pass Type ID certificate, so the server sends the pass's
texts to the TREK services' signer (TREK_WALLET_PASS_URL, push.liketrek.com)
once per tap; it stores nothing, and TREK_SERVICES=off turns it off with the
other services. Texts are cleaned of links and shortened here, the answer must
be a .pkpass within 512 KB, and a user gets 20 passes a minute.
- New wallet-pass domain: GET /api/trips/:tripId/reservations/:id/wallet-pass
behind the trip guard, the booking checked to belong to the trip; trip title
and place come from the reservations domain (findPassContext)
- MWalletButton in the transport and booking sheets, only where the app binary
has the wallet feature; undated bookings and failures are told apart
- TREK_WALLET_PASS_URL in .env.example, compose, Helm and the wiki
Opening a .pkpass in the iOS app brought up the generic share sheet. It now
shows Wallet's own sheet with the pass and "Add", and a pass that is already in
Wallet opens there instead of being offered twice (TrekShell.addToWallet,
feature wallet). A broken pass or a device without Wallet falls back to the
share sheet as before.
Passes carry an "Apple Wallet" badge in the trip's files and a wallet icon
where they hang on a booking, so it is clear what a tap does.
A share extension puts TREK in the share sheet of Mail, Safari, Files, Wallet
and any app that shares a document, a link, text or a photo. It shows what came
in (a Wallet pass, a PDF, a mail, the page as Safari rendered it, text, a photo)
and the user's trips with their covers, the next one already picked, and hands
both to the app, which opens that trip's booking import with the file in place.
"Choose later" leaves the trip question to the app's import sheet as before.
- ShareExtension target (add-extension.rb learns "share": JavaScript and the
localizations go in as resources), bundle com.liketrek.trek.share, signed in
the release lane, its plists linted in native.yml
- Safari hands over the rendered page (SharePage.js), so a confirmation behind
a sign-in arrives whole; other apps' links are fetched
- The item travels through the shared keychain group the widgets already use;
the app takes it on com.liketrek.trek://share or when it next becomes active
- Feature shareSheet; strings in the app's 28 languages
The plan picker sent buyers to the website, which the App Store does not allow.
It now sells through StoreKit 2 in the TrekShell plugin (feature storeKit):
App Store price strings in the buyer's currency, the purchase sheet, the signed
transaction redeemed at the control plane, and the transaction finished only
once the instance exists. A purchase whose redeem failed is redeemed again on
retry, never bought twice.
Restore purchases, the auto-renewal terms and the links to the terms of use and
the privacy policy sit under the plans. Where no store purchase is possible
(Android, an older binary) the picker shows the plans with no way to buy and no
pointer to the web.
The trip PDF's print view linked Poppins from fonts.googleapis.com, so printing
a trip sent the reader's IP address to Google, the case German courts treated as
a GDPR breach (LG München I, 3 O 17493/20). It now declares the Poppins files
TREK already ships under /fonts, and the content security policy no longer
allows fonts.googleapis.com or fonts.gstatic.com, so no font can be fetched
from Google again by accident.
The mobile admin never read managedAdminTabs, so a tab an install attaches
there (managed/index.tsx) existed on the desktop only. The phone's section
picker now lists them after the built-in sections, as the desktop sidebar does,
and shows `mobileElement` when a tab brings a phone layout of its own, else its
desktop `element`. TREK itself attaches nothing; the seam stays empty.
MDemoWelcome used raw rem sizes, which ignore the text size setting and broke
theme:lint, and was missing from feature-pairs.json, which broke lint:pairs.
components/ may not import from mobile/ (lint:layers). The phone dashboard
mounts the sheet itself, as it already mounted the welcome, and the desktop
dialog no longer asks which shell it is in.
On a phone the head took half the screen, the list scrolled in a small window
and the start button broke over two lines next to the links. The phone now gets
a bottom sheet of the phone shell: a small head with the countdown, the same
content as a list, a full-width start button with the two links under it. The
photo credits open in its place and back, as on the desktop.
- The carousel and timeline tests step by the new 90 ms settle
- The journey search keeps its suggestion type through the fallback
- nearestCardIndex leaves useJourneyCarouselSync, which had grown past its
line limit; WidgetBag is not exported; the test baseline drops by the
non-null assertions a test no longer needs
The journal searched the full text only, which for a hotel in Osaka listed
the Japanese names the index holds. It now asks the planner's typed-ahead
search first (usePlaceSuggestions, the user's language, one billed session
per search), shows each place with where it is, and takes its point from the
details once picked; the full search still answers when there are no
suggestions. (#3003)
The track was w-10, which is rem and followed the text size setting, while
the knob and its travel are px; with a smaller text size the knob hung over
the end of the track. The track is 40 px now.
Widgets followed the dashboard's trip only, which is the one under way, so a
packing list or to-dos for a trip still being planned could not sit on the
home screen. The trip, today, bookings, to-dos, packing and group widgets now
take a trip in Edit Widget (an AppIntent with the trips running or ahead,
Automatic by default, which keeps following the dashboard); the world map
needs none.
- The app writes a snapshot per trip running or ahead (at most six, the
dashboard's order) and the list to pick from, under their own keys
(feature widgetTrips, so an older binary never mistakes one for the
dashboard's); bags, chat and polls are kept ten minutes, covers per trip
- The widget extension targets iOS 17, which configurable widgets need; the
apps are not released, so no installed widget goes away
- The choices and their texts in the 28 languages of the gallery
- The entry carousel draws the card at the centre larger while the swipe is
still moving (focusIndex, a transform), and the active card and the map
follow 90 ms after it stops instead of 150; the grow takes 200 ms
- The journey map's zoom buttons set their display inline, which beat the
class that hides them on touch screens; the display is a class now (#3001)
- The packing widget data no longer carries a colour literal
- Packing: a ring of what is in the bags, each bag against its weight limit,
the items still missing in their bag's colour, a nudge when departure is
two days or less away
- World map: the world as dots from the Atlas country shapes
(WorldGrid.swift, generated by WidgetPreviews/make-world-grid.py), the
visited countries glowing, the latest one ringed, continents as bars and
the latest countries with their flags; it needs no trip
- Travel group: who comes along as avatars, the latest chat messages as
bubbles and the open poll with its votes as bars
- The to-dos widget is about to-dos only; the packing ring moved out
The app sends the bags, the Atlas countries (an hour's cache) and the chat
and poll with the snapshot; all optional, as before.
- On a phone the shared journey's map stopped at the safe area, so Safari's
toolbar sat over a bare strip; it now runs to the edge (#3002)
- The shared journey on desktop gains the journal's day picker, every day and
its entries one click away (#2998)
With several trips running at once, the widgets took whichever came first
on the device, so an old test trip spanning today pushed out the journey
under way, and the today widget counted its days. The dashboard's choice
moves to utils/heroTrip.ts (running first, earliest start, else the next to
come) and the widgets ask the same function, so the home screen always
shows the trip the app opens on.
The active card grew its box from 136 to 164 px once the swipe had settled,
which moved every card after it and made the snap pull the strip a second
time. Every card now keeps the same box and the active one is drawn larger
with a transform, so nothing moves; the strips keep the room with a wider gap
and top padding. Where the browser reports scrollend, the card grows the
moment the snap lands instead of a beat later.
TestFlight builds now carry the release tag's message as What to Test.
Four widgets instead of one plain text block, drawn in the phone tokens
(warm paper by day, near black by night) with contour lines and a route arc
as the travel motif:
- Trip (small, medium, large, lock screen): the cover photo, a countdown ring
and a plane on its arc before the trip; the day of the trip as segments,
the weather and the next stops on frosted glass while it runs
- Today (medium, large): the day as a timeline in its category colours, with
a now line, the day's progress and the weather
- Bookings (small, medium, large, lock screen): flights, trains, hotels and
tables ahead as coloured tiles, the next one featured, status dots
- To-dos (small, medium, large, lock screen): to-dos and packing as nested
rings, the most pressing open to-dos with priority and due dates
The app now sends the destination, a small cover JPEG, the weather where the
trip is, the bookings ahead, the open to-dos and the packing count; every
new field is optional on both sides, so older binaries and servers keep
working. Timelines step every quarter hour so the now line moves without
the app. client/ios/WidgetPreviews renders every widget in every size, light
and dark, on the CI's Mac, uploaded as the widget-previews artifact.
- RTL: the measured pills and centred controls use logical forms or a marked line
- i18n: the two native widget numbers are positions, not counts (NOT_PLURAL)
- Types: styleOverride on the lazy GL map, the location and shared-file payloads,
the capabilities fallback, the transport sheet's stay metadata
- ESLint: latest-value refs move to layout effects, usePresence keeps the last
open state in state, the booking import opens clean with a shared document
kept in the list (the reset effect used to clear it again)
- Layers: the phone import sheet lives under mobile/ and reads through tripRepo;
the offline host check moves to utils/tileUrl
- Sizes: plugin layer geometry, hardNavigate and the exit animations get
modules of their own
- Tests follow the status radios, the stay chip overview, the dock + button
for the first list and the field token; store registry lists the new stores
Connecting the app to a server as someone not signed in there showed
"Server not reachable" with the server up. The UI's first API call answered
401, the client redirected with a full page load while the first page was
still loading, WebKit cancelled that load (-999), and Capacitor answers every
failed navigation, a replaced one included, with its error page: the shell's
unreachable screen.
The client now waits for the first page to finish loading before such a
redirect inside the apps, which helps the binaries already out; and the iOS
shell puts a small navigation delegate in front of Capacitor's that lets a
cancelled or interrupted navigation pass without the error page.
Docked sheets defaulted to the lighter bar glass, through which their header
buttons hardly stood out; every sheet now uses the same glass. The Vacay
people and legend sheets get the icon tile header.
Atlas bucket list, open route in, open in TREK, the place actions and the
MCP/OAuth client sheets now show the icon tile header (or, where a sheet
closes only by its buttons, the same title row without the close).
Admin sheets: their frame draws the icon tile header (an icon per sheet, the
plugin sheets' icons moved out of the title into the tile), the soft scroll
edge and 42 px footer buttons; admin fields use the sheets' eyebrow labels
and round inputs. The settings picker, the bulk category sheet and the save
to list sheet get the common header (TileHeader may now leave out the icon
for a plain picker). Every phone sheet now uses the glass material, as the
trip settings do; it stays 94 % opaque, so forms over the map remain
legible.
The shared confirm sheet (44 places) gets the icon tile with the title, a
warning icon for destructive questions, and the sheets' 42 px buttons with
the danger colour on a destructive confirm. The dashboard's and the
collections' own confirm dialogs now use it.
Vacay invite, shared calendars and settings get the icon tile header; the
settings' rows separated by hairlines become cards on the inner surface, as
the trip settings' sections.
Both get the icon tile header (the entry keeps its move buttons between
title and close), glass, the soft scroll edge and the sheets' 42 px footer
buttons; the journey name is a round field, delete in the entry's footer a
round icon button. The collection share sheet's invite button is round like
the trip settings' one.
The collections sheets had a header of their own with a hairline, white
square fields and short buttons. Their kit now draws the icon tile header
every phone sheet has (an icon per sheet), round 42 px fields on the field
surface and 42 px footer buttons; the sheets sit on glass with the soft
scroll edge. New list and add place lose their last own field styles: the
labels are the sheets' eyebrows, the search fields, the category picker and
the status choices are round fields of the same height.
The pending / confirmed toggle was shorter than the booking code field next
to it. It is now a 42 px framed field with the status colour as a dot in
each option, marked up as a radio group, and sits under the code at full
width, where both words fit.
New trip: the icon tile header, sections with labels above round fields
instead of floating labels in boxes, the round date fields, the shared
footer buttons, and no box within a box for the reminder and the cover
search.
The shared select, date, time and airport pickers style themselves inline
for the desktop and looked out of place in phone sheets, white and
squarer than the fields beside them. Inside the phone UI they now take the
sheets' 42 px round field on the field surface; the desktop is unchanged.
A tour tapped in the plan opened the desktop detail dialog in a hand-built
overlay: no slide, no drag to close, the desktop's frame on a phone. The
dialog now renders into the frame it is given; on the phone that is a sheet
like every other detail (MTourDetailShell), with the tour's name as its
title, its whole content and every action, the back gesture and an exit
animation.
Tapping the accommodation in the plan went straight to the stay's editor,
where travellers only wanted to look it up. It now opens the overview of the
booking behind it, as on the desktop, with check-in, check-out and nights as
tiles (one shared stayStats for both), and only a stay without a booking or
a place still leads to the editor.
Edit in that overview opened the transport editor for every booking, a stay
or a restaurant table included; non-transport bookings now open the booking
editor, as in the Bookings tab.
A stay with two linked bookings showed its chips twice in the day header,
because the list repeats a stay per booking; the header counts it once.
The shared form fields are round like the trip settings' fields (multi-line
ones keep a softer radius), and the shared footer's cancel, submit and
delete are the sheets' 42 px buttons. Every trip form built on these parts
(place, transport, booking, cost, note, packing, tasks and more) follows.
The journey settings sheet looked like a different app next to the trip
settings. It now uses the same parts: the glass card with the icon tile
header and the journey's name under the title, sections as inner cards with
a title, count badge and hint, 42 px round fields and buttons, the shared
link field, and switch rows for tracks, entry fields and the public link.
Those parts (SheetSection and the SHEET_* button and field classes) moved
into MTripSheetUi for every sheet to use.
Fields and buttons sitting directly in a column no longer shrink below
their height; name, subtitle and the archive button were squeezed flat.
Collections: the empty screen's own New list button is gone; the dock's +
starts the first list while there is none and adds places once there is one.
Trip settings: counts sit in a badge next to the section title instead of
reading as part of it (ACCESS 1), and every round button of the sheet,
invite and add guest as well as the member and guest actions, is one 42 px
circle; the icon-only buttons were wider than tall.
Journey settings: the public link is the same field as the trip's share and
invite links, through one shared MLinkField; the shared map's extras hang
under the map row with a guide line and smaller labels; removing the link is
a full-size button.