Commit Graph
112 Commits
Author SHA1 Message Date
FuelBoard Contributor b1889dacf5 Tie alerts attach the app icon explicitly
LiveContainer shows its own icon in the notification header, so tied
alerts (which carry no map) now attach the app's compiled
AppIcon60x60@2x.png so the FuelBoard identity shows as the banner
thumbnail and in the expanded view. Single-station alerts keep the map
embed.
2026-08-12 11:19:51 +01:00
FuelBoard Contributor 6f4b6882c9 Tie alerts: no map, body tap doesn't direct
A tied-cheapest notification now delivers immediately with the choice
buttons and NO map snapshot (a map would only show one arbitrary
station). Tapping the notification body of a tie no longer opens
directions — nothing has been chosen; only the option buttons trigger
directions. Single-station alerts keep the embedded map + tap-to-route.
2026-08-12 11:05:21 +01:00
FuelBoard Contributor 9f579051e5 Tie buttons show full station name (not brand or address)
Action buttons for tied cheapest stations now read '<station name> ·
<distance>' (e.g. 'Tesco Express · 0.8 mi') instead of just the brand,
in both the live geofence path and the Settings real-data test.
2026-08-12 11:00:22 +01:00
FuelBoard Contributor 1f50d7dcf4 Tied cheapest stations become notification action buttons
When several stations share the cheapest price within the radius (within
0.01p), the alert registers a dynamic UNNotificationCategory with up to
4 action buttons (brand + distance, nearest first) and taps open
directions to the chosen station. Shared FuelStore.tiedStations helper
used by both the live geofence path and the Settings real-data test;
35 tests pass.
2026-08-12 10:48:08 +01:00
FuelBoard Contributor cbe01f4219 Single notification delivery: map renders before add()
LiveContainer did not honour the replace-in-place contract: adding a
second request with the same identifier delivered a SECOND notification
instead of updating the first. Now the MKMapSnapshotter renders first
(10s timeout), then exactly ONE request is added with the map attached.
If the snapshot fails or times out, the notification still fires without
the image — never two notifications.
2026-08-12 10:21:59 +01:00
FuelBoard Contributor b56d2a129c Deliver alert immediately; map attaches in place afterwards
The notification used to be added only inside the MKMapSnapshotter
completion — if the snapshot hung or failed, no notification was ever
delivered and the result line never showed the map status. Now the
notification fires immediately (same identifier), the snapshot renders
in the background with an 8s timeout, and on success the delivered
notification is replaced in place with the map attached. Result line
always updates: preparing -> attached / failed / timed out.
2026-08-12 10:12:35 +01:00
FuelBoard Contributor 9032c4a551 Both test buttons carry real station coords; map status surfaced
- Plain test notification now picks the nearest station selling the
  monitored fuel and carries its real name, price, distance and
  coordinates (was a static no-station notification)
- addAlertRequest reports whether the map snapshot attached or why it
  failed, appended to the Settings result line instead of silently
  delivering a notification without the embedded map
- Settings routes both test buttons through the live monitor
2026-08-12 10:06:48 +01:00
FuelBoard Contributor a964a45d49 Notification tap opens directions to the station
- Alerts (live + real-data test) carry the station name + coordinates in
  userInfo and attach a map snapshot with a red pin, so the expanded
  notification shows where the station is
- Tapping a notification opens Apple Maps driving directions to the
  station from the user's current location (maps:// daddr)
- If Apple Maps hand-off fails (LiveContainer), an in-app map sheet
  (StationMapView) pins the station + user location with a Directions
  button
2026-08-12 09:57:42 +01:00
FuelBoard Contributor 9f6ecb610c Alerts show actual station name, distance and price
Both the real alert path and the real-data Settings test now render the
notification body as 'Station · 1.2 mi away · 145.9p' — actual haversine
distance to the station in the user's unit, not just the radius.
2026-08-12 09:41:48 +01:00
FuelBoard Contributor 950844878c Test alert uses real data: actual cheapest station within radius
The first Settings test button no longer sends a hardcoded placeholder —
it runs the same criteria as a live alert (monitored fuel, alert radius,
current location) against the loaded stations and sends the REAL station
name, brand and price. Result line shown under the button; graceful
message when no stations or no sellers within radius.
2026-08-12 09:34:17 +01:00
FuelBoard Contributor ae37e224c6 Settings: plain test notification + foreground banner delegate
- New 'Send plain test notification' button fires with zero criteria checks
  (no geofence, no cheapest check, no permission gate) to isolate delivery
  and tap behaviour
- ProximityMonitor is now the UNUserNotificationCenter delegate: banners
  show even in the foreground (system suppresses them otherwise), and taps
  complete the 'open FuelBoard' action
