Add the watch app and its eight complications
The watch receives the phone's payload over WatchConnectivity and writes it into its own App Group container through the same SnapshotStore, so there is one wire format and one reader. The agenda, the detail screen and the filter all read that; the complications read what the app last wrote, since an extension cannot run a WCSession of its own. Complication timelines land on event boundaries rather than a schedule, so the next appointment flips in the exact minute it changes without spending refresh budget. CalendarrWatchShared holds what both watch targets need, attached to each the way Shared/ is attached to the app and the widgets. recommendations() is implemented because watchOS requires it — iOS has a default, the watch does not, since the complication gallery has to be populated. Found by building, not by reading. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6a7afe0647
commit
f9bc1965a5
@@ -0,0 +1,23 @@
|
||||
import Foundation
|
||||
import CalendarrCore
|
||||
|
||||
/// Which calendars this watch hides, on top of what the phone already filtered.
|
||||
///
|
||||
/// The snapshot arrives with hidden and banished calendars already removed, so
|
||||
/// this narrows further and can never contradict the phone. It lives in the App
|
||||
/// Group so one toggle in the app affects every complication at once.
|
||||
enum WatchFilterStore {
|
||||
private static let key = "watchHiddenCalendarKeys"
|
||||
|
||||
private static var defaults: UserDefaults? {
|
||||
UserDefaults(suiteName: CalendarrAppGroup.current)
|
||||
}
|
||||
|
||||
static func load() -> Set<String> {
|
||||
Set(defaults?.stringArray(forKey: key) ?? [])
|
||||
}
|
||||
|
||||
static func save(_ keys: Set<String>) {
|
||||
defaults?.set(keys.sorted(), forKey: key)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import Foundation
|
||||
|
||||
/// German and English strings for the watch app and its complications.
|
||||
///
|
||||
/// A local copy rather than a shared one: `CalendarrCore` is deliberately
|
||||
/// Foundation-only and holds no presentation concerns, and the iOS `WidgetL10n`
|
||||
/// belongs to a target the watch cannot link against.
|
||||
enum WatchL10n {
|
||||
static func t(_ key: String, _ stored: String) -> String {
|
||||
strings[resolve(stored)]?[key] ?? strings["en"]?[key] ?? key
|
||||
}
|
||||
|
||||
static func locale(_ stored: String) -> Locale {
|
||||
Locale(identifier: resolve(stored) == "de" ? "de_DE" : "en_US")
|
||||
}
|
||||
|
||||
private static func resolve(_ stored: String) -> String {
|
||||
if stored == "de" || stored == "en" { return stored }
|
||||
let preferred = Locale.preferredLanguages.first ?? "en"
|
||||
return preferred.lowercased().hasPrefix("de") ? "de" : "en"
|
||||
}
|
||||
|
||||
private static let strings: [String: [String: String]] = [
|
||||
"de": [
|
||||
"watch.title": "Calendarr",
|
||||
"watch.calendars": "Kalender",
|
||||
"watch.allday": "Ganztägig",
|
||||
"watch.no_events": "Keine Termine",
|
||||
"watch.today": "Heute",
|
||||
"watch.tomorrow": "Morgen",
|
||||
"watch.events_today": "Termine",
|
||||
"watch.sign_in": "Auf dem iPhone anmelden",
|
||||
"watch.update": "Watch-App aktualisieren",
|
||||
"watch.waiting": "Warte auf Daten vom iPhone",
|
||||
"watch.unreadable": "Daten nicht lesbar",
|
||||
"watch.as_of": "Stand",
|
||||
"watch.no_information": "Keine Daten nach",
|
||||
],
|
||||
"en": [
|
||||
"watch.title": "Calendarr",
|
||||
"watch.calendars": "Calendars",
|
||||
"watch.allday": "All-day",
|
||||
"watch.no_events": "No events",
|
||||
"watch.today": "Today",
|
||||
"watch.tomorrow": "Tomorrow",
|
||||
"watch.events_today": "events",
|
||||
"watch.sign_in": "Sign in on your iPhone",
|
||||
"watch.update": "Update the watch app",
|
||||
"watch.waiting": "Waiting for data from your iPhone",
|
||||
"watch.unreadable": "Data unreadable",
|
||||
"watch.as_of": "As of",
|
||||
"watch.no_information": "No data after",
|
||||
],
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user