Never miss a logging day#10
Conversation
Closes the "MissingDays calculator + MissingDayRange" plan step: pure WhereCore logic that finds unlogged days in [Jan 1, through] for a year and collapses them into consecutive ranges, with tests covering boundaries, leap years, clamping, and range collapsing. Co-authored-by: Cursor <cursoragent@cursor.com>
Closes the "ReminderTime + LoggingReminderScheduling + scheduler" plan step: ReminderTime value, the scheduling protocol, and a UserNotifications-backed implementation that schedules per-day cancellable reminders and drives the app-icon badge. Notification copy added to the WhereCore string catalog. Pure groundwork: exercised by the controller wiring + spy tests in the next step, so there are no standalone tests here. Verified to compile via `tuist build Where`. Co-authored-by: Cursor <cursoragent@cursor.com>
Closes the "controller reminder wiring" plan step: WhereController now takes an injectable reminder scheduler + clock, exposes configureReminders / notification-auth passthroughs, and reconciles the badge + per-day reminders after GPS ingest, manual entry, and startGPS. A frozen-clock spy scheduler drives deterministic tests for the backlog badge, the today-logged cancellation path, and enable/disable. Also adds isAuthorized() to the scheduler protocol and declares an explicit WhereCoreTests scheme so the bundle can run on its own (the autogenerated scheme has no working test action). Co-authored-by: Cursor <cursoragent@cursor.com>
Closes the "extend WhereModel" plan step: persisted remindersEnabled / reminderTime (default on, 8 PM), setters that push the config to the controller on change, configureReminders on start(), and missingDays/missingDayCount derived from the loaded report for the current year. Adds tests for the computation + settings persistence. Adds a NoopLoggingReminderScheduler in WhereCore (alongside the existing ScriptedLocationSource preview/test seam) and wires it into PreviewSupport and the view-model tests so they never touch UNUserNotificationCenter. Co-authored-by: Cursor <cursoragent@cursor.com>
Closes the "MissingDaysView + prefill" plan step: a sheet listing the year's unlogged day ranges, each opening a ManualDayEntryView prefilled with that range (new ManualDayEntryView(prefill:) init that seeds the single-day vs range mode and dates). Adds the missing-days strings (plus the banner strings the next step consumes) and a missingDaysModel preview fixture. Co-authored-by: Cursor <cursoragent@cursor.com>
Closes the "warning banner" plan step: when the current year has unlogged days, the Primary tab shows a tappable Liquid Glass banner above the content that opens MissingDaysView to backfill them. Hidden when there's nothing missing. Co-authored-by: Cursor <cursoragent@cursor.com>
Closes the "Reminders settings" plan step: a toggle for the daily logging reminder plus an hour/minute time picker, both routed through WhereModel so changes reconcile the scheduled reminders and badge. When reminders are on but notifications are denied, the section surfaces an "Allow notifications" affordance that deep-links to system Settings. Co-authored-by: Cursor <cursoragent@cursor.com>
Closes the "strings + AppDelegate delegate + UI tests" plan step: - AppDelegate becomes the UNUserNotificationCenterDelegate so logging reminders present (banner/sound/badge) even while Where is open. - Adds the missing-days, banner, and reminder-settings keys to the WhereUI string catalog so the new copy is translatable. - Adds hosting tests covering the Primary banner branch and MissingDaysView with real ranges. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@cursoragent review |
|
@codex review |
|
Findings addressed:
Pushed commits:
Verification:
This cloud image needs the repo’s Swift toolchain setup ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f505a28132
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for (id, day) in desiredIDs where !pendingIDs.contains(id) { | ||
| await scheduleReminder(identifier: id, day: day, time: reminderTime) |
There was a problem hiding this comment.
Reschedule pending reminders when time changes
When the user changes the reminder time, the desired identifiers stay the same because they are keyed only by day, so any existing pending request is skipped here instead of being replaced with a trigger using the new hour/minute. In that scenario Settings persists and displays the new time, but already-scheduled notifications continue firing at the old time until they fall out of the rolling window; remove/re-add owned pending requests when reminderTime changes, or include enough trigger state in the reconciliation to detect stale requests.
Useful? React with 👍 / 👎.
Co-authored-by: Kyle Van Essen <k@kve.me>
Co-authored-by: Kyle Van Essen <k@kve.me>


Summary
Adds a missing-day safety net to Where: it detects unlogged days since Jan 1 of the current year and surfaces them four ways — a daily local notification, an app-icon badge, an in-app warning banner, and a guided backfill flow.
[Jan 1, today], collapsed into consecutiveMissingDayRanges (boundaries, leap years,through-clamping, dedup/sort).ReminderTime+LoggingReminderSchedulingprotocol behind aUNUserNotificationCenter-backed impl: per-day cancellableUNCalendarNotificationTriggers keyed by date id, owned-only cleanup, andsetBadgeCount.NoopLoggingReminderScheduleradded for previews/tests.configureReminders, and badge/reminder reconciliation after GPS ingest, manual entry, andstartGPS(the "recorded successfully → drop today's reminder & lower the badge" path).remindersEnabled/reminderTime(default on, 8 PM), setters that reconcile through the controller,configureRemindersonstart(), andmissingDays/missingDayCountderived from the loaded report (current year only).MissingDaysView, which lists each gap and deep-links into a prefilledManualDayEntryView(newinit(prefill:)); plus a Reminders section in Settings (toggle + time picker + "Allow notifications" affordance when denied).AppDelegateis now theUNUserNotificationCenterDelegateso reminders present while foregrounded. No entitlement / Info.plist changes were needed.Notes / deviations from the plan
WhereCoreTestsscheme toProject.swift(the autogenerated one has no working test action, matching the existing note for the other unit-test bundles).Test plan
tuist test WhereCoreTests—MissingDaysTests+ spy-drivenWhereControllerTests(backlog badge, today-logged cancellation, enable/disable)tuist test WhereUITests—WhereModelMissingDaysTests(computation + settings persistence) and hosting tests for the Primary banner branch +MissingDaysViewtuist test WhereTests— app target green after the AppDelegate change./swiftformat --lintclean across the treeMade with Cursor