Commit Graph

2494 Commits

Author SHA1 Message Date
Maurice dacc413c34 fix(native): a list's progress bar heads its first section, so two items show on the small watch 2026-11-07 17:14:12 +01:00
Maurice b8f6a99895 fix(native): long single-word menu rows shrink further instead of cutting; lists close up their sections 2026-11-07 17:14:12 +01:00
Maurice db82e04b49 fix(native): compact list progress, one-line menu rows, a booking header that wraps instead of cutting
The packing list and to-dos open with a slim progress bar and their count,
the list right under it. The trip's menu rows stay on one line and shrink
a little before anything wraps. A booking's kind and status share a line
when they fit and stack when they do not; its countdown drops the day
word before it moves under the time. The place map fades its top edge,
where labels were cut, and leaves Apple's attribution as MapKit draws it.
The complication gallery has no English section names.
2026-11-07 17:14:11 +01:00
Maurice 3abc9eacf4 fix(native): no cut words on the watch, in any language or case size
The booking's countdown moves under its time and its reference and seat
under each other when a row is too short, menu rows and the countdown
title take a second line, a list's count opens the list instead of
crowding its title, text people wrote in Latin letters breaks between
words in a Chinese, Japanese or Korean interface, and the day opens at
its top with nothing behind its title. A trip's pages take one of a set
of clean colours by its id, the same in every language; the capture
switches the simulators to a 24 hour clock after English.
2026-11-07 17:14:11 +01:00
Maurice 3a0edafdf0 fix(native): the watch's booking and today cards fit the 40 mm case in every language
The booking's kind and status share one line and its day steps aside when
the countdown needs the room; the next stop's name is a size smaller on
the small cases; eyebrows shrink a little before they cut; the day header
points forward in right-to-left languages; the sample trip keeps the
watch's own 12 or 24 hour clock.
2026-11-07 17:14:11 +01:00
Maurice 35d2b19f4c ci(native): pass the screenshot clock on the Mac's bash 3 2026-11-07 17:14:11 +01:00
Maurice 6b5b2f614d fix(native): watch screens fit every size and language
Countdowns use the system's short relative style in every language
("через 2 ч", "in 5 Min."), and durations take the watch's language;
a stop on now shows its range, not a countdown. The top-level pages drop
the titles that collided with the scroll indicator, the pages keep the
standard margins, the day header carries a chevron instead of a crowded
icon, the booking page fits on one screen with its reference and seat,
the day timeline opens at the stop that is on, the map is drawn from
above without labels, the bookings list has the trip's light, and the
complication preview adapts to the case size. The sample trip speaks
German and Russian in those languages.
2026-11-07 17:14:10 +01:00
Maurice 92f10531d0 ci(native): capture the watch screens one watch at a time, retried, and complete
Each launch is terminated first, retried after 2, 5 and 10 seconds when
the simulator refuses it, and captured once the app reports the screen
drawn instead of after a fixed wait. The job fails when any expected
image is missing.
2026-11-07 17:14:10 +01:00
Maurice d5ba4439f2 feat(native): a calmer watch map, whole street names, a today page that ends clean
The place map is flat and muted without points of interest, with the
stop's own pin; the day timeline gives a street two lines; the today page
puts what follows into the card instead of a list cut off at the fold, and
its header opens the day. The screenshot mode takes the real clock, moved
to the hour a screen needs through the app's time zone, so the clock in
the corner agrees; the capture covers three watch sizes, three languages
and every state.
2026-11-07 17:14:10 +01:00
Maurice 78a28e2747 ci(native): capture the watch screens on simulators the runtime supports 2026-11-07 17:14:10 +01:00
Maurice e59af6dcb3 feat(native): TREK on Apple Watch, with complications
A watchOS app embedded in the iPhone app: today's next stop with its
countdown, the next booking with reference, seat and platform, the trip
with its days and places (Directions into Apple Maps), and the packing
list and to-dos to tick off. The phone sends the snapshot through
WatchConnectivity; ticks go back as queued user info. Three
complications (next stop, countdown, trip day) in every accessory family,
scored for the Smart Stack. 28 languages.

The native workflow captures every screen from watchOS simulators in a
screenshot mode, on the largest and the smallest watch.
2026-11-07 17:14:09 +01:00
Maurice 9755330113 feat(native): build the Apple Watch snapshot and take back what the watch ticks off
The hero trip goes to the watch in full: every day with its stops and
coordinates, the bookings with reference, seat, platform and both ends,
the packing list and to-dos with ids. Ticks from the watch come back
through TrekShell.takeWatchActions and are written through the trip
store's toggles, so they take the repos and the mutation queue like a tap
in the app.
2026-11-07 17:14:09 +01:00
Maurice 5ca44453ae fix(ci): the Android merge passes the client gates
- wallet.ts: the Google Wallet state is read through a function after it was
  set, so TypeScript no longer narrows it to its old value (TS2367)
- iap.ts: a store listener that cannot be added is reported through
  reportNativeFailure instead of an empty catch
- iap.test.ts: the restored purchase is checked instead of asserted non-null
- trekShell.ts: the fold's rectangle is typed as a DOMRect pick, geometry the
  RTL lint does not read as text sides
