Commit Graph

4845 Commits

Author SHA1 Message Date
Maurice b15dcd9730 fix(mobile): place details card in the phone form's style
The card gets a header with its icon in a round badge, its section labels use
the eyebrow of the form's own fields below it, and the description sits on
the mobile field surface with a pill button to take its text over.
2026-11-06 15:45:54 +01:00
Maurice 821f23aed4 fix(mobile): compact place details, softer sheet edges, icon buttons for copy and share
Add place on the phone: the place details showed the mascot half a screen
high while nothing was picked or nothing was found, and pushed the name field
below the fold. Both states are now one quiet line, as in the desktop dialog,
and the details sit in a card in the mobile tokens.

Sheets with a header over a scroll box (trip settings, day, export,
transport, files and more) cut their content off in a hard line right under
the header icon. The scroll box now keeps a little room under the header and
fades its content out at the edges.

Copy and share next to a share or invite link are round icon buttons of the
same size; the share button showed no icon because the copy button's padding
left it no room.
2026-11-06 15:44:07 +01:00
Maurice 79420cd945 revert(mobile): keep the page transitions' timing and drop the unused route preload
The previous commit carried, by mistake, shorter page transitions and a
route preloader that was never wired up. Both go back to how they were;
the warm-up and day rail fixes stay.
2026-11-06 15:38:15 +01:00
Maurice e3541535e2 fix(native): first visits no longer wait behind the offline warm-up, day rail follows the swipe
The iOS app pulls every file of a build into WebKit's cache so it can start
offline. It did so four at a time at normal priority, for all 1,900 files of
every new build including all 27 translations, and a page opened meanwhile
queued behind it: right after a server update, a first visit to Vacay took
five to seven seconds. The warm-up now runs two at a time at low priority,
takes only the user's language and English, and walks a build once.

The day rail in the trip header lit the new day only after the pager's snap
had landed and the plan had re-rendered, a second or two after the swipe.
The pager now reports the day a swipe heads for as soon as its page is the
nearer one, and the rail and its pill move with the finger; the plan still
switches when the snap lands.
2026-11-06 15:37:55 +01:00
Maurice 431cc226e3 feat(mobile): pull-to-refresh on the journal and notification lists
The journal list (its own scroll box, so the hook now takes an optional
scroller) and the notifications screen reload by pulling, like the
dashboard. The list stays on screen while it reloads.
v5.1.0-tf.7
2026-11-06 15:02:55 +01:00
Maurice 6835fd7bf3 feat(mobile): page transitions, pull-to-refresh, share sheet and haptics
Page transitions: on the phone, pages move like a native navigation stack,
built on the View Transitions API (mobile/routeTransition.ts). A page opened
from another slides in from the trailing edge, back slides it away, and
switching between the dock's sections cross-fades while the dock stands
still. Lazy routes do not flash their skeleton: the swap waits, behind the
frozen snapshot, until the new page rendered. The edge-swipe back gesture
already moves the page and skips the transition. RTL mirrors the motion,
reduced motion and desktop get none.

Pull-to-refresh on the mobile dashboard: the page follows the finger with
resistance, ticks once past the threshold and re-reads trips and
notifications quietly, without a loading state.

Share: trip share and invite links and the journey link get a share button
next to copy, opening the system share sheet (Capacitor Share in the apps,
Web Share in browsers that have it).

Haptics: a selection click for switches and segmented controls, a tick when
a sheet is flung closed, and a firmer bump for the chat's long press.

Theme lint: this branch's new screens use the type and z-index tokens, the
Vacay company tint is a token, and the baseline is lowered to the new
counts.
2026-11-06 15:02:55 +01:00
Maurice 3c4c880c31 feat(native): push notifications on Android through the relay
The Android app now subscribes like the iOS one: TrekShell.registerPush asks
for the notification permission (Android 13+), creates the device's P-256
Web Push keys and returns the relay endpoint for its FCM token
(push.liketrek.com/v1/fcm/<token>). TrekPushService receives the relay's
data message, decrypts the record (RFC 8291, TrekWebPush) and shows the
notification. A tap opens the linked page, also when it starts the app.

