Calendarr-IOS wird mit vollstaendiger Historie als Unterverzeichnis ios/ eingebunden. Damit liegen Server, Web, Android und iOS in einem gemeinsamen Repo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
157 lines
5.3 KiB
Swift
157 lines
5.3 KiB
Swift
import SwiftUI
|
|
import WidgetKit
|
|
import CalendarrCore
|
|
|
|
/// Attaches the curved bezel label that a face with a bezel slot — Infograph —
|
|
/// renders across the top, where the tick marks otherwise are. Faces without
|
|
/// that slot ignore it, so it costs nothing to always provide.
|
|
private extension View {
|
|
func eventBezelLabel(_ entry: WatchComplicationEntry) -> some View {
|
|
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
|
return widgetLabel {
|
|
Text(WatchComplicationSupport.eventLabel(next, at: entry.date, language: entry.language))
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A draining ring plus the remaining time.
|
|
struct CountdownComplicationView: View {
|
|
let entry: WatchComplicationEntry
|
|
@Environment(\.widgetFamily) private var family
|
|
|
|
var body: some View {
|
|
let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date)
|
|
switch family {
|
|
case .accessoryInline:
|
|
inline(next)
|
|
default:
|
|
circular(next)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func circular(_ next: SnapshotEvent?) -> some View {
|
|
if let next {
|
|
let target = WatchComplicationSupport.countdownTarget(next, at: entry.date)
|
|
Gauge(value: WatchComplicationSupport.gaugeFraction(to: target, at: entry.date)) {
|
|
Image(systemName: "calendar")
|
|
} currentValueLabel: {
|
|
Text(WatchComplicationSupport.compactCountdown(to: target, at: entry.date,
|
|
language: entry.language))
|
|
.font(.system(size: 13))
|
|
.lineLimit(1)
|
|
.minimumScaleFactor(0.6)
|
|
}
|
|
.gaugeStyle(.accessoryCircularCapacity)
|
|
.widgetAccentable()
|
|
.eventBezelLabel(entry)
|
|
} else {
|
|
ZStack {
|
|
AccessoryWidgetBackground()
|
|
Image(systemName: "calendar").font(.system(size: 18))
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func inline(_ next: SnapshotEvent?) -> some View {
|
|
if let next {
|
|
Label {
|
|
Text(WatchComplicationSupport.countdownTarget(next, at: entry.date), style: .timer)
|
|
} icon: {
|
|
Image(systemName: "timer")
|
|
}
|
|
} else {
|
|
Label(WatchL10n.t("watch.no_events", entry.language), systemImage: "calendar")
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Weekday and day number, the way a date is actually read. The ring appears
|
|
/// only while an appointment is running, and then shows how far through it we
|
|
/// are — an empty track around an idle day is noise, so there is no Gauge at
|
|
/// all in that case.
|
|
struct DateComplicationView: View {
|
|
let entry: WatchComplicationEntry
|
|
@Environment(\.widgetFamily) private var family
|
|
|
|
private var day: Int {
|
|
Calendar(identifier: .gregorian).component(.day, from: entry.date)
|
|
}
|
|
|
|
private var weekday: String {
|
|
WatchComplicationSupport.weekdayAbbreviation(entry.date, language: entry.language)
|
|
}
|
|
|
|
var body: some View {
|
|
switch family {
|
|
case .accessoryInline:
|
|
Label("\(weekday) \(day)", systemImage: "calendar")
|
|
default:
|
|
circular
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private var circular: some View {
|
|
if let running = WatchComplicationSupport.runningEvent(in: entry.snapshot, at: entry.date) {
|
|
Gauge(value: WatchComplicationSupport.progress(of: running, at: entry.date)) {
|
|
Text(weekday)
|
|
} currentValueLabel: {
|
|
dateStack
|
|
}
|
|
.gaugeStyle(.accessoryCircularCapacity)
|
|
.eventBezelLabel(entry)
|
|
} else {
|
|
ZStack {
|
|
AccessoryWidgetBackground()
|
|
dateStack
|
|
}
|
|
.eventBezelLabel(entry)
|
|
}
|
|
}
|
|
|
|
private var dateStack: some View {
|
|
VStack(spacing: -2) {
|
|
Text(weekday)
|
|
.font(.system(size: 11, weight: .semibold))
|
|
.widgetAccentable()
|
|
Text("\(day)")
|
|
.font(.system(size: 23, weight: .semibold))
|
|
.lineLimit(1)
|
|
.minimumScaleFactor(0.7)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// How many appointments today. Says "busy", not "what".
|
|
struct TodayCountComplicationView: View {
|
|
let entry: WatchComplicationEntry
|
|
@Environment(\.widgetFamily) private var family
|
|
|
|
private var count: Int {
|
|
WatchComplicationSupport.todayCount(in: entry.snapshot, at: entry.date)
|
|
}
|
|
|
|
var body: some View {
|
|
switch family {
|
|
case .accessoryInline:
|
|
Label("\(count) \(WatchL10n.t("watch.events_today", entry.language))",
|
|
systemImage: "calendar")
|
|
default:
|
|
Gauge(value: WatchComplicationSupport.todayRemainingFraction(in: entry.snapshot,
|
|
at: entry.date)) {
|
|
Image(systemName: "calendar")
|
|
} currentValueLabel: {
|
|
Text("\(count)")
|
|
.font(.system(size: 20, weight: .semibold))
|
|
.lineLimit(1)
|
|
.minimumScaleFactor(0.6)
|
|
}
|
|
.gaugeStyle(.accessoryCircularCapacity)
|
|
.widgetAccentable()
|
|
.eventBezelLabel(entry)
|
|
}
|
|
}
|
|
}
|