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>
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user