Commit Graph
100 Commits
Author SHA1 Message Date
FuelBoard Contributor f5a5d3325f Swap app icon to water-drop pound design (drip export) 2026-08-14 04:53:31 +01:00
FuelBoard Contributor 050c9f13a3 Onboarding: show the app icon with iOS rounded corners
The welcome page's green-gradient circle + SF symbol is replaced by the
app icon itself (AppIconArtwork imageset mirroring AppIcon.png), clipped
to the standard iOS rounded-rect corner radius (21pt at 96pt — ~22%),
so onboarding matches the Home Screen icon.
2026-08-13 23:41:16 +01:00
FuelBoard Contributor fec139cf5a Icon: replace with Fuelboard-copy composer render (Vecteezy gas-station art)
New Icon Composer project artwork (gas-station vector, layered + background)
rendered via ictool at 1024pt: Default / Dark / TintedLight renditions
replacing all three asset-catalog slots.
2026-08-13 23:36:57 +01:00
FuelBoard Contributor 018e6eec2f Icon: use the Icon Composer render instead of the pre-baked appiconset PNG
The export zip contained both a pre-rendered appiconset AND the
Fuelboard.icon composer project; the pre-baked PNGs were a different
(flatter) render. Re-rendered the .icon bundle with ictool (Xcode 26
Icon Composer executable) — Default/Dark/TintedLight renditions at
1024pt — and replaced the three asset-catalog slots.
2026-08-13 22:00:14 +01:00
FuelBoard Contributor 3545239ad6 New app icon: green pump showing £1.00, with dark + tinted variants
Replaces the single 1024px icon with the three-variant set (universal,
dark-luminosity, tinted) generated for FuelBoard; Contents.json updated to
the iOS-only slots.
2026-08-13 21:37:36 +01:00
FuelBoard Contributor dafc96f845 Fix small widget skeleton: use the proven medium intent; delete failing small intent
ROOT CAUSE (proven by A/B test on-device): the small widget's custom
intent (FuelBoardSmallWidgetConfigurationIntent with favouriteFuel +
favourite AppEntity params) fails fresh-widget default-config resolution
at the system level — the whole default config fails, no timeline is
ever requested, makeEntry never runs, widget stays on the skeleton.
A minimal small widget sharing the MEDIUM intent (fuel/sort/distance
only) populated immediately in the same extension on the same device.
Neither removing defaultResult() (4d35b08) nor the
@IntentParameterDependency (8a9e775) helped — the extra entity params
themselves break resolution.

