feat(sharing): show shared calendars read-only (parity with server/Android)
- Parse the server's new read_only flag on events. - Filter sheet: a calendar shared with me is listed under the owner's name (sharedBy) with a lock icon when I only have read access. - loadWritableCalendars: exclude read-only shared calendars so they no longer appear in the event editor (a save would 403). The group-title prefix removal is server-side and needs no iOS change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -65,6 +65,8 @@ struct CalEvent: Identifiable, Hashable {
|
||||
var displayTitle: String? = nil
|
||||
// Reminder offsets in minutes-before-start (0 = at start). Local events only.
|
||||
var reminders: [Int] = []
|
||||
// True for events from a calendar shared with the user read-only.
|
||||
var readOnly: Bool = false
|
||||
|
||||
// Group view supplies a server-resolved colour; otherwise per-event then calendar colour.
|
||||
var effectiveColor: String { displayColor ?? color ?? calendarColor }
|
||||
@@ -107,7 +109,8 @@ struct CalEvent: Identifiable, Hashable {
|
||||
isGroupEvent: json["is_group_event"] as? Bool ?? false,
|
||||
displayColor: (json["display_color"] as? String).flatMap { $0.isEmpty ? nil : $0 },
|
||||
displayTitle: (json["display_title"] as? String).flatMap { $0.isEmpty ? nil : $0 },
|
||||
reminders: (json["reminders"] as? [Int]) ?? (json["reminders"] as? [Any])?.compactMap { ($0 as? Int) ?? Int("\($0)") } ?? []
|
||||
reminders: (json["reminders"] as? [Int]) ?? (json["reminders"] as? [Any])?.compactMap { ($0 as? Int) ?? Int("\($0)") } ?? [],
|
||||
readOnly: json["read_only"] as? Bool ?? false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,7 +597,9 @@ class CalendarStore {
|
||||
async let haCals = (try? await api.getHACalendars()) ?? []
|
||||
|
||||
var result: [WritableCalendar] = []
|
||||
for cal in await localCals {
|
||||
// Skip read-only shared calendars — offering them in the event editor
|
||||
// only leads to a 403 on save. Own + read_write (incl. group) stay.
|
||||
for cal in await localCals where cal.owned || cal.permission == "read_write" {
|
||||
result.append(WritableCalendar(id: "local-\(cal.id)", name: cal.name, color: cal.color, source: "local", numericId: cal.id))
|
||||
}
|
||||
for acc in await caldavAccs where acc.enabled {
|
||||
|
||||
@@ -45,8 +45,12 @@ struct CalendarFilterSheet: View {
|
||||
if !visibleLocals.isEmpty {
|
||||
Section(L10n.t("accounts.local.header", appLang)) {
|
||||
ForEach(visibleLocals) { cal in
|
||||
row(name: cal.name, colorHex: cal.color,
|
||||
key: CalendarStore.calendarKey(source: "local", calendarId: "\(cal.id)"))
|
||||
// A calendar shared with me is shown under the owner's
|
||||
// name and flagged read-only when I can't write to it.
|
||||
row(name: cal.owned ? cal.name : (cal.sharedBy ?? cal.name),
|
||||
colorHex: cal.color,
|
||||
key: CalendarStore.calendarKey(source: "local", calendarId: "\(cal.id)"),
|
||||
readOnly: !cal.owned && cal.permission != "read_write")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +144,7 @@ struct CalendarFilterSheet: View {
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func row(name: String, colorHex: String, key: String) -> some View {
|
||||
private func row(name: String, colorHex: String, key: String, readOnly: Bool = false) -> some View {
|
||||
let isVisible = !hidden.contains(key)
|
||||
Button {
|
||||
if isVisible { hidden.insert(key) } else { hidden.remove(key) }
|
||||
@@ -156,6 +160,11 @@ struct CalendarFilterSheet: View {
|
||||
Text(name)
|
||||
.foregroundStyle(isVisible ? .primary : .secondary)
|
||||
.strikethrough(!isVisible, color: .secondary)
|
||||
if readOnly {
|
||||
Image(systemName: "lock.fill")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
if reminderDisabled.contains(key) {
|
||||
Image(systemName: "bell.slash")
|
||||
|
||||
Reference in New Issue
Block a user