2026-08-12 09:24:59 +01:00
FuelBoard Contributor d6550409df Settings: test alert notification button
Fires a local notification shaped exactly like a real price alert (uses the
currently configured alerts fuel + radius), with permission-gated handling:
prompts on first use, guides to Settings when denied. Tapping it opens
FuelBoard, same as a real alert.
2026-08-12 09:15:04 +01:00
FuelBoard Contributor 3dcfe30dcc Trim alerts fuel caption — no need to explain Stations-tab independence 2026-08-12 09:03:43 +01:00
FuelBoard Contributor 46d5d45c93 Alerts choose their own fuel, independent of the Stations tab
The proximity monitor was tied to the Stations-tab fuel selection, so
switching the list to another fuel silently re-targeted geofences. Now the
Alerts tab has its own 'Fuel to monitor' picker (persisted as
fuelboard.alertsFuel):
- Monitor reads loadAlertsFuel() on init/background relaunch
- Changing the alerts fuel re-registers geofences around stations selling
  that fuel only
- Favourites priority slots stay scoped to the monitored fuel
- Tests: alerts fuel round-trip + default (33/33 passing)
2026-08-12 08:51:31 +01:00
FuelBoard Contributor 35e0adaf0b Favourites are fuel-scoped: starring pins the station for one fuel only
Fixing the bug where favouriting an Unleaded entry also created a Diesel
favourite. Favourites are now [FavouriteEntry] = (station, fuel) pairs:
- Starring a row pins it only for the fuel being viewed
- Favourites tabs show only fuels that actually have favourites
- Geofence monitor gives priority slots only to favourites matching the
  monitored fuel
- Tests updated + new fuel-scoping test (31/31 passing)
2026-08-12 08:31:08 +01:00
FuelBoard Contributor 0dbf88ee7d Favourites: split list by fuel type with segmented tabs
Only fuel types that currently have at least one favourite appear as
tabs; each tab ranks that fuel's favourites cheapest-first and the
narrative (crown header, section title, cheapest-favourite explainer)
names the active fuel.
2026-08-12 08:17:10 +01:00
FuelBoard Contributor 7903c9a2d8 App icon: match the onboarding welcome badge
Green-to-blue gradient circle with a white fuel pump symbol on a light
background — identical to the first onboarding icon.
2026-08-12 08:10:25 +01:00
FuelBoard Contributor a6b1e24cd6 Merge station count into the top explainer
The 'N stations within X' caption now lives in the header explainer
(Closest/Cheapest + radius + count on one line); the Distance section
shows just the picker.
2026-08-12 08:02:38 +01:00
FuelBoard Contributor 75cfb3681d Remove 'Distance only applies to Cheapest' explainer
Closest mode now shows the disabled distance picker with no caption;
the N-stations-within caption still appears in Cheapest mode.
2026-08-12 07:58:01 +01:00
FuelBoard Contributor 44c6c38536 Add Settings tab: distance units toggle, onboarding replay, £4.99 tip; drop E10/E5 from labels
- FuelType labels: 'Unleaded (E10)'/'Premium (E5)' -> 'Unleaded'/'Premium'
- New DistanceUnit (miles/km) preference; all app + widget distances,
  radii and alert text convert/display in the chosen unit
- Settings tab (4th tab): segmented miles/km picker, 'Show introduction'
  replay button (onboarding moved out of Alerts tab), StoreKit tip £4.99