The keys are sealed with an AES key kept in the Android Keystore. Firebase
is configured from res/values/firebase.xml, so no google-services plugin is
needed. The decryption is tested against the RFC 8291 example, and the
native workflow now runs the Android unit tests. On the web side push is no
longer limited to iOS: it follows the nativePush capability on both platforms.
2026-11-06 15:02:55 +01:00
Maurice f35b189ca2 feat(native): home-screen widget and travel-day Live Activity on iOS
A WidgetKit extension (TREKWidgets) with two parts: the next trip on the home
screen, with a countdown before it starts and today's day and next stops
while it runs, and a Live Activity for the travel day on the lock screen and
in the Dynamic Island showing the next timed stop and the day's progress.

The web app builds both from the trips stored offline on the device
(native/widgets.ts) and hands them over through TrekShell
(setWidgetSnapshot, syncTripDay, endTripDay). The texts are already
translated, so the widget needs no localisation of its own. The snapshot
lives in the keychain group the app shares with its extensions, so no App
Group is needed. Taps open com.liketrek.trek://navigate/<path>.

NativeWidgetsSync refreshes on start, on returning to the foreground and every
quarter hour. Both parts are gated behind the widgets / liveActivity
capabilities, so older binaries and the PWA are unaffected.
2026-11-06 15:02:55 +01:00
Maurice c6f91716a4 feat(native): a notification extension that decrypts TREK pushes on the device
The relay hands APNs the encrypted record with a placeholder text and
mutable-content set; the new Notification Service Extension opens it with
the device's Web Push keys from the shared keychain group (RFC 8291 on
CryptoKit) and shows the real title, text and link. Without the keys the
placeholder stays, so a notification arrives either way and nothing on the
way reads it.

The target came in through ios/scripts/add-extension.rb (kept, idempotent),
its App ID is registered, and the release lane signs every target with its
own App Store profile. Web Push payloads now stop at 2800 bytes so the
encrypted, base64-encoded record still fits Apple's 4 KB.
2026-11-06 15:02:54 +01:00
Maurice ad69bbf4a9 feat(native): push inside the iOS app, through an encrypted relay
The app subscribes to Web Push like a browser, only the app makes the
subscription: it asks for permission, registers with APNs and keeps its own
P-256 key pair and auth secret in a keychain group a notification extension
can share. The endpoint is liketrek's relay for this device's token, so the
server encrypts exactly as for a browser and nobody on the way can read the
notification. The settings card, the test push, sign-in resync and the logout
teardown all take this path where the binary has nativePush; a tapped
notification opens the place it points at, also from a cold start.

The App ID has push enabled, the app carries the aps-environment and
keychain-group entitlements.
2026-11-06 15:02:54 +01:00
Maurice 30158cddc3 feat(push): accept the TREK apps' push relay as a push service
The apps subscribe to Web Push like a browser, with their own key pair, but
with an endpoint on liketrek's relay: only liketrek holds the apps' APNs and
FCM keys. The server encrypts exactly as for a browser (RFC 8291), so the
relay hands on ciphertext it cannot read and the device decrypts it. Lookalike
hosts stay refused.
2026-11-06 15:02:54 +01:00
Maurice 825469a7f2 feat(native): share a booking document to TREK and import it into a trip
TREK now appears under "Open in" and in the share sheet for PDFs, calendar
entries, Wallet passes and mails (iOS document types, Android VIEW and SEND
filters). The native side reads the file (up to 15 MB) and hands it to the UI,
which asks which trip it belongs to, upcoming trips first, then opens that
trip's booking import with the file already chosen.
2026-11-06 15:02:53 +01:00
Maurice 56f9a92054 feat(native): the app's own location on iOS, and a status bar tap scrolls to top
WKWebView asks for the location per server origin, under the server's name,
and again across launches. Binaries with the geolocation feature answer
navigator.geolocation from CoreLocation instead: one When-In-Use prompt as
the app, a stream that runs only while something listens, at the accuracy
the most demanding caller asked for. Every caller keeps the browser API.

