Commit Graph

611 Commits

Author SHA1 Message Date
Maurice b7a91fc84e fix(mobile): one form sheet for every dialog, opaque and placed beside the rail
Every sheet now stands on the opaque sheet surface: the frosting the
translucent one relied on is dropped while the scrim fades, so the page
read through the fields as sharp ghost text.

On a wide window a sheet is centred in the content beside the navigation
rail instead of on the whole window, so it never lies on the rail, and it
grows to the window's height instead of stopping at 880px. On a phone the
card stands between the status bar and the dock rather than across the
dock. A sheet marked as a form fills the height of a window as short as a
phone on its side, with Cancel and Save beside the title (new `short:`
variant, m-form-sheet grid in mobile.css).

The form chrome gains a scrolling body that fades at the edges with more
behind them (useEdgeFade on the vertical axis), a date field whose
keyboard entry moves into the picker, a field wrapper for amounts with a
currency, and two-line titles. The focused sheet no longer draws the
app-wide focus frame or loses its corners, and the desktop's dark field
paint stays out of the touch shell, which painted a dark box inside every
rounded field.
2026-11-07 23:25:14 +01:00
Maurice b644235eb6 fix(mobile): level card columns, the day framed beside the plan, the reader's place kept across a fold
From a screen by screen pass over the iPad and foldable sizes:

- Card lists (transports, bookings, tasks, polls) flow down their columns
  instead of filling rows, so the columns start level and a short card no
  longer ends above its neighbour in a ragged row. The cards' own top
  margin gives way to an even gap under each, so every column starts on
  one line.
- A booking's or transport's title may take two lines in a column of a wide
  window instead of being cut after a few words.
- The map beside the plan opened too close: the opening frame was worked
  out for the window, not the narrower map, and Leaflet rounded its zoom up
  to the next whole level, so the day ran off the top and bottom. The map
  now opens at the whole level that fits and, once it knows its own size,
  works the frame out again for it (OpeningFrameFit). On a wide window the
  touch shell frames a day clear of its floating toolbar and corner
  buttons.
- Costs and to-dos in two panes are one element tree with the stacked
  arrangement, so a rotation or a fold between them keeps what is typed or
  open in either pane, and the row at the top of the list stays at the top.
- The place sheet's category pill has room for its name on a wide window.
2026-11-07 23:25:13 +01:00
Maurice 6906f80ff8 feat(native): Android tablets get the shortcuts, two panes meet at a foldable's fold
The Android shell now reports keyCommands and windowLayout, so the large
screen layout no longer assumes an iPad:

- The keyboard host hands the shortcuts to the app whenever the shell has
  the keyCommands feature, whatever the platform. Before it also required
  iOS, so an Android tablet with a keyboard answered the keys in the page
  and never listed them in the system's shortcuts.
- useWindowFold follows the layout the Android shell reports (feature
  windowLayout). When a vertical fold separates the window, a foldable half
  open like a book, MobileShell marks it with data-fold and its place, and
  the leading pane (the trip's plan, a tab's summary, a list beside its
  detail) ends at the fold while the trailing one starts there, on either
  reading direction. A flat fold that does not separate stays one screen.
- The split size class starts at 840pt, Android's expanded width, instead
  of 900pt, so a foldable unfolded on its side (about 841dp) gets two panes
  like an iPad does. An 11" iPad upright (834pt) stays a single pane. The
  address screen of the native shell follows the same width.
- TrekShell exports NativeWindowLayout for it.

Also: the sheet's width and anchor types and the collab board type stay
private to their modules, which nothing else imports (knip).
2026-11-07 23:25:13 +01:00
Maurice 7083667cbf fix(mobile): grid cards keep their height, a previewed photo its proportions
Cards in the large-screen grid keep their own height instead of stretching
to a taller neighbour in their row, which drew a short booking as an empty
panel. The file preview beside the grid shows a photo at its own
proportions rather than letterboxed in a portrait frame.

On a 13" iPad on its side the cloud plans of the native shell stand two by
two in their half of the screen, so the whole offer is in view at once.
2026-11-07 23:25:12 +01:00
Maurice 0b7cee0b0d feat(mobile): the trip's tabs, administration, collections and the journal use the large screen
On an iPad or an unfolded foldable the list screens kept one phone-wide
column in the middle of the page. They now lay out for the room they have:

- Transports, bookings, polls and tasks put their cards in a grid of as many
  phone-wide columns as the page holds (CardGrid), three on a 13" iPad on
  its side, two upright, one in a narrow Split View.
- Costs: with room for two panes (split size class) the summary (total,
  owe and owed, settle up, final budget, categories) stands in a pane of its
  own beside the expenses, each scrolling on its own (TabPanes). The search
  field sits in the expenses' column again instead of running past it.
- Files: a grid of tiles with each photo's picture; with room for two panes
  the picked file is previewed beside the grid with what it belongs to and
  its actions, the way Files on an iPad does. The signed preview address
  comes through fileRepo.previewUrl.
- Packing: the categories stand in columns, each card whole in its column;
  the action menu drops from its button at a popover's width.
- To-dos: the four smart filters as tiles and the trip's lists in a pane
  beside the tasks, which take the filter's name as their head.
- Collab: the chat stays in view at the trailing edge beside the notes,
  links or polls, and the header switches only the board. The chat keeps its
  place in the tree, so a fold or a rotation into two panes keeps the draft;
  a chat at its newest message stays there when its box changes size. Notes
  stand as a board in columns.
- Administration and settings share a section list beside the page
  (MSectionNav); the stats grid takes four columns.
- Collections: the lists stand in a column beside the places, which take two
  columns, and the head names the list on show.
- Journal: the entries become a feed beside the map, under their days, with
  photo, place and the start of the story, in step with the map like the
  strip of cards it replaces; it fades out under the header.
- The assistant opens as a tall panel at the trailing edge over a dimmed
  page, as Slide Over would, instead of covering the whole screen.
- The rail fits every name: a word too wide for it shrinks the label before
  it is hyphenated, and the admin entry uses its short name.
- The journeys and notifications headers keep to their column.
- The address screen of the native shell sets the photo as a panel beside
  the choices on a large screen on its side.

On a foldable that reports its fold, a leading pane ends at the fold and a
trailing one starts there. Phones keep their layout.
2026-11-07 23:25:12 +01:00
Maurice 36ed0e0d23 fix(mobile): the map frames the day beside the pane, atlas search as a centred panel
With the plan beside the map, the map frames the day once the splash has
gone, and again once its box has settled after the pane opens or hides, a
rotation or a fold: a frame fitted to the old box left the day half outside
the new one. On a wide window the atlas country search stands as a centred
panel instead of a band across the screen.
2026-11-07 23:25:12 +01:00
Maurice c204a25a9c feat(mobile): the touch shell laid out for iPad and unfolded foldables
The app shows the touch shell at every size now, an iPad's and an unfolded
foldable's included, instead of switching to the desktop pages from 768px.
One route tree survives rotation, Split View, Stage Manager and folding, and
the app's own features (Wallet, sharing into a trip, the edge swipe back)
are there on the big screen too.

Two size classes drive the layout (hooks/sizeClass.ts, Tailwind variants
wide: and split:):

- wide (600pt and taller than a phone on its side): a glass navigation rail
  on the leading edge replaces the dock and holds every destination with its
  name; sheets become centred form sheets; the trip's More menu is a popover
  beside the rail; lists keep a readable measure; dashboard, journey and
  vacay get columns; full-bleed maps run under the floating rail.
- split (900pt): the trip's plan, places or road trip chain sit in a pane
  beside the map, which follows the plan's day; the list/map switch hides
  and shows the pane like a sidebar; on a foldable that reports its fold the
  pane ends at the fold. Settings show their sections beside the page and the
  dashboard keeps its widgets in a column of their own.

