7.4 KiB
VJ Page: Auto-Announce Save Button & Real-Time Playlist View
Problem Analysis
Issue 1: Auto-Announce Settings Keep Reverting
Root cause: The auto-announce settings (toggle, template, URL base) use a dual-save mechanism that causes a race condition:
- On every input/change event, the values are saved to
localStorageimmediately AND queued for remote save viaqueueAutoAnnounceSettingsPatch(). queueAutoAnnounceSettingsPatch()has a 15-second debounce (AUTO_ANNOUNCE_SAVE_DEBOUNCE_MS = 15000) before it callsqueueVjSettingsPatch(), which itself has a 300ms debounce before callingpatchUserSettings().- Meanwhile,
onUserSettings()fires whenever settings arrive from the relay/worker (including from other tabs or stale relay data). When it fires,applyVjPageSettingsFromState()overwrites all form fields with whatever the relay has — which may be the OLD values if the 15-second debounce hasn't flushed yet.
The revert flow:
- User changes template → localStorage updated, 15s timer starts
- Before 15s elapses,
onUserSettingsfires with stale relay data →applyVjPageSettingsFromState()overwrites the input fields back to old values - The pending patch may still fire, but it patches the OLD values that were already overwritten
Solution: Remove auto-saving for auto-announce settings entirely. Add an explicit "Save" button. Only persist when the user clicks Save.
Issue 2: No Real-Time Playlist View During Stream
Root cause: When a VJ goes live and songs are announced, appendNowPlayingToPlaylist() in vj-stream.mjs adds tracks to an in-memory playlistTracks array and publishes a Nostr event (kind 30078). However:
- There is no UI in vj.html that displays the current episode's playlist tracks as they build up.
- The
vj-playlist.htmlpage shows historical playlists but requires a page load/refresh — it does not subscribe to real-time updates. - The
playlistTracksarray invj-stream.mjsis internal and not exposed to the UI.
Solution: Add a real-time playlist panel in the VJ stream card area that shows tracks as they are added during a live stream, with the ability to edit (remove/reorder) tracks.
Implementation Plan
Task 1: Add Save Button for Auto-Announce Settings
File: www/vj.html
1a. Add Save button to HTML (around line 1557)
- Add a
<button id="btnSaveAutoAnnounce" class="btn" type="button">Save</button>after the template textarea inside#divAutoAnnounceCard.
1b. Remove auto-save from input event listeners (around lines 6130-6171)
autoAnnounceSongTogglechange listener (line 6131): Remove the call toqueueAutoAnnounceSettingsPatch(). Keep thelocalStoragewrite for immediate local persistence.inputAutoAnnounceTemplateinput listener (line 6152): Remove the call toqueueAutoAnnounceSettingsPatch(). Keep thelocalStoragewrite.inputAutoAnnounceUrlBaseinput listener (line 6162): Remove the call toqueueAutoAnnounceSettingsPatch(). Keep thelocalStoragewrite.
1c. Add Save button click handler (after line 6171)
- On click of
btnSaveAutoAnnounce:- Read current values from the three inputs (toggle, template, URL base)
- Call
queueVjSettingsPatch()directly (bypassing the 15s debounce) with the fullautoAnnounceobject:queueVjSettingsPatch({ autoAnnounce: { songEnabled: autoAnnounceSongToggle?.checked || false, template: String(inputAutoAnnounceTemplate?.value || '').trim() || DEFAULT_AUTO_ANNOUNCE_TEMPLATE, urlBase: String(inputAutoAnnounceUrlBase?.value || '').trim() || DEFAULT_AUTO_ANNOUNCE_URL_BASE, }, }); - Show brief visual feedback (e.g., change button text to "Saved!" for 1.5s)
1d. Guard applyVjPageSettingsFromState against overwriting dirty auto-announce fields
- In
applyVjPageSettingsFromState()(lines 2155-2176), add a check: if the auto-announce values from settings match what is already in localStorage (the user's latest local edits), apply them. Otherwise, prefer the localStorage values. This prevents relay-delivered stale settings from overwriting unsaved local changes. - Alternatively (simpler): skip applying auto-announce fields from remote settings entirely — only apply them on initial page load. After that, the Save button is the only way to sync.
1e. Remove queueAutoAnnounceSettingsPatch function and autoAnnouncePatchTimer variable
- These are no longer needed since we removed the debounced auto-save.
- Remove
AUTO_ANNOUNCE_SAVE_DEBOUNCE_MSconstant (line 2008). - Remove
autoAnnouncePatchTimervariable (line 2028). - Remove
queueAutoAnnounceSettingsPatchfunction (lines 2134-2143).
Task 2: Real-Time Playlist View During Stream
File: www/js/vj-stream.mjs
2a. Expose playlist tracks and add render callback
- Add an
onPlaylistChangecallback option toinitVjStreamPanel()config. - After every call to
playlistTracks.push(...)inappendNowPlayingToPlaylist()(line 879), call the callback with the current tracks array. - Also call it when tracks are cleared (go live, create show, etc.).
- Expose methods:
getPlaylistTracks(),removePlaylistTrack(index),reorderPlaylistTrack(fromIndex, toIndex)on the returned panel object.
2b. Add playlist display section to HTML
File: www/vj.html
- Add a new section inside
#vjStreamCard(after the auto-announce card, around line 1558), or as a collapsible section:<div id="divEpisodePlaylist" class="divPostItem streamSection"> <div class="musicSidebarTitle">EPISODE PLAYLIST</div> <div id="divEpisodePlaylistStatus" class="musicPlaylistMeta">No tracks yet.</div> <ul id="ulEpisodePlaylistTracks" class="musicPlaylistList"></ul> </div>
2c. Render playlist tracks in real-time
File: www/vj.html
- Implement
renderEpisodePlaylist(tracks)function that:- Clears and re-renders
#ulEpisodePlaylistTrackswith the current track list - Each item shows: track number, artist, title, timestamp
- Each item has a remove button (X) and is draggable for reorder
- Updates
#divEpisodePlaylistStatuswith track count
- Clears and re-renders
2d. Wire up the callback
File: www/vj.html
- Pass
onPlaylistChange: renderEpisodePlaylistin theinitVjStreamPanel()config (around line 6093).
2e. Enable editing (remove/reorder)
File: www/js/vj-stream.mjs
removePlaylistTrack(index): splice fromplaylistTracks, re-number remaining tracks, callpublishPlaylistEvent('live'), callonPlaylistChange.reorderPlaylistTrack(fromIndex, toIndex): reorder inplaylistTracks, re-number, callpublishPlaylistEvent('live'), callonPlaylistChange.
File: www/vj.html
- Wire remove button clicks to call
vjStreamPanel.removePlaylistTrack(index). - Wire drag-and-drop reorder to call
vjStreamPanel.reorderPlaylistTrack(from, to).
2f. Add minimal CSS for the episode playlist
File: www/vj.html
- Style
#divEpisodePlaylist,#ulEpisodePlaylistTracksitems with track number, artist/title, time, and remove button. - Reuse existing
.musicPlaylistListand.musicQueueItempatterns for consistency.
Summary of Files to Modify
| File | Changes |
|---|---|
www/vj.html |
Add Save button HTML, remove auto-save listeners, add save handler, add episode playlist section, render function, CSS |
www/js/vj-stream.mjs |
Expose playlist tracks, add callback, add remove/reorder methods, return new methods from panel |