A tap on the status bar now scrolls the list in the middle of the screen
back to the top, as in every iOS app.
2026-11-06 15:02:53 +01:00
Maurice 017f229cfc feat(native): an app lock with Face ID, Touch ID or the device passcode
A switch in the account settings, inside the app on a device that can unlock
that way (iOS LocalAuthentication, Android BiometricPrompt with the screen
lock as fallback). Turning it on asks once, so nobody locks themselves out.
With it on, TREK asks at launch and after half a minute in the background
before showing anything, and the app switcher shows a blurred cover (iOS) or
no snapshot (Android) instead of the screen. The setting stays on the
device, not the account.
2026-11-06 15:02:53 +01:00
Maurice dec6babe1d feat(native): an iOS map tile store, so viewed maps keep working offline
WKWebView runs no service worker for a self-hosted server, so on iOS nothing
kept a map tile once it scrolled away. Binaries with the tileCache feature
answer a trek-tile:// scheme from a store on disk: a tile viewed once is
served from there for up to 30 days (Mapbox's limit for a device cache,
applied to every provider), then fetched anew; the store keeps under 250 MB,
dropping the least recently used tiles first, sends a unique app User-Agent,
and never prefetches. Mapbox's own resources (style, tiles, glyphs, sprites)
and Leaflet's raster tiles go through it, telemetry never does.

Offline, a Mapbox map in such an app now stays Mapbox where it was viewed,
and the pill offers the stored trip region instead. The offline settings
show what the store takes against its limit, with a way to empty it.
2026-11-06 15:02:53 +01:00
Maurice d859e23879 feat(native): the app's own swipe back, and a way back closes the sheet on top first
WebKit's swipe from the left edge slid away a screenshot of the whole page,
closed no sheet and took every drag near the edge from the UI. Binaries from
this one turn it off and report an edgeBack feature, and the UI draws its own:
the page follows the finger, a release past a third or a flick slides it off
and goes back, anything else springs it home, with a light detent.

Open sheets now register in one overlay stack, so a way back closes the one on
top first, through its own close path (unsaved changes still ask): the edge
swipe on iOS and Android's back gesture alike.
2026-11-06 15:02:52 +01:00
Maurice f92aea80a7 feat(native): a capability handshake between the app binary and the server's UI
The UI ships with the server and the binary through the stores, so the two
drift. TrekShell.getCapabilities now reports the binary's shell API, version
and native features by name (iOS and Android), and the UI turns each native
feature on only where the binary has it; a binary from before the handshake
counts as shell API 1 with none. The other way round, the server names the
oldest binary its UI works with (minNativeShellApi), and an app below it is
asked to update with a link to its store.
2026-11-06 15:02:52 +01:00
Maurice 7b29a5d34e feat(mobile): Vacay's month picker and month heads in glass
The editor's twelve month chips were twelve grey boxes; they sit on one glass
surface now, the picked month under a pill that glides to it across both
rows. The month heads in the year overview are glass pills too.
2026-11-06 15:02:51 +01:00
Maurice c7a369a0ca feat(mobile): Vacay's people and legend pills share the row equally 2026-11-06 15:02:51 +01:00
Maurice 117af0f9b1 feat(mobile): swipe through Vacay's months and years, people and legend in sheets
In the editor the months of the leave year page like the trip's days, the
neighbour sliding in with the finger; in the overview the plan's years do
the same (only between years the plan has, the arrows still create one).
The day pager now pages anything with an id and can sit in a page's flow.

The row of person and legend chips is two compact pills now: People opens a
sheet to pick the member and show or hide shared calendars, Legend opens one
that explains the company days and holiday calendars, which were never a
choice.
2026-11-06 15:02:51 +01:00
Maurice 496c85e6e5 feat(mobile): one shape for the atlas header buttons
Planned was a label with a switch in a pill of its own beside the full-width
wishlist button, and with Dawarich on the row grew cramped. Wishlist,
Dawarich and Planned are now buttons of one shape, equal width and the phone
shell's glass; Planned fills while the planned countries show.
2026-11-06 15:02:51 +01:00
Maurice 1c15c33cf9 chore(i18n): drop the strings the removed map and today buttons used
The jump-to-today button and the Google Maps and CoMaps tiles are gone, so
their strings reached nothing and would fail the i18n key check.
2026-11-06 15:02:50 +01:00
Maurice 50bd1bcbc1 feat(ui): a calm, native-looking card when a view fails or TREK was updated
The error and new-version fallback was a bordered box with a warning
triangle and the raw error message on show. It is now one centred card with
a round symbol (a friendly one for an update), a short title and line, and a
full-width reload button; the technical message stays for issue reports,
folded under Details. Phone, PWA and desktop alike.
2026-11-06 15:02:50 +01:00
Maurice d2ae9a87c4 fix(mobile): the trip filter fits beside its buttons again
The German Completed filter (Abgeschlossen) made the dashboard's filter pill
wider than its row, so it scrolled inside its box and showed cut off. It is
now Beendet, the help texts naming the filter follow, and the intrinsic
segments take a little less padding.
2026-11-06 15:02:50 +01:00
Maurice c5cb39c27b feat(mobile): one Maps button for every map app, actions as one glass group
The day plan offered Google Maps and CoMaps as two tiles. One Maps button now
opens the day in whichever app the traveller picks: Google Maps, Apple Maps
with the stops between as waypoints, CoMaps, Waze and on Android any other
map app, the last two to the first stop, which they say. The preferred map
app from the settings opens straight away.

