feat(calendar): bootstrap race guard + os.Logger observability#27
Merged
Conversation
An EKEventStoreChanged notification firing during the await in requestAccessIfNeeded() could let handleStoreChanged complete the not-granted→granted setup before bootstrap resumes, causing it to unconditionally repeat the same setup. Short-circuit if authorization is already granted when the await returns.
feat(logging): os.Logger observability
Joining a meeting is the highest-value action available during startingSoon/inProgress, so surface it directly in the notch instead of requiring a context switch to Calendar. Detection is pure and https-only (Zoom /j//my/, Meet, Teams, Webex subdomains), preferring the event's URL field over location/notes.
Single-calendar selection was a v1 constraint; the legacy migration chain (array -> single -> set) shows multi-select was the original design intent, and the snapshot model already merges/sorts events across sources, so absorbing multiple calendars costs nothing there. Preferences.selectedCalendarIdentifiers replaces the single-value selection with a Set<String>, migrating both legacy keys (idempotent, preserves "never selected" vs "deliberately empty" distinction). SnapshotBuilder, CalendarManager, and EventProgressModel thread the Set through; SettingsView swaps radio rows for checkboxes.
Add os.Logger info lines in Preferences.migrateIfNeeded when a legacy-key migration actually writes the new key, restoring observability for diagnosing post-upgrade calendar selection changes. Add regression tests for single-vs-legacy-array precedence and empty legacy array handling.
The landing page has been bilingual FR/EN since launch, but the app itself was English-only. Follow system language per macOS convention: a String Catalog carries en/fr translations, threaded through SnapshotBuilder and the views, with a build tool plugin to compile it for swift build/test (SwiftPM's own build system copies .xcstrings verbatim instead of compiling it, unlike Xcode's build system).
SwiftPM's own build system doesn't compile .xcstrings catalogs, so the prior approach shelled out to xcstringstool via a custom build plugin and leaked the raw JSON catalog into the shipped app bundle. Convert the catalog to en.lproj/fr.lproj Localizable.strings, which SwiftPM processes natively, and drop the plugin entirely. Localized.string now resolves .lproj sub-bundles directly via Bundle(path:) for explicit non-current locales, deferring to Bundle.module's standard search order otherwise.
refactor(calendar): extract SnapshotBuilder
feat(panel): Join button for meeting links
feat(calendar): multi-calendar tracking
feat(l10n): French localization
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.
Why
Stack-continuation PR. #21 was merged into
mainwithout deletingfeat/authorization-recovery, so #22 (os.Logger) was merged into that branch instead of auto-retargeting tomain. As a result,mainis currently missing:d5639d9— fix(calendar): guard bootstrap against concurrent authorization recovery (race fix from feat(calendar): authorization recovery #21's review)090b1bb— feat(logging): os.Logger observability (feat(logging): os.Logger observability #22's content)This PR brings both to
mainand re-anchors the open stack (#23 → #24 → #25 → #26).Merge in order and check “Delete branch” at every merge — that's what makes GitHub retarget the next PR automatically:
feat/authorization-recovery) → refactor(calendar): extract SnapshotBuilder #23 retargets tomain