feat(sharing): label shared personal calendars by owner + drop group title prefix
Two group/sharing display fixes, both server-side so every client benefits:
1. A personal calendar shared WITH a user showed only its raw name
("Persönlich"), indistinguishable from the user's own. The merge read now
relabels a shared *personal* calendar under the owner's display name (so
Guido's "Persönlich" reads as "Guido" for recipients) and adds read_only:true
when the share isn't read_write. Group calendars are excluded — they keep
their own name and stay writable for members.
2. The combined group view prefixed every foreign event with the owner's first
name ("Guido: …"). Each member already has a distinct display_color, so the
prefix was redundant. _decorate_title now returns the raw title; display_title
stays non-empty so clients' legacy prefix fallback never triggers. The change
takes effect on already-installed clients with no app update.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -352,6 +352,21 @@ def get_events(
|
||||
.all()
|
||||
) if readable_ids else []
|
||||
name_cache = {u.id: (u.display_name or u.username) for u in db.query(models.User).all()}
|
||||
# A personal calendar shared WITH the current user is relabelled under the
|
||||
# owner's name (so Guido's "Persönlich" reads as "Guido" for his mum) and
|
||||
# flagged read_only unless the share grants write. Group calendars are
|
||||
# excluded — they keep their own name and stay writable for members.
|
||||
shares_map = {
|
||||
s.calendar_id: s.permission
|
||||
for s in db.query(models.CalendarShare).filter(
|
||||
models.CalendarShare.user_id == current_user.id
|
||||
)
|
||||
}
|
||||
group_cal_ids = {
|
||||
r[0] for r in db.query(models.GroupCalendar.calendar_id).filter(
|
||||
models.GroupCalendar.calendar_id.in_(readable_ids)
|
||||
)
|
||||
} if readable_ids else set()
|
||||
# Cache each owner's private-event visibility (one lookup per owner, not per event).
|
||||
vis_cache: dict = {}
|
||||
|
||||
@@ -361,6 +376,13 @@ def get_events(
|
||||
return vis_cache[uid]
|
||||
|
||||
for local_cal in local_calendars:
|
||||
# Decoration for a personal calendar shared with (not owned by) me.
|
||||
is_shared_personal = (
|
||||
local_cal.user_id != current_user.id
|
||||
and local_cal.id not in group_cal_ids
|
||||
)
|
||||
shared_owner_name = name_cache.get(local_cal.user_id) if is_shared_personal else None
|
||||
shared_read_only = is_shared_personal and shares_map.get(local_cal.id) != "read_write"
|
||||
local_events = (
|
||||
db.query(models.LocalEvent)
|
||||
.filter(
|
||||
@@ -391,6 +413,10 @@ def get_events(
|
||||
else:
|
||||
built = [build_local_event_dict(ev, local_cal, rrule=None, creator=creator)]
|
||||
for b in built:
|
||||
if shared_owner_name:
|
||||
b["calendar_name"] = shared_owner_name
|
||||
if shared_read_only:
|
||||
b["read_only"] = True
|
||||
b = apply_event_privacy(
|
||||
b, owner_id=owner_id, is_private=is_priv,
|
||||
requester_id=current_user.id, visibility=visibility,
|
||||
|
||||
@@ -317,17 +317,12 @@ def _first_name(name: Optional[str]) -> str:
|
||||
|
||||
def _decorate_title(title: str, *, is_group: bool, creator: Optional[dict],
|
||||
owner: Optional[dict], me_id: int) -> str:
|
||||
"""Server-side display title for the combined view so every client (web,
|
||||
iOS, Android) renders identically: another member's / creator's first name
|
||||
is prefixed. No icon glyph is embedded — group icons are semantic keys the
|
||||
clients render as native vector icons, and group-calendar events are
|
||||
distinguished by their (group) colour. The raw `title` stays for editing."""
|
||||
if is_group:
|
||||
if creator and creator.get("id") is not None and creator.get("id") != me_id:
|
||||
return f"{_first_name(creator.get('display_name'))}: {title}"
|
||||
return title
|
||||
if owner and owner.get("id") is not None and owner.get("id") != me_id:
|
||||
return f"{_first_name(owner.get('display_name'))}: {title}"
|
||||
"""Server-side display title for the combined view. The former owner/creator
|
||||
first-name prefix ("Guido: …") was dropped: each member already has a
|
||||
distinct colour, so the prefix was redundant noise. We still return a
|
||||
non-empty `display_title` (== raw title) so the clients' legacy fallback —
|
||||
which rebuilds a prefix when `display_title` is empty — never kicks in.
|
||||
`display_color` continues to carry the per-person colour."""
|
||||
return title
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user