screentinker/frontend/js/views
Claude 5297f091af Let a display's playlist actually be cleared
"No playlist" was an option you could select that did nothing. The picker offered it, and the change
handler opened with `if (!newPlaylistId) return; // Don't allow deselecting for now` — so choosing it
sent no request, changed nothing, and said nothing. The guard was honest about why: there was no way
to do it. PUT /devices/:id has never read playlist_id (200, ignored), and POST /playlists/:id/assign
can only ever set one.

Reported on #234 as "I also selected No playlist ... it still showed the same video". It did, and my
first explanation blamed the playlist-swap deferral. The deferral would have stranded it too — that
is fixed separately and tested — but on this path nothing was ever sent, so the deferral never got
the chance.

DELETE /api/devices/:id/playlist, device-scoped rather than playlist-scoped because there is no
playlist to authorize against when clearing. Ownership goes through checkDeviceOwnership like every
other device mutation, so a viewer and a stranger are refused. Clearing an already-clear display is
a no-op success, since it lives in a dropdown someone can pick twice. The now-empty playlist is
pushed to the device so the screen stops, rather than leaving the old content up until something
else happens to refresh it.

Validated on an Android 12 emulator against the reporter's shape: cleared while a YouTube item was
on screen, zero plays afterwards, device row cleared.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
2026-07-30 18:29:18 -05:00
..
activity.js Make help tips reachable, and explain the pages that had none 2026-07-28 19:37:49 -05:00
admin-player-debug.js Add player debug overlay and server-side error telemetry sink 2026-05-15 15:20:42 -05:00
admin.js Show every plan on the admin tab, with who is on each 2026-07-29 19:36:10 -05:00
billing.js i18n batch 5: wire layout-editor + video-wall + billing (~85 keys) 2026-04-29 20:13:38 -05:00
content-library.js feat(content): subtitle/caption support as a content property (#223) 2026-07-23 12:33:35 -05:00
dashboard.js Dragging a screen onto a group now moves it instead of adding it 2026-07-29 21:33:56 -05:00
designer.js Translate the labels that never went through t() 2026-07-28 19:52:52 -05:00
device-detail.js Let a display's playlist actually be cleared 2026-07-30 18:29:18 -05:00
force-password-change.js feat(admin): admin-provisioned user creation + first-login gate (#10) 2026-06-05 11:03:56 -05:00
help.js docs(help): add AI Content Design quick-start to the in-app Help page (#41) 2026-06-09 13:58:53 -05:00
kiosk.js fix(security): patch quick-win findings from the codebase review 2026-06-08 19:02:19 -05:00
layout-editor.js fix(layouts): atomic zone save (stop template zone duplication) 2026-06-09 10:16:01 -05:00
login.js feat(auth): self-service password reset 2026-07-27 11:19:39 -05:00
no-workspace.js feat(signup): optional org-on-create for self-service signups (#12) 2026-06-05 11:16:27 -05:00
onboarding.js i18n batch 3b: wire onboarding.js + admin.js (~84 keys) 2026-04-29 20:04:23 -05:00
playlists.js Make help tips reachable, and explain the pages that had none 2026-07-28 19:37:49 -05:00
reports.js i18n batch 4: wire schedule + reports + kiosk (~95 keys) 2026-04-29 20:09:32 -05:00
schedule.js Bind the calendar's pointer handlers once, not once per render 2026-07-28 20:08:31 -05:00
settings.js Make help tips reachable, and explain the pages that had none 2026-07-28 19:37:49 -05:00
teams.js Say Teams is switched off instead of showing an empty list 2026-07-28 19:46:26 -05:00
video-wall.js Translate the labels that never went through t() 2026-07-28 19:52:52 -05:00
widgets.js feat(designer): edit designer-made widgets in the designer (+ legacy reconstruction) (#207) 2026-07-21 09:00:36 -05:00
workspace-members.js feat(admin): admin-provisioned user creation + first-login gate (#10) 2026-06-05 11:03:56 -05:00