This repository has been archived on 2026-09-14. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Calendarr-IOS/CalendarrWatch/Views/WatchEventRow.swift
T
ScarriffleandClaude Opus 5 e5b3e549e8 Catch multi-day events that do not end on a day boundary
Requiring both ends to sit exactly on midnight was too strict. A holiday stored
as the 16th at 00:00 through the 21st at 01:00 slipped through and rendered as
a one-hour slot. Starting on a day boundary and running into a later day is
enough to call it all-day.

The agenda row now names the weekday on both sides when an event crosses a day.
Showing only "00:00 – 01:00" for something that runs from Wednesday to Monday
is true and useless.

The timezone handling above this is deliberately untouched: timed events show
the right time, so the UTC assumption for timestamps without an offset is not
what produced the odd hour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 14:52:52 +02:00

90 lines
3.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import SwiftUI
import CalendarrCore
/// One event: a colour bar for the calendar, the time range, the title.
struct WatchEventRow: View {
let event: SnapshotEvent
let language: String
var body: some View {
HStack(alignment: .top, spacing: 7) {
RoundedRectangle(cornerRadius: 2)
.fill(Color(watchHex: event.colorHex))
.frame(width: 3)
VStack(alignment: .leading, spacing: 1) {
Text(WatchEventFormat.timeRange(event, language: language))
.font(.system(size: 11))
.foregroundStyle(.secondary)
Text(event.title)
.font(.system(size: 14))
.lineLimit(2)
}
Spacer(minLength: 0)
}
.padding(.vertical, 2)
.fixedSize(horizontal: false, vertical: true)
}
}
extension Color {
/// Local copy of the hex initialiser: `CalendarrCore` is Foundation-only by
/// design, so colour parsing cannot live there.
init(watchHex hex: String) {
let cleaned = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var value: UInt64 = 0
Scanner(string: cleaned).scanHexInt64(&value)
guard cleaned.count == 6 else { self = .gray; return }
self.init(red: Double((value >> 16) & 0xFF) / 255,
green: Double((value >> 8) & 0xFF) / 255,
blue: Double(value & 0xFF) / 255)
}
}
enum WatchEventFormat {
static func timeRange(_ event: SnapshotEvent, language: String,
calendar: Calendar = Calendar(identifier: .gregorian)) -> String {
if event.isAllDay { return WatchL10n.t("watch.allday", language) }
let time = DateFormatter()
time.locale = WatchL10n.locale(language)
time.dateFormat = "HH:mm"
let start = time.string(from: event.start)
guard event.end > event.start else { return start }
// Without the weekday, an event running from Wednesday to Monday reads
// as a one-hour slot: "00:00 – 01:00" is true and useless.
if calendar.startOfDay(for: event.end) > calendar.startOfDay(for: event.start) {
let day = DateFormatter()
day.locale = WatchL10n.locale(language)
day.setLocalizedDateFormatFromTemplate("EEE")
return "\(day.string(from: event.start)) \(start) – "
+ "\(day.string(from: event.end)) \(time.string(from: event.end))"
}
return "\(start) – \(time.string(from: event.end))"
}
static func time(_ date: Date, language: String) -> String {
let formatter = DateFormatter()
formatter.locale = WatchL10n.locale(language)
formatter.dateFormat = "HH:mm"
return formatter.string(from: date)
}
/// "Heute", "Morgen", else "Do 11. Sep".
static func dayHeader(_ day: Date, language: String,
calendar: Calendar, now: Date = Date()) -> String {
if calendar.isDate(day, inSameDayAs: now) {
return WatchL10n.t("watch.today", language)
}
if let tomorrow = calendar.date(byAdding: .day, value: 1, to: calendar.startOfDay(for: now)),
calendar.isDate(day, inSameDayAs: tomorrow) {
return WatchL10n.t("watch.tomorrow", language)
}
let formatter = DateFormatter()
formatter.locale = WatchL10n.locale(language)
formatter.setLocalizedDateFormatFromTemplate("EEE d MMM")
return formatter.string(from: day)
}
}