The day's actions sit together on one glass surface as round icons over
their labels, and the up-next card wears the same surface as the timeline
card under it.
2026-11-06 15:02:49 +01:00
Maurice 6d093d1102 feat(mobile): one glass language across the day plan's controls
- The rename and reorder controls beside the day's name, and the day and
  weather chips on the timeline card, are now round or pill glass controls of
  one height, so they read as tappable; the stay chips follow.
- A leg between two stops is one pill, as on the desktop: the mode, the time
  in full weight and the distance beside it, larger and without a dot.
- The timeline flows under the up-next card at a fixed 12px, whatever its
  height, instead of an offset that left a gap when the stop has no address.
- The places bar on the map wears the day rail's glass at its 36px height.
- A context menu stays inside all four edges of the screen, measured at its
  real size rather than mid-animation, and scrolls when taller than it.
2026-11-06 15:02:49 +01:00
Maurice 01ddcc63bb feat(mobile): two calm round controls a side on the map, no extra buttons
The phone map stacked four controls on the right and one on the left, in
three different looks. Now each side has two at most, all in one frosted
style, 20px off the edge and 8px apart: the whole-trip overview and locate on
the right, the base layer and the Dawarich route on the left, with the compass
appearing only while the map is turned off north.

The map's own places filter button and sheet are gone: the places tab carries
the same filter, and the map's pins follow it. The jump-to-today button beside
the day rail is gone too, since today's chip is already marked.

Also fixes a leftover of the segmented-control change that broke the parse.
2026-11-06 15:02:49 +01:00
Maurice b07b38b5b7 feat(map): never leave an offline map grey, whatever the basemap
Offline, a Mapbox map, another OpenFreeMap style or a raster template the
device cannot serve now switches to the stored OpenFreeMap basemap, light or
dark with the colour scheme, keeping every marker, route and track, with a
small Offline map pill. It switches back once the connection returns.

"Prepare for offline" stores that basemap's region for every trip, whatever
the provider, and both styles' sprites and glyphs, including the scripts a
trip in East or South Asia labels its places in. The vector prefetch keeps to
OpenFreeMap's download rules (four requests at a time, 50 MB per run), raster
tiles are no longer prefetched from OpenStreetMap's own servers, which their
policy forbids, nor inside the iOS app, which has no service worker to serve
them.
2026-11-06 15:02:48 +01:00
Maurice 83f0bd2ead feat(mobile): pull sheets from anywhere with a mouse too, glide the active day
A pull on a sheet's content only answered touch, so with a mouse the sheet
could only be dragged by its top edge, and a pull that started on a button
read as a press. Mouse and pen now pull the content like a finger, a pull
that starts on a button swallows its click, and text fields keep their own
gesture.

The active day chip and the segmented controls now glide one shared pill to
the new item on the compositor, instead of repainting two buttons.
2026-11-06 15:02:48 +01:00
Maurice 7293ee1174 feat(mobile): sheets that drag, fling and rise like native ones
The docked sheet could only be pulled by an invisible 20px strip, re-rendered
React on every finger move, and a flick sprang back to the top before fading
out. A finger on the content now carries the sheet once the content is at its
top, the panel moves through direct style writes, and a dismissing release
throws it off screen from where it was let go, faster the harder the flick.
With unsaved changes it springs back and asks instead. Pulling up only
stretches a little.

