feat: show relative time in notifications + move default duration to profile settings

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 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-06-15 19:38:20 +02:00
parent cc3d16ddce
commit 52040fff53
2 changed files with 28 additions and 7 deletions

View File

@@ -50,7 +50,10 @@ enum NotificationScheduler {
for item in limited { for item in limited {
let content = UNMutableNotificationContent() let content = UNMutableNotificationContent()
content.title = item.event.title 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 content.sound = .default
let comps = Calendar.current.dateComponents( let comps = Calendar.current.dateComponents(
[.year, .month, .day, .hour, .minute, .second], from: item.fire) [.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 { private static func bodyText(_ ev: CalEvent) -> String {
var parts: [String] = [] var parts: [String] = []
if !ev.isAllDay { if !ev.isAllDay {

View File

@@ -39,6 +39,7 @@ struct SettingsView: View {
Form { Form {
profilSection profilSection
privatsphaereSection privatsphaereSection
termindauerSection
benachrichtigungenSection benachrichtigungenSection
geteilterKalenderSection geteilterKalenderSection
liquidGlassSection 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 // MARK: Geteilter Kalender
var geteilterKalenderSection: some View { var geteilterKalenderSection: some View {
@@ -361,12 +377,6 @@ struct SettingsView: View {
} }
Toggle(L10n.t("settings.dimpast", appLang), isOn: $dimPastEvents) Toggle(L10n.t("settings.dimpast", appLang), isOn: $dimPastEvents)
.tint(Color.accentColor) .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) }
} }
} }