Files
ScarriffleandClaude Opus 5 6e981e7aea chore(repo): iOS-App als ios/ ins Hauptrepo uebernehmen
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>
2026-09-14 09:20:12 +02:00

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)
}
}
}