Move mode picker below device status bar; fix invisible calendar lines; add CalDAV calendar support
Build and push server image / build-and-push (push) Successful in 50s
Build and push server image / build-and-push (push) Successful in 50s
Calendar rule/grid lines were light gray, which dithers away to near-invisible on the 6-color e-ink palette -- now black. CalDAV accounts (Nextcloud, Fastmail, iCloud, ...) can now be linked alongside the existing single ICS subscription, since one account can expose several calendars. A frame's Calendar tab now lists calendars per person rather than one opt-in per person: your own row shows every calendar you have available with a full add/remove toggle, while other linked users' rows show only calendars they've included, toggleable off (mute) but not on -- only a calendar's owner can add it to a shared frame. FrameCalendar replaces the old single-boolean UserFrame.calendar_included; existing opt-ins are migrated forward.
This commit is contained in:
@@ -36,7 +36,7 @@ from ..image_pipeline import (
|
||||
render_preview_png,
|
||||
)
|
||||
from ..firmware import firmware_path, parse_app_version
|
||||
from ..models import BatteryLog, Frame, UserFrame
|
||||
from ..models import BatteryLog, Frame, FrameCalendar
|
||||
from .common import (
|
||||
FRAME_MODES,
|
||||
OVERDUE_FACTOR,
|
||||
@@ -409,33 +409,53 @@ def api_preview_rendered(frame: Frame = Depends(require_frame_view), db: Session
|
||||
return Response(content=png, media_type="image/png")
|
||||
|
||||
|
||||
class CalendarIncludedRequest(BaseModel):
|
||||
class CalendarSelectRequest(BaseModel):
|
||||
user_id: int
|
||||
calendar_key: str
|
||||
calendar_label: str = ""
|
||||
included: bool
|
||||
|
||||
|
||||
@router.post("/api/frames/{frame_id}/calendar-included")
|
||||
def api_calendar_included(
|
||||
body: CalendarIncludedRequest,
|
||||
@router.post("/api/frames/{frame_id}/calendar-select")
|
||||
def api_calendar_select(
|
||||
body: CalendarSelectRequest,
|
||||
request: Request,
|
||||
frame: Frame = Depends(require_frame_view), # view access only -- NOT require_frame_control
|
||||
db: Session = Depends(get_db),
|
||||
):
|
||||
"""A user's own opt-in into this frame's merged calendar (see
|
||||
UserFrame.calendar_included). Deliberately not require_frame_control:
|
||||
this is the toggling user's own data-sharing preference about their
|
||||
own calendar, not a frame setting its controller manages on someone
|
||||
else's behalf -- there's no target user_id in the request body by
|
||||
design, it always toggles the calling session's own row."""
|
||||
"""Include/exclude one calendar (calendar_key "ics" or
|
||||
"caldav:<href>", see FrameCalendar) on this frame. Deliberately not
|
||||
require_frame_control: adding your own calendar, or muting anyone's
|
||||
(including your own), is each viewer's own call, not something a
|
||||
frame's controller manages on someone else's behalf. The one-sided
|
||||
permission split lives here: turning a calendar ON requires being its
|
||||
owner (nobody can add someone else's calendar to a shared frame for
|
||||
them); turning one OFF only requires being linked to the frame at
|
||||
all, so anyone sharing the display can mute a calendar they'd rather
|
||||
not see there even if they don't own it."""
|
||||
user = require_user_api(request, db)
|
||||
row = db.get(UserFrame, (user.id, frame.id))
|
||||
if body.included and body.user_id != user.id:
|
||||
raise HTTPException(403, "Only a calendar's owner can add it to a frame")
|
||||
row = db.execute(
|
||||
select(FrameCalendar).where(
|
||||
FrameCalendar.frame_id == frame.id,
|
||||
FrameCalendar.user_id == body.user_id,
|
||||
FrameCalendar.calendar_key == body.calendar_key,
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
if row is None:
|
||||
raise HTTPException(404, "Not linked to this frame")
|
||||
row.calendar_included = body.included
|
||||
if not body.included:
|
||||
raise HTTPException(404, "Not currently included on this frame")
|
||||
row = FrameCalendar(frame_id=frame.id, user_id=body.user_id, calendar_key=body.calendar_key)
|
||||
db.add(row)
|
||||
row.included = body.included
|
||||
if body.calendar_label:
|
||||
row.calendar_label = body.calendar_label
|
||||
# Force this frame's merged cache to pick up the change promptly
|
||||
# rather than waiting out the throttle.
|
||||
frame.calendar_checked_at = 0.0
|
||||
db.commit()
|
||||
return {"status": "saved", "included": row.calendar_included}
|
||||
return {"status": "saved", "included": row.included}
|
||||
|
||||
|
||||
def _calendar_photo_inlay(frame: Frame, db: Session):
|
||||
|
||||
Reference in New Issue
Block a user