FIX: FuelPriceSmallWidget now uses FuelBoardWidgetConfigurationIntent
exactly like the working test widget. FuelBoardSmallWidgetConfiguration
Intent, FavouriteFuel(+Query), WidgetFavourite(+Query) deleted; the
pinned-favourite picker is gone (approved tradeoff). Favourites sort
still works on both faces via the list branch — the small face renders
the cheapest favourite of the chosen fuel. Beacon tag distinguishes the
small widget in diagnostics; test widget removed, gallery order
restored (small, medium, live activity).
2026-08-13 20:43:15 +01:00
FuelBoard Contributor 3cf1d5c9af Widget diagnostics: per-intent beacons (keychain slots + relay query)
The single-slot beacon couldn't distinguish small vs medium (last writer
wins). Beacon keychain service now embeds the intent type name so small
and medium never overwrite each other; the relay GET now carries the
intent type + entry state as query params so the access log shows exactly
which widget kind ran makeEntry, when, and with what. Diagnostics screen
shows SMALL and MEDIUM beacons side by side — a missing SMALL beacon
after adding the small widget proves its timeline never reaches the
provider (system-level config resolution failure).
2026-08-13 20:13:24 +01:00
FuelBoard Contributor 335a9751dc Widget diagnostics: beacon from makeEntry (keychain + relay) + app diagnostics section
The small-widget skeleton survived four resolution fixes; instrument the
widget instead of guessing. Every makeEntry now writes its entry state
(intent type, location source, station count, fuel, sort, favourites flag,
first station) to a keychain beacon (app-readable) and fire-and-forgets a
GET to the relay's /api/v1/widget-diag so its access log records whether a
widget timeline actually runs in the extension and what it produced.
Settings → Debug gains a Widget Diagnostics section: the keychain beacon +
the installed-widget inventory from WidgetCenter. Relay gains the
/api/v1/widget-diag route (204, log-only).
2026-08-13 19:52:12 +01:00
FuelBoard Contributor 2409b71f2f Labels: pluralize miles (1 mile) and show true distances in the chosen unit (5 mi = 8 km) across Alerts/Stations pickers + captions 2026-08-13 15:10:23 +01:00
FuelBoard Contributor 490c343fd5 Alerts: mile-friendly radius 1/2/3/5/8 + Follow search (capped 8 mi); Live Activity Follow search; legacy radii clamped 2026-08-13 15:00:53 +01:00
FuelBoard Contributor bb48995fac Onboarding copy: tighten values (daily refresh, widget line, alert line) 2026-08-13 14:22:12 +01:00
FuelBoard Contributor 2d070fa3ba Fuel colours: align with wheel palette — Unleaded #30D158, Premium #FFD60A, Diesel #64D2FF 2026-08-13 13:09:22 +01:00
FuelBoard Contributor 3fe2a7f03c Onboarding copy: drop premium fuel mention, 'updated daily' 2026-08-13 13:02:50 +01:00
FuelBoard Contributor 0d91c82bb1 Localize all app copy via Localizable.strings 2026-08-13 10:57:36 +01:00
FuelBoard Contributor bbe43a6149 Onboarding: Local Network confirmation matches other pages
Granted label now reads just 'Connected' with the green tick and uses
the same .subheadline confirmation styling as the Location and
Notifications pages.
2026-08-13 10:35:18 +01:00
FuelBoard Contributor a1fa0f9998 Onboarding: silent Local Network pre-flight on replay
Replaying onboarding showed a visible probe on the 4th page because
iOS exposes no status API for the Local Network permission — the
connection attempt IS the only detector. Location and Notifications
query their status at init, so their ticks are already known when the
page appears; Local Network could only be known by probing.

Now, when onboarding is re-opened (already completed), a pre-flight
probe runs silently at open: granted connects in ms, denied reports
.waiting/.localNetworkDenied immediately, and a plain .waiting
(permission undetermined after a reinstall) cancels so page 4's
Continue still fires the prompt in context. By the time the data page
appears its tick (or denied text) is already on screen, matching the
other permission pages. First run is unchanged.
2026-08-13 10:29:20 +01:00
FuelBoard Contributor ed243fae9e Onboarding: show green tick on Local Network page before advancing
The probe result landed while the Local Network prompt was still
dismissing, so the granted tick was skipped and the page jumped
straight to the final slide. Hold 'Connected — prices will load'
on screen for a beat before auto-advancing, matching the visible
tick flow of the Location and Notifications pages.
2026-08-13 10:11:57 +01:00
FuelBoard Contributor cb174f66fa Settings About: drop redundant 'FuelBoard x.y(z)' footer
Version already shown in the Version row; the footer just duplicated it.
2026-08-13 10:06:51 +01:00
FuelBoard Contributor c2a9943d4c Favourites tab: fuel-type selector gets the same coloured pump icons
- FuelTypeSegmentedPicker promoted to shared (was private in StationsView)
  and parameterised by fuel list so Favourites passes only fuels with favourites
- Favourites tab swaps native .segmented picker for the capsule control with
  green/purple/grey fuelpump icons, matching Stations
