feat(focus): single 'Go' focused trip, pinned on home#58
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Schedule the focused-trip leave reminder as a real Apple AlarmKit alarm on iOS 26+ so it breaks through Silent Mode / Focus, instead of a local notification that's easy to miss. Falls back to the existing notification everywhere else (Android, web, AlarmKit unavailable/denied, or any scheduling error). The alarm replaces the notification — never both. Stacked on the go-focused-trip refactor: the AlarmKit preference lives in the focused-trip orchestration (useFocusedTrip) plus a thin iOS-only wrapper; notificationScheduler.ts stays a pure primitive. - Add @capgo/capacitor-alarm + src/lib/native/leaveAlarm.ts wrapper (availability/auth/schedule/cancel + pure decideReminderChannel). - armAndPersistReminder prefers a Leave Alarm, scheduling the new channel before retiring the old one so a failed (re)schedule never leaves the user with no reminder; persist the alarm id on FocusedTripReminder. - Cancel/clear/disarm retire both channels (notification + alarm). - Label the active pill "Leave alarm" when AlarmKit-backed (en + es). - Add NSAlarmKitUsageDescription to Info.plist. - Vitest coverage for the channel decision and alarm scheduling/fallback. Android keeps the notification: the plugin's Android path only hands off to the system Clock app (uncancellable), and true break-through alarms require USE_FULL_SCREEN_INTENT, which Android 14+ won't grant a transit app by default.
- Date-safety (high): @Capgo's createAlarm only takes a clock time and fires at the NEXT occurrence of that HH:MM — it can't target a calendar date. A weekend trip focused on a weekday (serviceDate = nextServiceDate) or a "tomorrow" departure whose time recurs earlier today would have fired days early. scheduleLeaveAlarm now bails (→ dated notification fallback) unless fireAt is the next occurrence of its own clock time (alarmFiresOnIntendedDay). - Stale-focus race: a permission prompt can block long enough for the user to Stop / switch trains / the trip to auto-clear. armAndPersistReminder now re-reads the focus after scheduling and, if it changed, rolls back the freshly scheduled alarm/notification instead of resurrecting the stale trip. - Permission gate: notification permission is now requested only on the notification fallback path, so an iOS user who denied notifications but authorized AlarmKit can still set a Leave Alarm. Adds unit coverage for the day-safety helper and the off-day fallback.
3 tasks
…rm-lASlY feat(reminders): leave alarm via AlarmKit on iOS 26+
…stination subtext
…ip reads as MY TRIP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the per-trip departure-reminder array with a single, global, user-indicated focused trip ("Go" — I'm taking this train), pinned at the top of the home screen with live times. The reminder is demoted to an opt-in sub-option of the focused trip.
FocusedTripCardalways shows above the schedule, reconstructed from static schedule via its stored identity and overlaid with live realtime status (delays/cancellations) for the focused leg — independent of the home screen's current from/to. Its duplicate row is hidden from the list when on the matching leg.source: "user"seam so the deferred riding-detector integration (spec fix(api): allow Capacitor CORS for GTFS-RT endpoints #2) needs no schema change.selectedTripNumber(open-sheet / deep-link state) andfocusedTripare deliberately separate facets on the same provider.smart-train-departure-remindersarray is migrated on first load, then removed. Notification mechanics extracted intonotificationScheduler.ts; olddepartureReminder.ts/useDepartureReminder.tsdeleted.Data model — anchored on identity + service date (not stored times)
The focused trip stores its identity (train number + leg + scheduleType) and a
serviceDateanchor; departure/arrival are derived (reconstruct static schedule → resolve against serviceDate → overlay realtime). This is why the pinned card shows live times rather than a frozen snapshot.We deliberately do not anchor on GTFS
trip_id: SMART'strip_ids are synthetic and regenerated per feed build (statict_6153517_b_86615_tn_0vs realtimet_6043274_b_86583_tn_0), and our data refreshes daily — a storedtrip_idwould dangle after the next republication. The stable public identity is the train number, which the app already uses. See the spec's "Revision" section for the full rationale.Design spec:
docs/superpowers/specs/2026-05-29-go-focused-trip-design.mdImplementation plan:
docs/superpowers/plans/2026-05-29-go-focused-trip.mdTest plan
npx vitest runnpx tsc -p tsconfig.app.json --noEmit),npx vite buildsucceeds.Known v1 limitations
Follow-up
sourceseam is in place).🤖 Generated with Claude Code