2026-11-07 17:13:47 +01:00
Maurice 4461899910 docs(android): each tile store comment sits over the code it describes 2026-11-07 16:49:07 +01:00
Maurice ec22642f38 fix(android): review findings on the tile store, widgets, location, billing and the share sheet
- Tile store: the WebView's headers go along (a URL-restricted Mapbox token
  is checked against the Referer), and anything but a tile is left to the
  WebView, so a 401/403 never comes from the store.
- Widgets read their snapshot inside the composition, so every write reaches
  a running Glance session; the trip picker updates only the widget the id
  belongs to.
- Location: Approximate counts as allowed; the stream pauses with the app,
  resumes with it, and stops when the page reloads or the activity goes.
- Billing: an already-owned subscription comes back as the purchase, so the
  shell redeems it; a payment that clears outside the sheet is announced
  (storeTransaction) and found again at start (storeUnfinished).
- getWindowLayout answers with the last posture and hinge, not the bare window.
- Share sheet: keeps the picked trip through rotation and folding, and takes
  content:// only, never a file:// path read with TREK's own rights.
2026-11-07 14:57:50 +01:00
Maurice 46f4cab1b7 fix(native): location at the accuracy asked for, and a Play payment that clears later is redeemed
- 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.
2026-11-07 14:57:50 +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 924836be31 fix(android): the brand strings are not translated, and stopping location needs no permission
Android lint counts every string without a translation once the app has
languages; the app name and the URL scheme are names, not text. Removing
location updates is guarded for a permission revoked meanwhile.
2026-11-07 14:27:57 +01:00
Maurice da05ba1121 refactor(native): keep the Android bridge additions apart from the iOS ones in trekShell.ts
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.
2026-11-07 14:20:33 +01:00
Maurice 1e4f1922e8 feat(android): bring the Android app level with the iOS app
Everything the iOS shell gained, built natively for Android 16 with Material
You, and back to Android 7:

- Home-screen widgets in Jetpack Glance: trip, today, bookings, to-dos,
  packing, world map and travel group, from the same snapshot the iOS widgets
  read. Wallpaper colours on Android 12+, resizable in three layouts, a trip
  picker per widget (reconfigurable), live previews in Android 15's picker
  and preview images before it.
- The travel day as a Live Update: a promoted ongoing notification with the
  next stop as the status-bar chip, a countdown and the day's stops as
  segments (ProgressStyle), a quiet ongoing notification before Android 16.
- A share sheet of its own: a Material bottom sheet with the trip picker for
  files, mails, pages and links shared from any app, and the coming trips as
  launcher and direct-share shortcuts.
- Push: channels per kind of notification (chat, trip updates, invitations,
  general) by the event the server now names, stacked per channel.
- Location for navigator.geolocation from the fused provider, a store for
  Mapbox's map resources (30 days, 250 MB), Google Play Billing behind the
  StoreKit methods, and Google Wallet save links.
- Large screens: every orientation and window size without a restart,
  the window's size classes and a foldable's posture and hinge for the UI
  (windowLayout), the UI's keyboard shortcuts as the app's (keyCommands),
  predictive back, per-app language.
- The native strings in the 28 languages of the iOS app, generated from its
  strings files (scripts/generate-android-strings.mjs).

Debug builds carry a widget gallery that draws every widget at every size.
2026-11-07 14:16:04 +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 e1a08e82b9 fix(native): name our terms before the purchase and speak Google Play on Android
- 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.
2026-11-07 12:22:34 +01:00
Maurice 0557c7290b test: the server section links the app's view of the privacy policy 2026-11-07 12:16:00 +01:00
Maurice 640998a4ef fix(native): the privacy manifest declares trip expenses as other financial info
Budget keeps expenses, splits and who owes whom; declared for app functionality,
linked, never for tracking, as in the App Privacy answers.
2026-11-07 11:55:19 +01:00
Maurice ac7dde7d93 fix(native): the privacy manifest names the user id, the push token and messages too
It now matches the App Privacy answers in App Store Connect: user id, device id
(the push token for the relay) and emails or text messages (group chat, imported
booking mails), next to email, name, purchases, photos, other user content and
precise location; all for app functionality, linked, never for tracking.
2026-11-07 11:54:31 +01:00
Maurice 6e1c9e8e9f fix(native): App Review findings on buying TREK Cloud in the app
- 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.
2026-11-07 11:48:03 +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 792e55b2ca style(native): format the in-app purchase files and strings 2026-11-07 11:25:17 +01:00
Maurice 5462bce9c3 feat(native): TREK Cloud subscriptions through the App Store
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.
2026-11-07 11:25:17 +01:00
Maurice ede54d10cd test(managed): the mock factory brings its own imports
vi.mock is hoisted above the file's imports, so the managed-tab mock could not
use the icons and JSX imported at the top (ReferenceError before initialization).
2026-11-07 11:25:10 +01:00
Maurice e7a99db03b fix(privacy): the PDF print view uses the bundled Poppins, not Google Fonts
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.
2026-11-07 11:22:53 +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 55e4bfa11e fix(demo): the phone welcome sheet is opaque, so the dashboard does not show through its list 2026-11-07 00:22:04 +01:00
Maurice ad0fa41bb5 Revert "fix(demo): the phone welcome sheet is opaque, so the dashboard does not show through its list"
This reverts commit 7c91a609da.
2026-11-07 00:21:29 +01:00
Maurice 7c91a609da fix(demo): the phone welcome sheet is opaque, so the dashboard does not show through its list 2026-11-07 00:20:06 +01:00
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 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
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 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