Commit Graph

4886 Commits

Author SHA1 Message Date
Maurice 545eb650ca fix(demo): the welcome is a sheet on a phone, not the desktop dialog made narrow
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.
2026-11-07 00:15:14 +01:00
Maurice 43f86f4559 test(booking-import): the per-leg seat tests pass the type check of the tests
An object literal with airplaneSeat inside the legs() call failed the excess
property check of tsconfig.tests.json; the nodes are built as constants first,
as the other seat tests do.
2026-11-07 00:12:56 +01:00
Maurice 25f2cd8192 fix(booking-import): every leg of a connecting flight keeps its own seat
The grouped flight wrote a seat only on the booking itself, from the first
segment. Each leg in metadata.legs now carries the seat of its own segment,
which the planner and the PDF already show per leg.
2026-11-06 23:50:53 +01:00
aaronspruit 7ae9198a43 fix(booking-import): read the seat that KItinerary gives
KItinerary writes a flight seat as airplaneSeat and a ticket seat as
reservedTicket.ticketedSeat.seatNumber, the schema.org fields, and it
removes seat and seatNumber from a script result. segmentDetail read only
seat and seatNumber, so an imported booking lost its seat.

segmentDetail now also reads airplaneSeat, and then the ticket seat when
no other key has a value. seat and seatNumber stay first, so a booking
that already has a seat gets the same result.
2026-11-06 23:49:31 +01:00
Maurice 8f3a8bd670 fix(ci): the carousel tests at the 90 ms settle, the search type, the hook back under its line limit
- 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
2026-11-06 22:51:15 +01:00
Maurice e0c3642874 fix(journey): the location search suggests as you type, like the planner
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)
2026-11-06 22:28:37 +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 fe7847fc08 feat(native): pick the trip a widget shows in Edit Widget
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
v5.1.0-tf.12
2026-11-06 21:41:21 +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 3364c0d131 chore(native): the lock screen mockup without the preview's boxes, the photo kept to the screen v5.1.0-tf.11 2026-11-06 21:03:27 +01:00
Maurice deabaf7398 chore(native): home and lock screen mockups of the widgets in the preview renderer 2026-11-06 21:03:27 +01:00
Maurice 9b780d6b7e fix(native): whole initials in the group avatars, a smooth glow across the world map 2026-11-06 21:03:26 +01:00
Maurice 93727084ec feat(native): packing, world map and travel group widgets
- 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.
2026-11-06 21:03:26 +01:00
Maurice ed178f3b5e fix(journey): the shared journey map reaches the screen edge, and desktop readers get the day picker
- 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)
2026-11-06 20:52:07 +01:00
Maurice f46f6f38fc fix(journey): no zoom buttons on the journey map on touch screens
On a phone they sat under the bottom bar, and a finger zooms with a pinch
anyway; devices with a mouse or trackpad keep them.
2026-11-06 20:45:15 +01:00
Maurice ae082c1fd0 fix(native): the widgets show the trip the dashboard features
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.
2026-11-06 20:25:41 +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 89f7bd2e2f feat(native): category emoji of older installs as widget symbols v5.1.0-tf.10 2026-11-06 19:13:59 +01:00
Maurice 2fe487154f feat(native): the widget gallery in the app's languages, previews with a cover
- Names and descriptions of the four widgets in TREKWidgets/<lang>.lproj for
  the 28 languages the app speaks, linted with the other property lists
- The preview renderer draws a cover photo state too, and the lock-screen
  accessories as the system does, without a box of their own
2026-11-06 19:08:51 +01:00
Maurice 71d014af95 feat(native): fuller large widgets, the live activity in the widgets' look
- The large trip and today widgets put the stops right under the day and the
  next booking at their foot instead of an empty middle; the large to-dos
  widget names its two rings with their counts
- The trip and day for a travel day as a Live Activity: a progress ring, the
  next stop large, the stops as segments, the Dynamic Island in the same green
- The place shows with a location arrow, and rows link through RowLink, which
  the preview renderer draws without the Link it cannot render
2026-11-06 19:06:18 +01:00
Maurice b1037dec62 feat(native): home and lock screen widgets in the TREK look
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.
v5.1.0-tf.9
2026-11-06 19:01:08 +01:00
Maurice 3466556849 test(e2e): swipe the day pager past its middle, as a native pager needs
The days are a scroll-snap pager since the native UX round: a page turns once
the finger has carried it past half its width. The spec swiped 180 px on a
412 px phone and read the chip right after; it now swipes 40 % either side of
the middle and polls the chip until the snap and the selection have landed.
2026-11-06 18:53:30 +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 f81fe8cb17 fix(native): a signed-out first start no longer ends on Server not reachable
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.
v5.1.0-tf.8
2026-11-06 16:54:02 +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 75d0a6181b feat(ui): dialogs, confirms and context menus leave as gently as they come
They faded and scaled in but vanished at once. A small presence hook keeps
them for 170 ms after closing while the mirror of their entrance plays:
scaling down and fading on a desktop, sliding down on a phone where the
dialog is docked as a drawer, with the backdrop fading out. During the exit
they show their last content, since an owner often clears the subject in the
same step, and take no clicks. Reduced motion gets a short fade. Unit tests
keep the old instant close.
2026-11-06 15:51:28 +01:00
Maurice 6de15a92a4 feat(mobile): fold the place details card away in the add place form
Its header is a button with a chevron; folded, the card shrinks to that
header with an eased height and the form's own fields move up.
2026-11-06 15:47:03 +01:00
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