The docked sheet now rises from below the screen edge and sinks back there,
and the floating card pops in place the way a native alert does.
2026-11-06 15:02:48 +01:00
Maurice 25351c3628 feat(mobile): page through the days like a native pager
The day swipe only rubber-banded the current day, then cut to the next one
after a fixed slide and a synchronous re-render of the whole planner. The
previous, current and next day now sit side by side in a horizontal
scroll-snap strip, so the finger drags the neighbour in, and momentum,
edge bounce and axis locking are the browser's own. The day changes once
the strip comes to rest, with a light haptic tick in the app.

The pager is one building block, MDayPager, used by the plan timeline and
the road trip stages alike. Each page renders its own day through the
timeline and stage hooks, which now take the day as a parameter, and keeps
its state when it slides into the middle.
2026-11-06 15:02:48 +01:00
Maurice d18fb7f63d fix(native): drop the unused yearly-savings string and a dead initial value
The plan picker no longer shows a yearly badge, so its string reached nothing
and failed the i18n key check; the shell's start also initialised a value it
always overwrote.
2026-11-06 15:02:47 +01:00
Maurice 1054f735ad i18n(native): carry the new onboarding wording into every language
The welcome headline and subtext, the demo button, the have-a-server link,
the unlimited-plan line and the claim title changed in en and de with the
redesign. All other locales now say the same, and the English headline
reads Your Trips to match the German plural.
2026-11-06 15:02:47 +01:00
Maurice 954d419671 feat(native): show the photo and a visible grid on the light welcome screen
The light scheme dropped the Kyoto band and drew the grid so faint it
vanished, leaving a bare white page. The photo now stays in both schemes
under a veil in the page colour (a dark dusk, or a bright haze in light
mode so the black wordmark still reads), and the light grid and wash are
strong enough to see.
2026-11-06 15:02:47 +01:00
Maurice 2077b9e375 ci(ios): import one stored distribution certificate instead of minting one per run 2026-11-06 15:02:46 +01:00
Maurice 5135052446 feat(native): fix the claim fields into one address box, add placeholders, shorter fields, better title 2026-11-06 15:02:46 +01:00
Maurice f9e1dfe804 feat(native): equal plan cards, mark absent features with a dash, one-line unlimited label 2026-11-06 15:02:46 +01:00
Maurice bd42661a62 fix(native): more space between price and its per-period suffix 2026-11-06 15:02:46 +01:00
Maurice 3b4d220936 feat(native): sample plans in dev preview, drop the yearly badge, plain white primary, readable subtext 2026-11-06 15:02:46 +01:00
Maurice e3ac025a59 feat(native): drop the demo button from the connect screen 2026-11-06 15:02:45 +01:00
Maurice c04864f693 feat(native): TREK Cloud wordmark lockup on the cloud screen, fix a German string 2026-11-06 15:02:45 +01:00
Maurice a9b05bba15 feat(native): make TREK Cloud the primary action, shorter calmer subtext 2026-11-06 15:02:45 +01:00
Maurice 6ce19eb4a8 feat(native): a welcome subtext that shows the whole TREK ecosystem, hosting-neutral 2026-11-06 15:02:44 +01:00
Maurice 52ddf9b4f7 feat(native): blend the photo into the grid left-to-right, slimmer demo button 2026-11-06 15:02:44 +01:00
Maurice 61a7b5bceb feat(native): set the welcome headline in lowercase MuseoModerno, full-width demo button 2026-11-06 15:02:44 +01:00
Maurice 70cfe9bfb0 feat(native): Kyoto hero band, headline tagline, tidy demo and footer 2026-11-06 15:02:44 +01:00
Maurice b63f1f01b5 feat(native): use the full TREK wordmark on the welcome screen, per scheme 2026-11-06 15:02:43 +01:00
Maurice e9d9690844 feat(native): richer onboarding — Poppins display type, a masked grid backdrop and depth 2026-11-06 15:02:43 +01:00
Maurice b1a31d4d68 ci(ios): create a passworded keychain so the distribution cert is usable for signing 2026-11-06 15:02:43 +01:00
Maurice 5cb33b2858 ci(ios): sign the release with fastlane so TestFlight uploads from a device-less runner 2026-11-06 15:02:43 +01:00