Commit graph

34 commits

Author SHA1 Message Date
8a2a87a1a9 v0.1.9: About page — plain-English "what's sent to IPS" + privacy blurb
All checks were successful
build-apk / build (push) Successful in 16m49s
Documents exactly what reaches ParkSmarter's API (sign-in, account, a single
map-search coordinate, session-start details) and what never does: device GPS is
used only on-device to position the map and cached locally — never attached to
sign-in/sessions/vehicles/payments; no background location, no IMEI/device-id/ad
tracking, no Google services. Audited against the source: the only endpoint that
sends a coordinate is getMetersByLocation, called solely from map searches with
the map's centered point.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:48:33 -07:00
4cb5478737 v0.1.8: tap a session for full detail + receipt
- Sessions list items are now tappable -> SessionDetailScreen.
- Past sessions fetch the full receipt (GET /api/ParkingReceipt): vehicle,
  start/end, time purchased, parking/fee/total, payment (MASTERCARD ••1234),
  auth code, meter, city, transaction id. Adds an "Email me this receipt" action.
- Active sessions show zone/space/start/ends/time-remaining.
- Confirm PastSession + ParkingReceipt types from a live DL-zone session (were
  UNCONFIRMED); fix past-session card to show Zone/Description, not blank.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:38:39 -07:00
42a96b0cff v0.1.7: collapse flat-rate zones to one option; add DL capture script
Some checks failed
build-apk / build (push) Failing after 8m10s
- Start-session: when the single-estimate fallback finds the same price at the
  min and max duration (a flat-rate zone like DL), show ONE option — "$X flat,
  parked until <end>" — instead of a wall of identical-price rungs. No reason to
  offer shorter durations when the price doesn't change.
- capture-dl.mjs: dumps the DL zone's live requests+responses (tokens redacted)
  to ~/Downloads/dl-capture/ for a durable record.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 10:58:49 -07:00
65fbd86644 v0.1.6: validate the session on open, not mid-map
All checks were successful
build-apk / build (push) Successful in 38m15s
A stale/expired token doesn't 401 — /api/User returns an empty body, so
getUserDetail() resolved to undefined without throwing and the launch probe set
signedIn anyway. The dead session then only surfaced when the map/kiosk calls
ran. Now the launch check requires real user data (PersonalPhone/Email); if it's
missing it clears the token and shows the login screen at open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 10:46:04 -07:00
7212badd2d v0.1.5: detect free windows in the estimate fallback + add API tests
All checks were successful
build-apk / build (push) Successful in 49m41s
App:
- When the single-estimate fallback sees MaxTime 0 (or an all-$0.00 ladder),
  show "Parking is currently free" instead of a bogus $0.00 purchase ladder.
  Free-detection extracted to tested client helpers (isFreeEstimate/ladderAllFree).

Client + tests (node:test, zero deps):
- src/estimates.ts: pure isFreeEstimate/ladderAllFree helpers (exported).
- test/estimates + test/http (mocked fetch): headers, query serialization,
  error->ParkSmarterApiError, rolling Auth_Token refresh, 401 clears token.
- test/integration: live checks against the Sandpoint (DSB) zones; targets the
  `test` instance by default and SKIPS when it's unreachable (non-prod is
  IP-restricted -> 403). Verified 3/3 green against prodv2 read-only.
- CI runs `npm test` as a build gate.

Note: dev/stage/test instances return 403 "Web App - Unavailable" from the
public internet (IP-restricted). API schema is otherwise unchanged vs Jul 6.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 20:42:58 -07:00
f8d9e2970c v0.1.4: fix silent failure on zones that reject ParkingEstimateMulti
All checks were successful
build-apk / build (push) Successful in 14m27s
Some zones (e.g. short-term "DL" spaces at the downtown lot) return
Response.Status:Error "Unable to process your request" for ParkingEstimateMulti
even though the single-duration ParkingEstimate works. The screen swallowed that
error -> empty duration list, disabled Pay button, no message ("nothing happens").

- Fall back to a single-estimate ladder across the zone's Min/Max time so these
  zones are purchasable (verified live: flat ~$0.10 up to ~33 min).
