fix(web): hide edit/delete for others' events; persistent hide for shared calendars

Bug 1 — a calendar shared with me stayed visible after unchecking it: the hide
was a one-shot cache filter the server undid on refetch. Add a per-device
hidden set (localStorage 'hiddenLocalCalendars'), honoured in filterEvents
(normal view) and used to drive the checkbox state, so it survives refetch/reload.

Bug 2 — in the group combined view, other members' events showed edit/delete and
403'd on save. The combined endpoint now emits read_only (editable = the group
calendar OR my own events), via a read_only param threaded through
build_local_event_dict/expand_recurring_local. The event popup and edit modal now
treat read_only events as read-only (copy still allowed). Test added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-07 10:11:46 +02:00
parent ec85a5b5f3
commit f844ded57d
5 changed files with 80 additions and 10 deletions

View File

@@ -380,6 +380,9 @@ def combined_events(
def emit_calendar(cal: models.LocalCalendar, owner_id: int, is_group: bool):
owner_user = name_cache.get(owner_id)
owner = {"id": owner_id, "display_name": owner_user}
# Editable by the requester iff it's the shared group calendar (all members
# may write) or the requester's own calendar; everyone else's is read-only.
read_only = not (is_group or owner_id == current_user.id)
events = (
db.query(models.LocalEvent)
.filter(
@@ -405,9 +408,9 @@ def combined_events(
creator = {"id": None, "display_name": f"{ev.creator_name_external} (importiert)"}
if ev.rrule:
built = expand_recurring_local(ev, cal, start_dt, end_dt, creator=creator, owner=owner, is_group_event=is_group)
built = expand_recurring_local(ev, cal, start_dt, end_dt, creator=creator, owner=owner, is_group_event=is_group, read_only=read_only)
else:
built = [build_local_event_dict(ev, cal, rrule=None, creator=creator, owner=owner, is_group_event=is_group)]
built = [build_local_event_dict(ev, cal, rrule=None, creator=creator, owner=owner, is_group_event=is_group, read_only=read_only)]
for b in built:
if ev.is_private and creator_owner_id != current_user.id and visibility_for(creator_owner_id) == "busy":