A hardware keyboard gets the shortcuts an iPad app has (⌘1 to ⌘9 for the
sections, ⌘N, ⌘, , ⌥←/⌥→ through the days, ⌃⌘S for the pane, ⌘[ back),
answered by the page or, with a binary that has the keyCommands feature,
handed to it as native key commands.

The day strip fades at an edge that runs on, journey cards grow on a wide
window and a card without a photo takes the day's colour, and the Leaflet
map measures itself again when its box changes size.
2026-11-07 23:25:12 +01:00
Maurice c1c71a12cf fix(native): show Add to Google Wallet only where the server can make the pass
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.
2026-11-07 14:52:09 +01:00
Maurice 3beb5c4223 feat(native): the Android app's widgets, location, tile store, Play Billing and Google Wallet
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.
2026-11-07 14:11:59 +01:00
Maurice 354e30a075 fix(ci): wallet passes pass the server gates
- 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
2026-11-07 11:45:00 +01:00
Maurice 37a4e84360 feat: Apple Wallet passes for bookings
"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
2026-11-07 11:25:19 +01:00
Maurice 794da3be72 feat(native): booking passes go straight into Apple Wallet
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.
2026-11-07 11:25:18 +01:00
Maurice 7b8bd4b166 feat(native): TREK in the share sheet, with the trip picked right there
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
2026-11-07 11:25:18 +01:00
Maurice e5a119b6bd feat(managed): an install's own admin tabs on the phone too
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.
2026-11-07 10:47:59 +01:00
Maurice 315db3fa5a style(demo): format the phone welcome 2026-11-07 10:41:25 +01:00
Maurice 19ef38a377 fix(demo): the phone welcome scales with the text size and is paired with the desktop one
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.
2026-11-07 10:05:23 +01:00
Maurice 7797a9a562 fix(demo): the phone welcome lives with the phone dashboard, not under components
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.
2026-11-07 00:33:16 +01:00
Maurice 25091dbd60 fix(mobile): switches keep their knob inside the track at every text size
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.
2026-11-06 21:57:10 +01:00
Maurice 77b9252577 fix(journey): the card under the finger grows while swiping, and the zoom buttons really go on touch screens
- 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
2026-11-06 21:30:27 +01:00
Maurice 6b8eb939c3 fix(journey): the entry carousel no longer jolts when a swipe lands
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.
2026-11-06 20:12:26 +01:00
Maurice b7951baf23 fix(ci): green the dev gates after the native and popup rounds
- 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
2026-11-06 18:24:25 +01:00
Maurice 7e8e342e29 fix(mobile): stay editor and save to list match the common sheet header
Their icon tiles match TileHeader's (40 px, ink icon) and the stay editor's
search field is round like the others.
2026-11-06 16:50:46 +01:00
Maurice 445fb173fb feat(mobile): one sheet material, Vacay people and legend headers
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.
2026-11-06 16:49:54 +01:00
Maurice b317877c41 feat(mobile): remaining sheets get the common 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).
2026-11-06 16:48:25 +01:00
Maurice 27c19b5ff5 feat(mobile): admin, settings picker and place sheets in the common style, all sheets on glass
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.
2026-11-06 16:45:09 +01:00
Maurice ffd848e5dc feat(mobile): confirm dialogs and Vacay sheets in the common sheet style
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.
2026-11-06 16:40:06 +01:00
Maurice 45361c276c feat(mobile): journey create and entry sheets in the common sheet style
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.
2026-11-06 16:34:44 +01:00
Maurice 1b24e3dcef feat(mobile): collections sheets in the common sheet style
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.
2026-11-06 16:32:37 +01:00
Maurice 69d88ffefd fix(mobile): booking status toggle as tall as the fields, with status dots
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.
2026-11-06 16:29:33 +01:00
Maurice 92fc8ef28f feat(mobile): new trip sheet and shared pickers in the sheets' style
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.
2026-11-06 16:28:06 +01:00
Maurice 2aa25e3fe7 fix(mobile): a tour opens in a sheet instead of the desktop dialog
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.
2026-11-06 16:22:23 +01:00
Maurice f6bcdb8e6f fix(mobile): a stay in the day plan opens its booking's overview, not the editor
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.
2026-11-06 16:17:32 +01:00
Maurice 867a52bc0d feat(mobile): form sheets use the trip settings' fields and footer
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.
2026-11-06 16:11:08 +01:00
Maurice ec87f280c3 feat(mobile): journey settings in the trip settings' style
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.
2026-11-06 16:07:57 +01:00
Maurice 6e362de3da fix(mobile): collections start their first list from the dock, settings sheets aligned
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.
2026-11-06 16:01:28 +01:00
Maurice 654900bc50 fix(mobile): page transitions hung four seconds on every navigation to a lazy page
The transition waited for the new page to replace its route fallback and
polled for that with requestAnimationFrame. While a view transition waits
for its update the browser renders no frames, so the callback never fired
and every navigation to a page whose chunk was still loading sat frozen
until the browser gave up after four seconds. The wait now polls with a
timer: from Vacay to the journal takes 0.45 s instead of 4.1 s with an
empty cache.
2026-11-06 15:58:53 +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.
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 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 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 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 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