- Surface estimate errors with a message + Try again, instead of a blank screen.
- Log the estimate fallback/failure to the on-device diagnostics log.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 16:31:22 -07:00
626e46a55f v0.1.3: version bump for a clean CI trigger on the cached runner
Some checks failed
build-apk / build (push) Failing after 21m0s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 12:22:19 -07:00
870da6f432 v0.1.2: theme Favorites screen (dark mode); versionCode 2
Some checks failed
build-apk / build (push) Failing after 1h1m11s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:31:48 -07:00
5d269bf2a3 Fix release signing in CI: anchor plugin regex to buildTypes release block
All checks were successful
build-apk / build (push) Successful in 35m3s
The signing plugin added a release{} inside signingConfigs{}, so the debug-line
replacement matched that block instead of the release BUILD TYPE — CI produced
debug-signed APKs (would break Obtainium updates vs the release-signed v0.1.0).
Anchor on `buildTypes { ... release {`. Verified locally: release buildType now
uses the release key when BBP_UPLOAD_* is set. Also unify keystore path via
github.workspace in both steps.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 19:49:37 -07:00
3d1505d2e7 CI: move workflow to .forgejo/workflows; self-contained Forgejo release; bump 0.1.1
Some checks failed
build-apk / build (push) Failing after 10m45s
- .forgejo/workflows/build-apk.yml (Forgejo's location): on v* tag, build signed
  APK using the Actions secrets and publish via the Forgejo API (curl + auto token),
  no external action. Best-effort NDK install for the RN version.
- Remove the old .gitea workflow to avoid double runs.
- Bump app version to 0.1.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:18:06 -07:00
02e2c750ea Notifications: local-only session reminders; drop the FCM-bridge framing
- Session-expiry reminders are purely on-device (AlarmManager); added an Android
  notification channel and a "Send a test reminder" button (Account > Notifications)
  to verify delivery without a real session
- Strip all FCM/bridge talk from unifiedPush.ts + README; UnifiedPush(ntfy) is now
  an optional no-op stub for hypothetical future server-initiated messages only

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:14:34 -07:00
a8ba1fcae4 Release signing fix + Obtainium install guide + publish script
Some checks failed
build-apk / build (push) Failing after 5m9s
- Fix withReleaseSigning: RN template's unconditional debug signingConfig was
  overriding the release key. Now uses the release key when BBP_UPLOAD_* is set
  (verified: APK signed CN=BigBrainParking), else debug.
- INSTALL.md: dead-simple Obtainium setup (one-tap obtainium:// link + manual
  Forgejo-source steps). README links it.
- scripts/publish-release.sh: create a Forgejo release + upload the signed APK.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:27:19 -07:00
a5bfec894e Fix "$652.89/hr" on Variable-rate meters (display only)
Waterfront zones (BS/WB/RV/PB/CB) use RateType "Variable" with a placeholder
Rate 652.89 ("Variable Rate - See signs"), not an hourly price. Only render $/hr
for the real "Hour" rate type; show "Variable — see signs" otherwise, and derive
the headline Rate from the current policy instead of the raw zone Rate ("NA"/652.89).
Actual quoting is unaffected — the estimate ladder returns sane per-duration prices.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 13:17:46 -07:00
d1473f9776 Scanner: parse processQR URLs, manual Zone ID entry, zone-name fallback
Kiosk QR decoded to a bare https://www.parksmarter.com/home/processQR (no zone) —
even the official app rejects code-less URL QRs. Now: parse processQR?code= URLs,
detect bare app-link QRs and prompt for the printed Zone ID, and add manual Zone ID
entry. Lookups try getMetersByScannerCode then getMetersByZoneName (DSB13 maps to
zone 113150). Fixes scanning real stickers, which previously passed the URL to the API.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:38:38 -07:00
1c597ec471 Detect declined sessions (200 + Status:Error); theme Sessions screen
- startParkingSession: server returns HTTP 200 even on a declined charge — now
  detect Response.Status:"Error" and throw the gateway reason (e.g. "DECLINED")
  so the app shows the failure instead of a false "Parked!"
- Confirmed the session request format is correct (server processed it); expand
  StartParkingSessionResponse with the real fields (OriginalErrorMessage, etc.)
- Theme SessionsScreen (was black-on-black in dark mode)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:25:14 -07:00
7ec7ae9200 Add on-device debug logging: file capture + share (for real-session testing)
- fileLogger: append redacted API requests/responses + app events to an on-device
  log file (persists across restarts); the client's logSink now writes to it
- Diagnostics screen (Account > Diagnostics): toggle recording, Share log file
  (expo-sharing), clear. StartSession logs the session attempt/result explicitly.
- Lets a real parking session be captured while away from a computer, then shared.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:59:41 -07:00
80812d25cf StartSession: current-policy awareness (Free / No Parking / paid)
Read the CurrentSlot policy: when the meter's current time-of-day window is Free
or No Parking, show that status instead of a purchase flow (matches the official
app). Confirmed via API: DSB3 9AM-5PM is RateType "Hour" $4/hr with no free grace;
the "2 hours free" is a city grace period outside the ParkSmarter payment system.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:48:35 -07:00
f044a9a511 Wire up start-session flow (variable-rate aware) + fix About dark mode
- New StartSession screen off MeterDetail: pick vehicle -> duration (from the live
  multi-estimate ladder, exact cost per option) -> card; detects a free prefix and
  shows "Free until X, then charged"; explicit Pay $X confirmation before charging
  via postStartParkingSession; schedules the local expiry reminder on success
- About screen themed (was black-on-dark in dark mode)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:39:19 -07:00
9a5c9d445f Add Notifications preferences (session-expiry reminder lead time)
- New Notifications screen (Account > Notifications): enable/disable expiry
  reminders + a 5-minute-increment stepper (5–60 min), default 15 min
- localReminders.scheduleExpiryReminder now reads the persisted lead time + enabled
  flag, so it's ready to fire once the start-session flow is wired

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:33:17 -07:00
a566f41d66 Remove [CAM] debug logging; default HTTP request logging off
Camera recenter-on-search confirmed fixed (view-lock). Strip the temporary trace
logs; keep viewport tracking (needed for the fix). HTTP request logging now defaults
off — still toggleable in Account > Log requests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:27:14 -07:00
7321453337 Map: pin current view before 'Search this area' to defeat camera stop-revert
The native MapLibre camera re-applies its last setCamera() stop on the search
re-render (confirmed via trace: revert to prior target with userInteraction=false).
Track the live viewport and, before searching the current area, setCamera() to the
current center (0ms) so the re-applied stop is a no-op instead of a jump-back.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:22:47 -07:00
a4150555ae Fix map recenter (root cause), login logo/theming, policy pricing display
- ROOT CAUSE: MapLibre Camera re-applies its last setCamera() stop on every
  re-render, so a search (setZones) snapped the map back to the last programmatic
  target. Memoize the Camera + UserLocation elements so search re-renders never
  touch them; only the marker ShapeSource updates. (Confirmed via [CAM] trace.)
- Login: brain-car logo + full theming so text is readable in light/dark
- Meter policies: negative Rate is a sentinel (RateType No Parking=-1 / Free=-3),
  not a price; show DisplayString + colored type dot, real $/hr only when positive.
  Fix zone rate "$ 4.00" formatting.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:14:23 -07:00
648acb95cb Meter screen: theme it + fix rate/max-time/policy display; add camera trace logs
- MeterDetail now themed (fixes unreadable black headings in dark mode)
- Rate no longer double-'$'; MaxTime 0 -> "Unlimited"; policies show a clean
  title + time window and hide sentinel (<=0) rates
- Temporary [CAM] logging on the map to trace the persistent recenter-on-search

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 11:05:42 -07:00
3837200d89 Map: render meters as a GL CircleLayer instead of MarkerView
MarkerView (React overlays repositioned per frame) caused the pins to float and
the camera to snap back to the user on pan/zoom/tap. Meters are now a ShapeSource
+ CircleLayer (GPU-drawn, coordinate-anchored); tapping a circle opens its parking
flow via the source's native onPress. No React overlays = no floating, no camera thrash.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:46:10 -07:00
b0c343ab20 Map: pin markers -> parking flow; stop native user-location camera tracking
- Markers are now compact colored pins (no long label bubbles); tapping one opens
  the meter/parking flow for that zone
- UserLocation renderMode "native" -> "normal": the native location component was
  tracking the camera back to the user on re-render / marker tap. Normal mode draws
  the dot in the JS layer with no camera coupling.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:38:39 -07:00
f28bc4dd93 Map: disable rotation gesture
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:33:15 -07:00
fb3d1cdf3a Detect failed logins; fix map camera recenter, marker contrast, dark map tiles
- Client: server returns 200 + Status:"Error" on bad credentials; loginWith* now
  throw LoginError with the server message instead of faking a signed-in state
- Map: uncontrolled camera positioned once on load + explicit actions only, so
  Search/marker-tap/re-renders no longer snap back to the user's location
- Markers: always-dark high-contrast bubble + zone-colored dot (some zones report
  a white BackgroundColor -> was white-on-white)
- Dark mode now switches map tiles to CARTO dark-matter

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:27:41 -07:00
2f9f0f604a Add request logging mode, dark mode, Account hub, profile, vehicle CRUD, cards view
- Client: logRequests option + redacted request/response logging (setLogRequests to
  toggle at runtime); enabled via app extra.debugHttp for on-device 401 debugging
- Dark mode (persisted) via ThemeProvider + React Navigation theme
- New "Account" tab: Profile (view), Vehicles (full CRUD), Payment methods (view-only),
  About, dark-mode + request-logging toggles, sign out

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 10:14:24 -07:00
65e9118806 401 auto re-login, My Location via native GPS, edge-to-edge, brain-car icon
- Client: onUnauthorized hook; 401 clears the token and (in-app) bounces to sign-in
- Map: "My location" uses the native UserLocation fix + flyTo; safe-area insets on
  the floating status bar / controls
- Enable edgeToEdgeEnabled (Android 15 forces it) so headers/back-arrow sit correctly
- App icon: brain-car launcher + adaptive icon

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:58:23 -07:00
b31cb78592 Map: search the viewport, add My Location + status feedback; tab bar icons
- "Search this area" now searches the map's current center (getCenter), so it
  works with no GPS fix — pan anywhere and search. Fixes "search here does nothing".
- "My location" recenters the camera on the GPS fix and searches there.
- Status bar shows real feedback (N meters / none found / errors) instead of silent.
- Add bottom tab icons via @expo/vector-icons (map/qr/star/time).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:35:46 -07:00
d3d81a9de1 Add babel-plugin-module-resolver (required for release JS bundling)
The '@/' import alias needs this plugin; debug builds hid the gap because
they bundle lazily via Metro. Release (standalone) bundling now succeeds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:19:25 -07:00
454c72ddb4 Fix native module versions for Expo SDK 53; use real joelangel.png
- Bump react-native-screens ~4.11.1, safe-area-context 5.4.0, async-storage 2.1.2
  (the 4.4.0/4.12.0/1.23.1 pins failed react-native-screens Kotlin compile on RN 0.79)
- Move the real joelangel.png into app/assets; drop stray root copies
- Debug APK now builds successfully (arm64-v8a)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 09:02:48 -07:00
6b19fc79dd Add About screen with 7-tap joelangel easter egg; fix deps
- About screen surfaces getAbout() content; tapping the title 7x reveals
  assets/joelangel.png (placeholder committed; replace with the real image)
- Correct UnifiedPush package to react-native-unifiedpush-connector (lazy-required)
- Fix expo-notifications NotificationBehavior (shouldShowAlert)
- Reachable via an ⓘ header button on the main tabs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:55:37 -07:00
1dede50995 Initial commit: parksmarter-client + BigBrainParking app
Reverse-engineered ParkSmarter API client (TypeScript, live-verified) plus a
de-Googled Expo/React Native app for GrapheneOS: MapLibre meter map with GPS,
VisionCamera QR kiosk scanning with save/share, local session-expiry reminders,
UnifiedPush wiring, and Gitea CI to publish signed APKs for Obtainium.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:33:47 -07:00