2026-08-12 23:03:35 +01:00
FuelBoard Contributor 0fef9870cb Favourites tab: remove the crown 'cheapest favourite' banner
The gold-crown header row is redundant with the list itself (rows are already
cheapest-first with a TOP badge on the first). The bottom caption about
alerts monitoring stays.
2026-08-12 23:00:16 +01:00
FuelBoard Contributor d864319593 Fuel-type segmented control: coloured pump icons per fuel
- Custom capsule segmented control (native .segmented can't tint per-segment)
- fuelpump.fill icon per segment: green=Unleaded, purple=Premium, grey=Diesel
- FuelType.tintColor: Premium blue -> purple (also updates the nav title icon)
- Selected segment keeps the raised-capsule look; save+widget reload unchanged
2026-08-12 22:52:03 +01:00
FuelBoard Contributor 7f7c0fff03 Alerts tab: picker-style config matching Live Activity; move Live Activity there
- Alerts section restyled to mirror the Live Activity section: one section
  with toggle + Fuel picker + Radius picker (was segmented fuel + slider)
- Alert radius options expanded to 1/2/3/5/8/10/15/20 (display unit, snapped
  to nearest option; stored km unchanged; validation widened to 33 km)
- Live Activity section moved from Settings into the Alerts tab so both
  'notify while driving' surfaces sit together
- SettingsView loses the Live Activity bindings/section
2026-08-12 22:30:54 +01:00
FuelBoard Contributor fcb83f1eed Live Activity: own fuel + distance config, independent of Stations tab
- Settings → Live Activity now has Fuel and Distance pickers (5/10/15,
  unit-aware) stored under fuelboard.liveActivityFuel / liveActivityRadiusMiles
- LiveActivityManager uses these instead of the Stations-tab selectedFuel /
  stationLimit; Stations changes no longer re-target the pill
- Fuel moved from Activity attributes into ContentState so changing fuel in
  Settings updates the RUNNING activity in place (no end+restart needed)
- Stations tab stays a pure data-interrogation + favourites surface
2026-08-12 20:58:05 +01:00
FuelBoard Contributor 04b233bf9a Live Activity: cheapest-in-radius Lock Screen/Dynamic Island pill
- Shared FuelBoardLiveActivityAttributes/ContentState (app + widget targets)
- FuelBoardLiveActivity Widget: ActivityConfiguration rendered by the widget
  extension - Lock Screen banner + Dynamic Island (leading/trailing/bottom,
  compact, minimal), tap anywhere -> maps:// directions to the best station
- LiveActivityManager (app side): request/update/end; mirrors the app list's
  TOP badge (selected fuel + chosen distance radius, price-then-distance
  ties); local start-date tracking for the 8h cap (this SDK's Activity has no
  startDate) - auto-restarts at 7h59m; silent no-op when disabled/denied
- Wiring: 250m/60s location hook (incl. background significant-change
  wake-ups), refresh completion, fuel/radius/unit changes, toggle flip
- Settings toggle + FuelStore persistence (keychain-backed, app-group)
- App-Info.plist: NSSupportsLiveActivities
2026-08-12 20:11:42 +01:00
FuelBoard Contributor 1dcd47c6cd Fix widget distance menu + geofence notification chain
Widget distance menu: the Distance picker only makes sense for
Cheapest ordering. It was hidden for Favourites but Closest still
showed it (regressed when the per-widget Distance picker was added).
parameterSummary now nests When clauses: Distance visible for
Cheapest only; Closest and Favourites hide it.

Notifications: three compounding defects kept real geofence alerts
from ever firing while the app was suspended:

1. Always permission was never properly requested. requestPermissions
   fired WhenInUse and Always back-to-back; iOS ignores the second
   call while the first prompt is pending, leaving the app stuck on
   WhenInUse — and region entries are never delivered in the
   background. Added locationManagerDidChangeAuthorization to
   ProximityMonitor: escalate WhenInUse -> Always, and re-register
   geofences on grant (regions registered under WhenInUse-only won't
   deliver in the background).

2. The 18-region window was frozen in the background. Re-registration
   lived in SwiftUI .onChange(of: locationManager.current), which
   never runs while suspended. Added a delegate hook
   (LocationManager.onLocationUpdate) fired from didUpdateLocations on
   every fix including background significant-change wake-ups; the app
   wires it to re-register geofences around the new position.

3. Region-registration failures were invisible. monitoringDidFailFor
   was never implemented, so a failed startMonitoring (region budget,
   auth, radius) silently stopped alerts. Now surfaced as
   monitor.lastRegionError and shown in Settings -> Debug; cleared on
   the next successful registration.
2026-08-12 18:18:24 +01:00
FuelBoard Contributor f822bd2636 Live-test fixes: CarPlay disfavor, widget location refresh, debug cheapest parity
1. CarPlay widget taps could never launch Maps — FuelBoard is not a
   CarPlay app, and Apple only lets a widget launch its own app in
   CarPlay when that app is CarPlay-enabled. Marked the widget as a
   disfavored CarPlay location (iOS 26+; no-op below): read-only in
   the car, no dead tap. Header comment updated to match reality.

2. Widgets now track the user's location: timeline refresh 15m -> 5m,
   and the app's location manager reloads all widget timelines after
   every significant move (250m filter, throttled to 60s), so the
   widget re-orders around the new position while driving instead of
   waiting for the next tick.

3. Debug 'Cheapest in range' no longer shows the ALERT prediction
   (alertsFuel + alert radius — a different pool by design). It now
   mirrors the app list exactly: the TOP-badge station for the
   selected fuel within the stationLimit radius (new DebugAppCheapest
   computed in ContentView, passed to Settings). Alert-prediction
   cheapest kept as fallback when the app view hasn't resolved.
2026-08-12 18:01:29 +01:00
FuelBoard Contributor fccd0c07ef Settings About: data source section (connection, station count, GOV.UK update time)
- Relay envelope now carries source (api|csv), stations_count, and
  data_updated (freshest price_last_updated the GOV.UK server reports,
  independent of relay sync time).
- App decodes the new envelope metadata (RelayMeta), persists it after
  each full fetch, and shows it in Settings → About → Data source:
  Connection (API/CSV), Stations count, Data updated (local-formatted).
- Older relays without the fields decode cleanly (nil meta → em-dash).
- 2 new tests for meta decode + absent-meta tolerance; 37/37 pass.
2026-08-12 16:23:41 +01:00
FuelBoard Contributor 3988dea30c Onboarding: network page no longer loads data — bare TCP connect
- The Data page's probe was an HTTP GET /api/v1/stations (8s timeout)
  that read as 'loading data' and could stall the slide. Replaced with a
  bare TCP connect to the relay host:port — just enough to trigger the
  iOS Local Network permission prompt, zero payload transferred.
- Grant -> auto-advance to the final slide (existing onChange); denial or
  unreachable relay -> Continue re-enables and proceeds. 12s safety
  timeout so the page can never dead-end on a spinner.
- NWConnection held on the prompter so the connect survives until the
  prompt is answered. Status copy: 'Waiting for network permission…'.
  Full dataset still downloads AFTER onboarding via forced refresh.
  35 tests pass.
2026-08-12 15:19:00 +01:00
FuelBoard Contributor 55ab4537b5 Onboarding: Data page never dead-ends on Open Settings
- The Prices-ready page button stays 'Continue': tapping it fires the
  Local Network prompt + relay probe; on grant the page auto-advances to
  the final slide (existing onChange), on denial it still advances.
- Removed the Open Settings branch from the Data page entirely (kept for
  Location page, where the user can still be sent to Settings).
- Denied caption no longer suggests opening Settings. 35 tests pass.
2026-08-12 15:02:16 +01:00
FuelBoard Contributor 3303035c04 Alerts: trigger on approach — geofence radius = alert radius
- Region radius changed from fixed 300 m forecourt fence to the alert
  radius (the Alerts-tab slider), clamped to the device's
  maximumRegionMonitoringDistance.
- Notification now fires when you ENTER the winner's circle while
  approaching, not when you reach the forecourt.
- Cheapest-gate unchanged: still only fires when the entered station is
  the cheapest within the alert radius (fresh prices). Dedup still 1/
  station/hour. 35 tests pass.
2026-08-12 14:50:09 +01:00
FuelBoard Contributor 126fdbde62 Settings Debug: device coordinates + in-range station indicator
- Debug section now shows the last device fix (lat/lng to 5dp) and its age.
- In-range indicator uses the SAME criteria as live alerts: stations selling
  the monitored fuel within the alert radius of the fix. Green dot + count
  when in range, red when none, grey while waiting for a fix.
- Shows the cheapest in-range station (name, distance, price).
- ProximityMonitor recomputes the snapshot on every location/stations
  change and on Debug-section appear; ContentView passes the live status.
- SettingsView extracted to its own property to keep TabView body within
  the compiler type-check budget. 35 tests pass.
2026-08-12 14:33:52 +01:00
FuelBoard Contributor 07521b1c98 Settings: move test notifications into hidden Debug section + dev flag
- Test-alert buttons moved out of the Alerts section into a Debug section.
- Debug section only renders when the debug flag is on; completely hidden
  otherwise (no user-facing toggle).
- Hidden toggle: tap Settings → About → Version five times to flip the
  flag (keychain + app-group storage, survives reinstall).
- About section added with version/build number.
- 35 tests pass.
2026-08-12 14:10:07 +01:00
FuelBoard Contributor 5246f41c48 Widget Favourites filter: pinned stations cheapest-first, no radius
- New Sort by value 'Favourites' in Edit Widget (per-widget config).
- ParameterSummary hides the Distance picker when Favourites is selected
  (favourites are not radius-bound).
- Favourites mode reads keychain favourites (shared access group works on
  SideStore free even without App Groups), filters to the widget's fuel,
  refreshes prices from a focused relay fetch when in range, and ranks
  cheapest-first with distance tiebreak. Not radius-filtered.
- App now re-saves refreshed favourites to keychain after every fetch so
  the widget shows current prices instead of star-time snapshots.
- Empty state + headings adapt to favourites mode. 35 tests pass.
2026-08-12 13:45:42 +01:00
FuelBoard Contributor 3e226ece57 Widget links maps:// directly so CarPlay can route to Apple Maps
- The widget now uses the native maps://?daddr= scheme for taps in both
  sizes. In CarPlay there is no containing app to relay through, but since
  Apple Maps IS a CarPlay app, the system may route the tap straight to
  Maps on the car display (WWDC25: widgets can launch apps in CarPlay).
- iOS keeps working via onOpenURL: the app now forwards maps:// URLs (if
  the Home Screen delivers them to the app), plus legacy fuelboard:// and
  http://maps.apple.com links from older cached timelines.
