Add calendar creation, all-day events, and focused alerts - #71
Conversation
|
APIError: You've reached your usage limit for this billing cycle. Your quota will be refreshed in the next cycle. To continue now, purchase extra usage or upgrade your plan: https://www.kimi.com/code/#pricing |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
dayline-website | e59bc68 | Aug 11 2026, 12:21 AM |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (4)
📝 WalkthroughWalkthroughThe PR adds all-day calendar event support, meeting-link recognition, Apple Calendar event creation, provider-aware calendar controls, reusable date pickers, UI-test synchronization helpers, and updated permission and privacy documentation. ChangesCalendar data and agenda pipeline
Apple Calendar creation
Calendar presentation and support
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant StatusMenuView
participant StatusStore
participant AppleCalendarEventEditorView
participant AppleCalendarService
participant EventKit
User->>StatusMenuView: Select calendar event creation
StatusMenuView->>StatusStore: requestAppleCalendarEventCreation()
StatusStore->>AppleCalendarEventEditorView: Open editor window
User->>AppleCalendarEventEditorView: Enter and submit draft
AppleCalendarEventEditorView->>StatusStore: createAppleCalendarEvent(draft)
StatusStore->>AppleCalendarService: createEvent(draft)
AppleCalendarService->>EventKit: Save event
EventKit-->>AppleCalendarService: Return save result
AppleCalendarService-->>StatusStore: Return creation result
StatusStore-->>StatusMenuView: Refresh calendar agenda
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Sources/Dayline/Stores/StatusStore.swift (1)
4083-4091: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winAlign the Apple fetch window with the Google fetch window.
Line 4088 starts the EventKit predicate at
now. Line 4100 now starts the Google fetch attodayStart.agendaSectionskeeps every event whoseendDate > todayStart, so the two providers no longer contribute the same slice of today.A timed Apple event that started and ended earlier today is never fetched. The equivalent Google event is fetched and appears in
sections.today. The same event synced to both providers also loses its Apple source label after deduplication, because only the Google copy is present.Start the Apple predicate at
todayStartso both providers cover the same window.🐛 Proposed fix
- let events = appleCalendarService.events(in: enabledCalendarIDs, from: now, to: dayAfterTomorrow) + let events = appleCalendarService.events(in: enabledCalendarIDs, from: todayStart, to: dayAfterTomorrow)
todayStartis already aletcaptured in this scope, so no other change is required.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Dayline/Stores/StatusStore.swift` around lines 4083 - 4091, Update the Apple fetch window in the closure initializing appleFetchTask to pass todayStart, rather than now, as the start argument to appleCalendarService.events. Keep the existing enabled-calendar filtering, cancellation checks, and dayAfterTomorrow end boundary unchanged.
🧹 Nitpick comments (6)
Sources/Dayline/Services/CalendarService.swift (1)
232-243: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winRestrict the entry-point fallback to join-capable types.
Line 243 returns the first entry point with any http(s) URL. Google supplies
entryPointTypevaluesvideo,phone,sip, andmore. Amoreentry point is an https link to a "more phone numbers" page, not a join link. That URL then becomesmeetingURL, sohasMeetingLinkreportstrueand the link-only meeting alert offers a page that cannot join the call.Consider excluding
morefrom the fallback.♻️ Suggested change
- return entries.lazy.compactMap { CalendarEventItem.safeWebURL($0.url) }.first + return entries.lazy + .filter { $0.entryPointType != "more" } + .compactMap { CalendarEventItem.safeWebURL($0.url) } + .first🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Dayline/Services/CalendarService.swift` around lines 232 - 243, Restrict the fallback in conferenceURL to join-capable entry points by excluding entries whose entryPointType is "more" before selecting a safe URL. Preserve the existing preference for hangoutLink and video entry points, and keep the fallback limited to valid HTTP(S) URLs.Sources/Dayline/Stores/StatusStore.swift (2)
2022-2057: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winShare the draft normalization between the mock path and the live path.
This mock branch reimplements the validation and all-day normalization that
AppleCalendarService.createEventperforms inSources/Dayline/Services/AppleCalendarService.swiftat lines 46-70. The two copies can drift.The copies already differ in error precedence. The live path throws
missingTitlebefore it checks the calendar. This mock path checks the calendar first and the title last. A draft with an empty title and an unwritable calendar therefore reports a different error in mock mode than in production, which weakens the mock as a test surrogate forTests/DaylineTests/AppleCalendarEventCreationTests.swift.Consider extracting one static helper that validates the title and returns the normalized start and end dates, then calling it from both paths.
♻️ Sketch of the shared helper
// Sources/Dayline/Services/AppleCalendarService.swift extension AppleCalendarService { /// Validates a draft and returns its trimmed title with normalized event bounds. static func normalizedDraft( _ draft: AppleCalendarEventCreateDraft ) throws -> (title: String, startDate: Date, endDate: Date) { let title = draft.title.trimmingCharacters(in: .whitespacesAndNewlines) guard !title.isEmpty else { throw AppleCalendarServiceError.missingTitle } guard draft.isAllDay else { guard draft.endDate > draft.startDate else { throw AppleCalendarServiceError.invalidDateRange } return (title, draft.startDate, draft.endDate) } let startDay = Calendar.current.startOfDay(for: draft.startDate) let selectedEndDay = Calendar.current.startOfDay(for: draft.endDate) guard selectedEndDay >= startDay else { throw AppleCalendarServiceError.invalidDateRange } let endDay = Calendar.current.date(byAdding: .day, value: 1, to: selectedEndDay) ?? selectedEndDay.addingTimeInterval(24 * 60 * 60) return (title, startDay, endDay) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Dayline/Stores/StatusStore.swift` around lines 2022 - 2057, Extract a shared static draft-normalization helper on AppleCalendarService that validates the trimmed title and date range, returning the normalized title, startDate, and endDate. Call it from both AppleCalendarService.createEvent and StatusStore.createAppleCalendarEvent before mock-specific calendar validation, preserving missingTitle-first error precedence and removing the duplicated mock normalization logic.
4054-4061: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract the shared day-boundary computation.
Lines 4054-4061 and lines 4188-4195 compute
todayStart,tomorrowStart, anddayAfterTomorrowwith identical code and identical fallbacks. Lines 4158-4166 and lines 4196-4205 also repeat the same four limit arguments.Consider one private helper that returns the three boundaries, and one that returns the capped sections for a given event pool. This keeps the two agenda paths from drifting when a boundary rule changes.
Also applies to: 4143-4143, 4154-4166, 4176-4177, 4188-4209, 4297-4298
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Dayline/Stores/StatusStore.swift` around lines 4054 - 4061, Extract the duplicated day-boundary calculation used by the two agenda paths into a private helper returning todayStart, tomorrowStart, and dayAfterTomorrow, preserving the existing calendar fallbacks. Also extract the repeated four limit arguments around the agenda section-building logic into a helper that accepts the event pool and returns the capped sections, then update both paths to use these helpers.Sources/Dayline/Views/StatusMenuView.swift (1)
1562-1580: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExplain the disabled state of the single-provider control.
Line 1572 disables the button when Apple Calendar is connected but no writable calendar exists. Line 1573 still shows "New Apple Calendar event". The user sees a dead control with no reason.
Consider a help string that names the cause when the control is disabled.
♻️ Suggested change
- .help(store.appleCalendarConnected ? "New Apple Calendar event" : "Open Google Calendar") + .help(helpText)/// Explains the control, including why Apple event creation is unavailable. private var helpText: String { guard store.appleCalendarConnected else { return "Open Google Calendar" } return store.canCreateAppleCalendarEvent ? "New Apple Calendar event" : "Enable a writable Apple calendar in Settings to create events" }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Dayline/Views/StatusMenuView.swift` around lines 1562 - 1580, Update the help text for the calendar creation Button to explain the disabled Apple Calendar state: retain “Open Google Calendar” when disconnected, use “New Apple Calendar event” when creation is available, and provide a Settings-oriented writable-calendar message when store.canCreateAppleCalendarEvent is false. Apply this computed text to the existing help modifier.Tests/DaylineTests/MeetingAlertTests.swift (1)
203-208: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReport the failing URL in the recognition assertions.
allSatisfycollapses 22 positive cases and 40 negative cases into two boolean results. When a case regresses, the failure names only the array. Diagnosis then requires manual bisection.Iterate instead, so the report contains the offending URL.
♻️ Suggested change
- `#expect`(validURLs.allSatisfy { - CalendarEventItem.recognizedMeetingURL(URL(string: $0)) != nil - }) - `#expect`(nonMeetingURLs.allSatisfy { - CalendarEventItem.recognizedMeetingURL(URL(string: $0)) == nil - }) + for candidate in validURLs { + `#expect`(CalendarEventItem.recognizedMeetingURL(URL(string: candidate)) != nil, "\(candidate)") + } + for candidate in nonMeetingURLs { + `#expect`(CalendarEventItem.recognizedMeetingURL(URL(string: candidate)) == nil, "\(candidate)") + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Tests/DaylineTests/MeetingAlertTests.swift` around lines 203 - 208, Replace the allSatisfy-based assertions in the meeting URL recognition tests with per-URL iterations, asserting each positive URL is recognized and each negative URL is rejected so failures identify the offending URL.Sources/Dayline/Models/CalendarEventItem.swift (1)
138-148: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueTighten the generic Webex path check.
hasMeetingPathaccepts any path that contains ameetorjoincomponent anywhere. On any*.webex.comhost, marketing paths such as/pricing/join-a-meetingor/products/meetthen resolve to a meeting link. The other providers in this function require a positional match, so this branch is the loosest one.Consider requiring the component position, as the sibling branches do.
♻️ Suggested tightening
- let hasMeetingPath = path.contains("meet") || path.contains("join") + let hasMeetingPath = path.count >= 2 + && ["meet", "join"].contains(path[0]) + && !path[1].isEmptyNote: this changes behavior for
https://company.webex.com/company/j.php?MTID=…only throughhasLegacyJoin, which still applies. Verify the existing positive fixtures inTests/DaylineTests/MeetingAlertTests.swiftstill pass, in particularhttps://company.webex.com/meet/alex.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Sources/Dayline/Models/CalendarEventItem.swift` around lines 138 - 148, In the Webex host branch of the calendar-event URL logic, tighten hasMeetingPath so it matches meet or join only in the expected positional path component rather than anywhere in path. Preserve hasGuestJoinPath and hasLegacyJoin behavior, including legacy j.php URLs with a valid MTID, and keep /meet/alex recognized.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@Sources/Dayline/Stores/StatusStore.swift`:
- Around line 4083-4091: Update the Apple fetch window in the closure
initializing appleFetchTask to pass todayStart, rather than now, as the start
argument to appleCalendarService.events. Keep the existing enabled-calendar
filtering, cancellation checks, and dayAfterTomorrow end boundary unchanged.
---
Nitpick comments:
In `@Sources/Dayline/Models/CalendarEventItem.swift`:
- Around line 138-148: In the Webex host branch of the calendar-event URL logic,
tighten hasMeetingPath so it matches meet or join only in the expected
positional path component rather than anywhere in path. Preserve
hasGuestJoinPath and hasLegacyJoin behavior, including legacy j.php URLs with a
valid MTID, and keep /meet/alex recognized.
In `@Sources/Dayline/Services/CalendarService.swift`:
- Around line 232-243: Restrict the fallback in conferenceURL to join-capable
entry points by excluding entries whose entryPointType is "more" before
selecting a safe URL. Preserve the existing preference for hangoutLink and video
entry points, and keep the fallback limited to valid HTTP(S) URLs.
In `@Sources/Dayline/Stores/StatusStore.swift`:
- Around line 2022-2057: Extract a shared static draft-normalization helper on
AppleCalendarService that validates the trimmed title and date range, returning
the normalized title, startDate, and endDate. Call it from both
AppleCalendarService.createEvent and StatusStore.createAppleCalendarEvent before
mock-specific calendar validation, preserving missingTitle-first error
precedence and removing the duplicated mock normalization logic.
- Around line 4054-4061: Extract the duplicated day-boundary calculation used by
the two agenda paths into a private helper returning todayStart, tomorrowStart,
and dayAfterTomorrow, preserving the existing calendar fallbacks. Also extract
the repeated four limit arguments around the agenda section-building logic into
a helper that accepts the event pool and returns the capped sections, then
update both paths to use these helpers.
In `@Sources/Dayline/Views/StatusMenuView.swift`:
- Around line 1562-1580: Update the help text for the calendar creation Button
to explain the disabled Apple Calendar state: retain “Open Google Calendar” when
disconnected, use “New Apple Calendar event” when creation is available, and
provide a Settings-oriented writable-calendar message when
store.canCreateAppleCalendarEvent is false. Apply this computed text to the
existing help modifier.
In `@Tests/DaylineTests/MeetingAlertTests.swift`:
- Around line 203-208: Replace the allSatisfy-based assertions in the meeting
URL recognition tests with per-URL iterations, asserting each positive URL is
recognized and each negative URL is rejected so failures identify the offending
URL.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: f052de82-cd11-4220-896e-8048d4c14248
📒 Files selected for processing (29)
README.mdSources/Dayline/App/DaylineApp.swiftSources/Dayline/Models/AppleCalendarSource.swiftSources/Dayline/Models/CalendarEventItem.swiftSources/Dayline/Models/MenuControlID.swiftSources/Dayline/Services/AppleCalendarService.swiftSources/Dayline/Services/CalendarService.swiftSources/Dayline/Stores/StatusStore.swiftSources/Dayline/Support/AppleCalendarEventEditorWindowPresenter.swiftSources/Dayline/Support/MockData.swiftSources/Dayline/Views/AppleCalendarEventEditorView.swiftSources/Dayline/Views/AppleReminderEditorView.swiftSources/Dayline/Views/GraphicalDatePicker.swiftSources/Dayline/Views/LinearIssueEditorView.swiftSources/Dayline/Views/MeetingAlertView.swiftSources/Dayline/Views/NoteEditorView.swiftSources/Dayline/Views/PreviewPopovers.swiftSources/Dayline/Views/Settings/CalendarSettingsTab.swiftSources/Dayline/Views/Settings/SettingsTab.swiftSources/Dayline/Views/StatusMenuView.swiftTests/DaylineTests/AppleCalendarEventCreationTests.swiftTests/DaylineTests/CalendarEventItemTests.swiftTests/DaylineTests/MeetingAlertTests.swiftTests/DaylineTests/MockDataTests.swiftUITests/DaylineUITests/DaylineUITests.swiftscript/build_and_run.shscript/build_mock_and_run.shscript/package_release.shwebsite/src/routes/privacy.tsx
|
APIError: You've reached your usage limit for this billing cycle. Your quota will be refreshed in the next cycle. To continue now, purchase extra usage or upgrade your plan: https://www.kimi.com/code/#pricing |
|
APIError: You've reached your usage limit for this billing cycle. Your quota will be refreshed in the next cycle. To continue now, purchase extra usage or upgrade your plan: https://www.kimi.com/code/#pricing |
|
APIError: You've reached your usage limit for this billing cycle. Your quota will be refreshed in the next cycle. To continue now, purchase extra usage or upgrade your plan: https://www.kimi.com/code/#pricing |
|
APIError: You've reached your usage limit for this billing cycle. Your quota will be refreshed in the next cycle. To continue now, purchase extra usage or upgrade your plan: https://www.kimi.com/code/#pricing |
Summary\n\n- add native Apple Calendar event creation with the shared graphical date picker\n- add optional all-day calendar rows and correct Today/Tomorrow partitioning\n- add link-only full-screen meeting alerts with safe meeting-link provenance\n- speed up the native UI suite while preserving semantic actions and typed Markdown coverage\n- update privacy and permission copy for Apple Calendar event creation\n\n## Validation\n\n- Swift: 125/125 passed\n- Native UI: 16/16 passed in 282.83 seconds\n- Website production build: passed\n- Release build and notarization pipeline checks: passed\n- Secret scan and whitespace checks: clean\n- Final Codex full-branch review: clean at P0-P2\n\n## Review availability\n\n- CodeRabbit completed earlier passes; its final rerun was unavailable due the free OSS rate limit\n- OpenCode Kimi was unavailable due its billing-cycle usage limit\n- OpenCode Grok was unavailable after repeated provider server errors\n\n## Accepted test tradeoff\n\nUI-test mode disables macOS spellcheck, grammar checking, and automatic correction to avoid nondeterministic correction-panel stalls. Exact typed Markdown and formatting semantics remain covered; production OS text-assistance behavior itself is not exercised end to end.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation