fix: prevent auto-play on page load (#238)

* fix: prevent auto-play on page load and remove broken playback position restore

- change lastUpdateWasLocal default from true to false
- on fresh page load, queue hydration from server should not trigger auto-play
- only explicit user actions (play track, skip, etc.) set this to true
- remove client-side playback position restoration code
- never worked due to timing issues (player mounts before queue hydrates)
- saves/restores were happening but restoration had race condition
- cleaner UX: page refresh = paused state, user clicks play when ready

fixes #225

* docs: add emoji usage guideline

authored by zzstoatzz.io and committed by GitHub 563ffecc 89748e5f

Changed files
+2 -35
frontend
src
+1
CLAUDE.md
··· 13 13 - **logs**: `flyctl logs` is BLOCKING - use `run_in_background=true` 14 14 - **type hints**: required everywhere 15 15 - **ATProto namespaces**: NEVER use Bluesky lexicons (app.bsky.*). ALWAYS use our namespace (fm.plyr.*) for ALL records 16 + - **communication**: use emojis sparingly and strictly for emphasis (avoid excessive check marks) 16 17 17 18 ## structure 18 19
-34
frontend/src/lib/components/Player.svelte
··· 46 46 player.volume = parseFloat(savedVolume); 47 47 } 48 48 49 - // restore playback position if available 50 - const savedPlaybackState = localStorage.getItem('player_playback_state'); 51 - if (savedPlaybackState && player.currentTrack) { 52 - try { 53 - const { file_id, position, timestamp } = JSON.parse(savedPlaybackState); 54 - const isStale = Date.now() - timestamp > 1000 * 60 * 60; // 1 hour 55 - 56 - if (!isStale && file_id === player.currentTrack.file_id && position > 0) { 57 - // wait for audio to load before seeking 58 - if (player.audioElement) { 59 - player.audioElement.addEventListener('loadedmetadata', () => { 60 - player.currentTime = position; 61 - }, { once: true }); 62 - } 63 - } 64 - } catch (error) { 65 - console.warn('failed to restore playback state:', error); 66 - } 67 - } 68 - 69 49 // update player height css variable for dynamic positioning 70 50 function updatePlayerHeight() { 71 51 const playerEl = document.querySelector('.player') as HTMLElement | null; ··· 115 95 // save volume to localStorage when it changes 116 96 $effect(() => { 117 97 localStorage.setItem('player_volume', player.volume.toString()); 118 - }); 119 - 120 - // save playback position to localStorage periodically 121 - let lastSavedPosition = $state(0); 122 - $effect(() => { 123 - // only save if we're playing and position has changed significantly 124 - if (player.currentTrack && Math.abs(player.currentTime - lastSavedPosition) > 5) { 125 - lastSavedPosition = player.currentTime; 126 - localStorage.setItem('player_playback_state', JSON.stringify({ 127 - file_id: player.currentTrack.file_id, 128 - position: player.currentTime, 129 - timestamp: Date.now() 130 - })); 131 - } 132 98 }); 133 99 134 100 // handle track changes - load new audio when track changes
+1 -1
frontend/src/lib/queue.svelte.ts
··· 16 16 revision = $state<number | null>(null); 17 17 etag = $state<string | null>(null); 18 18 syncInProgress = $state(false); 19 - lastUpdateWasLocal = $state(true); 19 + lastUpdateWasLocal = $state(false); 20 20 21 21 initialized = false; 22 22 hydrating = false;