- ProximityMonitor + widget notifications use the chosen unit
- Tests: label expectations updated, DistanceUnit conversion + format tests
2026-08-12 07:46:08 +01:00
FuelBoard Contributor fb79cf0788 Add first-launch onboarding with location + notification permission prompts
- OnboardingView: 4-page flow (welcome + features, location, notifications, done)
- Triggers system prompts in-context via OnboardingPermissionPrompter
- Shown at initial launch only (FuelStore.onboardingCompleted flag, app-group defaults)
- Test hook: 'Show onboarding (testing)' button at bottom of Alerts tab
2026-08-12 07:10:54 +01:00
FuelBoard Contributor 2ed18d81a3 Backlog, tests, logo compression, security audit, App Store icon
- BACKLOG.md: P0-P3 roadmap incl. govuk API switch (blocked on credentials)
- APPSTORE.md: review-gate analysis (2 blockers: LAN data source, Always-location notes)
- SECURITY.md: audit - no secrets, no ATS holes, one MEDIUM (relay IP in binary, planned fix)
- FuelBoardTests/: SPM package, 28 tests on real Shared/ sources (sanitizer, price guard, RAG, sort, distance, brands)
- Logos: proper 2x/3x pairs, palette-quantized, 75% smaller (315KB -> 79KB)
- App icon: 1024px fuelpump on gradient (was missing entirely -> instant rejection)
- decodeStations(from:) exposed for testability; relay fetch reuses it
2026-08-11 23:14:28 +01:00
FuelBoard Contributor 05f3ef0a70 Add (E10)/(E5) bracket labels to Unleaded/Premium in picker and descriptions 2026-08-11 22:41:50 +01:00
FuelBoard Contributor 313fe84037 Use native large-title behaviour: drop custom icon header, .navigationTitle + .large (exact Mail-style collapse) 2026-08-11 22:27:38 +01:00
FuelBoard Contributor 24fffe1e8f Pin large title above list so it fades+collapses in place on scroll (Mail-style cross-fade) 2026-08-11 22:21:09 +01:00
FuelBoard Contributor ec191d77aa Cross-fade large title with compact bar on scroll (Mail-style) 2026-08-11 22:15:11 +01:00
FuelBoard Contributor cf69350472 Use native onScrollGeometryChange (iOS 18) so the compact icon+title bar actually appears on scroll, Mail-style 2026-08-11 22:05:03 +01:00
FuelBoard Contributor 8d24de401d Large icon+title header at rest; compact gas-pump bar appears only after scrolling (tracker on header row background) 2026-08-11 21:59:32 +01:00
FuelBoard Contributor 644342ae63 Nav bar: always show gas-pump icon next to title (inline mode); remove scroll-offset row hack 2026-08-11 21:54:56 +01:00
FuelBoard Contributor 12cb3707b6 Nav bar: show compact gas-pump+title only when scrolled (large title at rest); drop icons from fuel picker 2026-08-11 21:49:18 +01:00
FuelBoard Contributor 39b310d68d Add gas-pump icon left of title + coloured fuel-type tab icons (Unleaded green, Premium blue, Diesel grey) 2026-08-11 21:36:28 +01:00
FuelBoard Contributor 7be98c51ad Closest mode: TOP + deltas vs the CHEAPEST within radius (not the nearest); list stays nearest-first 2026-08-11 20:16:32 +01:00
FuelBoard Contributor 2ca1274b8f Closest mode: deltas vs the CLOSEST station (signed +X.Xp/-X.Xp), not the nationwide cheapest; baseline = sortedStations.first 2026-08-11 20:04:29 +01:00
FuelBoard Contributor 3bc795317a Distance picker disabled in Closest mode; whole-country pool for nearest-first (radius never empties Closest) 2026-08-11 19:49:26 +01:00
FuelBoard Contributor 3cff677729 Distance change is local-only: no re-fetch (full-UK cache already covers 5/10/15 miles) 2026-08-11 19:41:46 +01:00
FuelBoard Contributor d62ac1b265 Distance filter below fuel type under Distance heading; list paginated 10 rows with Show more 2026-08-11 19:38:08 +01:00
FuelBoard Contributor cf9765be7b Fetch full-UK dump (no radius/limit); stations in app-group defaults only; alert path keeps focused radius fetch 2026-08-11 19:30:02 +01:00
FuelBoard Contributor 3953c65a65 Show distances in miles (app + widget) to match the miles filter 2026-08-11 19:18:27 +01:00
FuelBoard Contributor 6eae42ff3d STRICT radius enforcement: never fall back to out-of-radius stations in list, cheapest reference, or widget 2026-08-11 18:36:08 +01:00
FuelBoard Contributor e0a5bb3d5d Cheapest reference uses chosen miles radius (same pool as list) in app + widget; header shows 'within N miles' 2026-08-11 18:29:20 +01:00
FuelBoard Contributor bcf4ea4f81 Enforce chosen miles radius client-side (cache may hold larger-radius stations); monospaced £ price + delta for decimal alignment 2026-08-11 18:07:17 +01:00
FuelBoard Contributor 0fc24eab7c Fix alert fetch signature (radiusKM param); alert fetches within max(trigger radius, 10km) 2026-08-11 18:03:23 +01:00
FuelBoard Contributor 213f93aa04 Results filter is now search radius in miles (5/10/15); relay fetch scoped to radius, limit 500; changing miles re-fetches; caption shows stations within N miles 2026-08-11 18:02:23 +01:00
FuelBoard Contributor 810cc2c006 Show full fetch in list (count picker respected); radius scope only for cheapest/RAG reference; row = logo | name/address/distance | £price + delta | star 2026-08-11 17:54:04 +01:00
FuelBoard Contributor 8230daa249 Cache-first refresh policy: auto-fetch at most twice a day (12h gate), no fetch on launch/foreground/location/fuel-switch, pull-to-refresh forces; geofences still follow location 2026-08-11 17:43:11 +01:00
FuelBoard Contributor 5a08a3a2fa Revert station name to natural width (truncates, does not fill row); price stays on row below right-aligned 2026-08-11 17:29:13 +01:00
FuelBoard Contributor 2d95c4e1d6 Scope cheapest/RAG/TOP to alert trigger radius in app + widget; header shows 'within X km'; falls back to full fetch when radius pool empty 2026-08-11 17:21:36 +01:00
FuelBoard Contributor cde51622a3 All fuel prices per station (E10/E5/Diesel from one fetch); re-fetch on fuel switch; fuel tabs populate instantly 2026-08-11 17:16:55 +01:00
FuelBoard Contributor e8a82ce515 Preload cached stations at launch (all tabs populate instantly); alert handler falls back to cache on background wake, restores enabled/fuel/radius in monitor init, re-registers geofences on setEnabled 2026-08-11 16:50:45 +01:00
FuelBoard Contributor c5050df445 Live location tracking: continuous foreground updates (250m filter) + significant-change background monitoring; list/widget refresh and geofences follow the user, re-fetch on every move 2026-08-11 16:16:46 +01:00