From 52040fff5327d51d5001ccfa6c0b3be3591041d7 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Mon, 15 Jun 2026 19:38:20 +0200 Subject: [PATCH] feat: show relative time in notifications + move default duration to profile settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Notifications now show "in 30 Min. · 10:00" instead of just the time, giving users immediate context about how soon the event is. Default event duration picker moved from the View section to right below the privacy setting, grouped under the calendar/profile settings area. Co-Authored-By: Claude Sonnet 4.6 --- .../Services/NotificationScheduler.swift | 13 ++++++++++- Calendarr iOS/Views/SettingsView.swift | 22 ++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/Calendarr iOS/Services/NotificationScheduler.swift b/Calendarr iOS/Services/NotificationScheduler.swift index 0a402b9..ab4810a 100644 --- a/Calendarr iOS/Services/NotificationScheduler.swift +++ b/Calendarr iOS/Services/NotificationScheduler.swift @@ -50,7 +50,10 @@ enum NotificationScheduler { for item in limited { let content = UNMutableNotificationContent() content.title = item.event.title - content.body = bodyText(item.event) + let minutes = Int(item.event.startDate.timeIntervalSince(item.fire) / 60) + let rel = relativeText(minutes) + let detail = bodyText(item.event) + content.body = detail.isEmpty ? rel : "\(rel) · \(detail)" content.sound = .default let comps = Calendar.current.dateComponents( [.year, .month, .day, .hour, .minute, .second], from: item.fire) @@ -60,6 +63,14 @@ enum NotificationScheduler { } } + private static func relativeText(_ minutes: Int) -> String { + if minutes < 60 { return "in \(minutes) Min." } + if minutes == 60 { return "in 1 Std." } + if minutes < 1440 { return "in \(minutes / 60) Std." } + if minutes == 1440 { return "morgen" } + return "in \(minutes / 1440) Tagen" + } + private static func bodyText(_ ev: CalEvent) -> String { var parts: [String] = [] if !ev.isAllDay { diff --git a/Calendarr iOS/Views/SettingsView.swift b/Calendarr iOS/Views/SettingsView.swift index c6c1605..97caa2b 100644 --- a/Calendarr iOS/Views/SettingsView.swift +++ b/Calendarr iOS/Views/SettingsView.swift @@ -39,6 +39,7 @@ struct SettingsView: View { Form { profilSection privatsphaereSection + termindauerSection benachrichtigungenSection geteilterKalenderSection liquidGlassSection @@ -147,6 +148,21 @@ struct SettingsView: View { } } + // MARK: – Standardtermindauer + + var termindauerSection: some View { + Section { + Picker(L10n.t("settings.default_duration", appLang), selection: $defaultEventDurationMinutes) { + ForEach([15, 30, 45, 60, 90, 120, 240], id: \.self) { m in + Text(ReminderOptions.durationLabel(m, appLang)).tag(m) + } + } + .onChange(of: defaultEventDurationMinutes) { _, _ in SettingsSync.push(api: api) } + } header: { + Text(L10n.t("settings.calview", appLang)) + } + } + // MARK: – Geteilter Kalender var geteilterKalenderSection: some View { @@ -361,12 +377,6 @@ struct SettingsView: View { } Toggle(L10n.t("settings.dimpast", appLang), isOn: $dimPastEvents) .tint(Color.accentColor) - Picker(L10n.t("settings.default_duration", appLang), selection: $defaultEventDurationMinutes) { - ForEach([15, 30, 45, 60, 90, 120, 240], id: \.self) { m in - Text(ReminderOptions.durationLabel(m, appLang)).tag(m) - } - } - .onChange(of: defaultEventDurationMinutes) { _, _ in SettingsSync.push(api: api) } } }