iOS: localization fixes, per-calendar reminders, widget polish
C1 — Localization: route the remaining hardcoded German strings through L10n (LoginView, ServerSetupView, SettingsView email, EventDetailSheet) so "System Default" + English device language shows fully English text. C2 — Per-calendar reminders: parse the new reminders_enabled flag on every calendar type; CalendarStore persists a reminderDisabledKeys set and passes it to NotificationScheduler, which skips events of muted calendars (default and per-event reminders). Filter sheet gains a per-calendar reminder toggle (leading swipe + bell.slash indicator), reconciled from the server and synced back via PUT. C3 — Widgets: - Shared WidgetTime.range helper; Today / Today & Tomorrow / Three Days / Up Next now show start–end instead of only the start time. - This Week: show up to 6 events per day (was 3) to use the height. - Two Weeks: mini event-title pills instead of bare dots. - Two Months: weeks expand to fill the column (no more empty lower third). - Day & Events: smaller header/strip/rows so content stops clipping. - Next 5 days → Next 7 days (range + labels), higher row cap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,8 @@ struct CalendarFilterSheet: View {
|
||||
@State private var isLoading = true
|
||||
@State private var hidden: Set<String> = []
|
||||
@State private var banished: Set<String> = []
|
||||
/// Calendars whose events do not generate reminder notifications.
|
||||
@State private var reminderDisabled: Set<String> = []
|
||||
/// All non-banished keys discovered during load — used by bulk show/hide.
|
||||
@State private var allKeys: Set<String> = []
|
||||
/// Group-mode: the active group's full detail (members + colours) and the
|
||||
@@ -155,12 +157,27 @@ struct CalendarFilterSheet: View {
|
||||
.foregroundStyle(isVisible ? .primary : .secondary)
|
||||
.strikethrough(!isVisible, color: .secondary)
|
||||
Spacer()
|
||||
if reminderDisabled.contains(key) {
|
||||
Image(systemName: "bell.slash")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Image(systemName: isVisible ? "eye" : "eye.slash")
|
||||
.foregroundStyle(isVisible ? Color.accentColor : .secondary)
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.swipeActions(edge: .leading, allowsFullSwipe: false) {
|
||||
let disabled = reminderDisabled.contains(key)
|
||||
Button {
|
||||
toggleReminders(forKey: key)
|
||||
} label: {
|
||||
Label(L10n.t(disabled ? "filter.reminders_on" : "filter.reminders_off", appLang),
|
||||
systemImage: disabled ? "bell" : "bell.slash")
|
||||
}
|
||||
.tint(.orange)
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
|
||||
Button(role: .destructive) {
|
||||
hidden.remove(key)
|
||||
@@ -173,6 +190,17 @@ struct CalendarFilterSheet: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Flip a calendar's reminder mute, persist locally + on the server, reschedule.
|
||||
private func toggleReminders(forKey key: String) {
|
||||
let nowDisabled = !reminderDisabled.contains(key)
|
||||
if nowDisabled { reminderDisabled.insert(key) } else { reminderDisabled.remove(key) }
|
||||
store.setReminderDisabled(key, disabled: nowDisabled)
|
||||
if let parsed = CalendarStore.parseCalendarKey(key) {
|
||||
Task { try? await api.setCalendarRemindersEnabled(
|
||||
source: parsed.source, calendarId: parsed.id, enabled: !nowDisabled) }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: – Group overlay filter (hide individual members / the group calendar)
|
||||
|
||||
@ViewBuilder
|
||||
@@ -250,6 +278,19 @@ struct CalendarFilterSheet: View {
|
||||
store.setBanishedCalendars(b)
|
||||
banished = b
|
||||
|
||||
// Reconcile reminder-muted state from the server's reminders_enabled flags.
|
||||
var rd = Set<String>()
|
||||
func applyReminders(_ source: String, _ id: Int, _ enabled: Bool) {
|
||||
if !enabled { rd.insert(CalendarStore.calendarKey(source: source, calendarId: "\(id)")) }
|
||||
}
|
||||
for cal in localCalendars { applyReminders("local", cal.id, cal.remindersEnabled) }
|
||||
for acc in caldavAccounts { for cal in acc.calendars ?? [] { applyReminders("caldav", cal.id, cal.remindersEnabled ?? true) } }
|
||||
for sub in icalSubs { applyReminders("ical", sub.id, sub.remindersEnabled ?? true) }
|
||||
for acc in googleAccounts { for cal in acc.calendars ?? [] { applyReminders("google", cal.id, cal.remindersEnabled ?? true) } }
|
||||
for acc in haAccounts { for cal in acc.calendars ?? [] { applyReminders("homeassistant", cal.id, cal.remindersEnabled) } }
|
||||
store.setReminderDisabledKeys(rd)
|
||||
reminderDisabled = rd
|
||||
|
||||
var keys = Set<String>()
|
||||
for cal in localCalendars {
|
||||
keys.insert(CalendarStore.calendarKey(source: "local", calendarId: "\(cal.id)"))
|
||||
|
||||
Reference in New Issue
Block a user