A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz

Move WebSocket handling to Feed and update cache

Remove duplicate WebSocket and heartbeat logic from NowPlayings. Feed
now opens the socket using WS_URL (without a trailing /ws), updates the
["now-playings"] and ["scrobblesChart"] query data, and includes
queryClient in the effect dependencies.

+5 -39
+4 -2
apps/web/src/pages/home/feed/Feed.tsx
··· 37 37 const { data, isLoading } = useFeedQuery(); 38 38 39 39 useEffect(() => { 40 - const ws = new WebSocket(`${WS_URL.replace("http", "ws")}/ws`); 40 + const ws = new WebSocket(`${WS_URL.replace("http", "ws")}`); 41 41 socketRef.current = ws; 42 42 43 43 ws.onopen = () => { ··· 53 53 54 54 const message = JSON.parse(event.data); 55 55 queryClient.setQueryData(["feed"], message.scrobbles); 56 + queryClient.setQueryData(["now-playings"], message.nowPlayings); 57 + queryClient.setQueryData(["scrobblesChart"], message.scrobblesChart); 56 58 }; 57 59 58 60 return () => { ··· 64 66 } 65 67 console.log(">> WebSocket connection closed"); 66 68 }; 67 - }, []); 69 + }, [queryClient]); 68 70 69 71 return ( 70 72 <Container>
+1 -37
apps/web/src/pages/home/nowplayings/NowPlayings.tsx
··· 7 7 import dayjs from "dayjs"; 8 8 import relativeTime from "dayjs/plugin/relativeTime"; 9 9 import utc from "dayjs/plugin/utc"; 10 - import { useEffect, useRef, useState } from "react"; 10 + import { useEffect, useState } from "react"; 11 11 import { useNowPlayingsQuery } from "../../../hooks/useNowPlaying"; 12 12 import styles from "./styles"; 13 - import { WS_URL } from "../../../consts"; 14 - import { useQueryClient } from "@tanstack/react-query"; 15 13 16 14 dayjs.extend(relativeTime); 17 15 dayjs.extend(utc); ··· 90 88 `; 91 89 92 90 function NowPlayings() { 93 - const queryClient = useQueryClient(); 94 91 const [isOpen, setIsOpen] = useState(false); 95 - const socketRef = useRef<WebSocket | null>(null); 96 - const heartbeatInterval = useRef<number | null>(null); 97 92 const { data: nowPlayings, isLoading } = useNowPlayingsQuery(); 98 93 const [currentlyPlaying, setCurrentlyPlaying] = useState<{ 99 94 id: string; ··· 111 106 } | null>(null); 112 107 const [currentIndex, setCurrentIndex] = useState(0); 113 108 const [progress, setProgress] = useState(0); 114 - 115 - useEffect(() => { 116 - const ws = new WebSocket(`${WS_URL.replace("http", "ws")}/ws`); 117 - socketRef.current = ws; 118 - 119 - ws.onopen = () => { 120 - heartbeatInterval.current = window.setInterval(() => { 121 - ws.send("ping"); 122 - }, 3000); 123 - }; 124 - 125 - ws.onmessage = (event) => { 126 - if (event.data === "pong") { 127 - return; 128 - } 129 - 130 - const message = JSON.parse(event.data); 131 - queryClient.setQueryData(["now-playings"], message.nowPlayings); 132 - queryClient.setQueryData(["scrobblesChart"], message.scrobblesChart); 133 - }; 134 - 135 - return () => { 136 - if (ws) { 137 - if (heartbeatInterval.current) { 138 - clearInterval(heartbeatInterval.current); 139 - } 140 - ws.close(); 141 - } 142 - console.log(">> WebSocket connection closed"); 143 - }; 144 - }, []); 145 109 146 110 const onNext = () => { 147 111 const nextIndex = currentIndex + 1;