From 0bf67061f8270269c37d4e7456a20d9a047189a5 Mon Sep 17 00:00:00 2001 From: Mat12143 Date: Sat, 2 Aug 2025 05:04:30 +0200 Subject: [PATCH] feat: websockets & realtime updates --- frontend/package-lock.json | 21 ------------ frontend/src/lib/utils.ts | 8 ++--- frontend/src/routes/admin/[id]/+page.svelte | 21 ++++++------ frontend/src/routes/room/[id]/+page.svelte | 37 ++++++++++++++++----- 4 files changed, 41 insertions(+), 46 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a545ce8..ab99984 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1210,18 +1210,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/node": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", - "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "undici-types": "~7.8.0" - } - }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -2315,15 +2303,6 @@ "node": ">=14.17" } }, - "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/vite": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index 1babeb9..203d6f8 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -60,10 +60,8 @@ export const triggerPlayNext = async function (roomId: string): Promise<[FetchEr let songs: Song[] = [] - if (json["ended"]) { - json["queue"].forEach(async (i: any) => { - songs.push(await parseSong(i)) - }) - } + json["queue"].forEach(async (i: any) => { + songs.push(await parseSong(i)) + }) return [null, songs, json["index"]] } diff --git a/frontend/src/routes/admin/[id]/+page.svelte b/frontend/src/routes/admin/[id]/+page.svelte index 55d93a5..23f2214 100644 --- a/frontend/src/routes/admin/[id]/+page.svelte +++ b/frontend/src/routes/admin/[id]/+page.svelte @@ -4,6 +4,7 @@ import { onMount } from "svelte" import type { FetchError } from "$lib/types" import { getQueueSongs, triggerPlayNext } from "$lib/utils.js" + import Error from "$lib/components/Error.svelte" let { data } = $props() @@ -20,24 +21,22 @@ playingIndex = index }) - $effect(() => { - $inspect(queueSongs) - }) - async function playNext() { let songs, index ;[returnError, songs, index] = await triggerPlayNext(data.roomId) if (returnError) return - if (songs.length != 0) queueSongs = songs + queueSongs = songs playingIndex = index } -{returnError} - -
- - -
+{#if returnError} + +{:else} +
+ + +
+{/if} diff --git a/frontend/src/routes/room/[id]/+page.svelte b/frontend/src/routes/room/[id]/+page.svelte index 9d9702b..408de8b 100644 --- a/frontend/src/routes/room/[id]/+page.svelte +++ b/frontend/src/routes/room/[id]/+page.svelte @@ -2,12 +2,12 @@ import QueueSlider from "$lib/components/QueueSlider.svelte" import SuggestionInput from "$lib/components/SuggestionInput.svelte" import Error from "$lib/components/Error.svelte" - import { type Suggestion, type Song } from "$lib/types.js" - import { onMount } from "svelte" + import { type Suggestion, type Song, parseSong, parseSuggestion } from "$lib/types.js" + import { onDestroy, onMount } from "svelte" import SuggestionList from "$lib/components/SuggestionList.svelte" import { getQueueSongs, getSuggestions, joinRoom } from "$lib/utils.js" import type { FetchError } from "$lib/types.js" - import { io } from "socket.io-client" + import { io, Socket } from "socket.io-client" let { data } = $props() @@ -18,23 +18,20 @@ let returnError = $state() - let wsUrl = "" + let socket: Socket onMount(async () => { // Join the room + socket = io("/", { path: "/ws" }) let sugg, queue, index - ;[returnError, wsUrl] = await joinRoom(data.roomId) + ;[returnError] = await joinRoom(data.roomId) if (returnError) { return } ;[returnError, sugg] = await getSuggestions(data.roomId) if (returnError) return - - // Setup websocket connection - let socket = io("/", { path: "/ws" }) - await socket.emitWithAck("join_room", { id: data.roomId }) ;[returnError, queue, index] = await getQueueSongs(data.roomId) if (returnError) { return @@ -43,6 +40,28 @@ queueSongs = queue suggestions = sugg playingIndex = index + + // Setup websocket connection + await socket.emitWithAck("join_room", { id: data.roomId }) + + socket.on("queue_update", async (d) => { + const songs = await Promise.all(d.queue.map(parseSong)) + queueSongs = songs + playingIndex = d.index + }) + + socket.on("new_vote", async (d) => { + const updated = await parseSuggestion(d.song) + suggestions = suggestions.map((s) => (s.uuid === updated.uuid ? updated : s)) + }) + + socket.on("new_song", async (d) => { + const song = await parseSuggestion(d.song) + suggestions = [...suggestions, song] + }) + }) + onDestroy(() => { + if (socket) socket.disconnect() })