- No public API exists for a widget to detect CarPlay context, so the
  single maps:// link serves both; the app-side forward is the iOS safety
  net. 35 tests pass.
2026-08-12 13:07:44 +01:00
FuelBoard Contributor 1ff14607c5 Widget tap relay via fuelboard:// scheme; full station names; no check-again
- WidgetKit widgets can only open their containing app — the Link URL is
  delivered to FuelBoard via onOpenURL, never opened directly in Maps. The
  widget now links to a registered fuelboard://directions?lat=&lng= URL,
  and FuelBoardApp forwards it to native maps://?daddr= directions.
  (http://maps.apple.com and raw maps:// both silently dropped in-app.)
- Widget rows (small + medium) now show the full station name (station.name)
  instead of just the brand.
- Onboarding network page: removed the 'Check again' secondary screen after
  the Local Network prompt — Open Settings remains the only retry path.
  35 tests pass.
2026-08-12 12:54:08 +01:00
FuelBoard Contributor 9ce0c83d66 Widget opens Maps via maps://; onboarding prompts after Continue; forced first fetch
- Widget tap-to-directions now uses the native maps:// scheme instead of
  http://maps.apple.com. The universal-link http URL failed to open from a
  widget and fell back to launching the containing app; maps:// opens
  Apple Maps directly for both small (whole widget) and medium (per row).
- Onboarding: removed the Skip button (mandatory flow), and system prompts
  (location, notifications, local network) now fire only when the user taps
  Continue/Allow on each page — not when the page appears. Pages
  auto-advance once a permission is granted.
- After 'Start Using FuelBoard' the first data fetch is forced, so a
  reinstall that leaves a recent lastRefresh in app-group defaults can no
  longer skip the fetch and leave the station list empty. 35 tests pass.
2026-08-12 12:41:19 +01:00
FuelBoard Contributor 20589f41fe Onboarding: prompt permissions in context + data page
Location and network fetches no longer fire at launch before onboarding
reaches the relevant step: foreground tracking and the first refresh are
gated behind onboarding completion (scenePhase no longer starts tracking
while the cover is up, and onAppear defers its fetch until after
finish). A new Data page sits just before 'You're all set': it probes
the FuelBoard Relay, which surfaces the iOS Local Network permission
prompt in context (NSLocalNetworkUsageDescription added), verifies
connectivity, and offers Open Settings/Check again on denial. Onboarding
is now 5 pages: Welcome, Location, Notifications, Data, Done. 35 tests
pass.
2026-08-12 11:59:07 +01:00
Ade ThompsonandFuelBoard Contributor 7d0a3645ee Revert "Tie alerts attach the app icon explicitly"
This reverts commit b1889dacf5.
2026-08-12 11:26:58 +01:00
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