Skip to content

Commit 5237346

Browse files
committed
refactor: redesign GroupChat UI and simplify GroupHeader component structure
1 parent c041858 commit 5237346

8 files changed

Lines changed: 486 additions & 510 deletions

File tree

client/src/Context/GroupMusicContext.jsx

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export function GroupMusicProvider({ children }) {
3333
const [currentSong, setCurrentSong] = useState(null)
3434
const [volume, setVolume] = useState(0.7)
3535
const [isLoading, setIsLoading] = useState(false)
36+
const [isSyncing, setIsSyncing] = useState(false)
37+
const [syncCountdown, setSyncCountdown] = useState(0)
3638

3739
// Search state
3840
const [searchResults, setSearchResults] = useState([])
@@ -60,6 +62,17 @@ export function GroupMusicProvider({ children }) {
6062
const periodicSyncRef = useRef(null)
6163
const hasAttemptedRejoinRef = useRef(false)
6264
const audioRef = useRef(null)
65+
const syncTimerRef = useRef(null)
66+
67+
const isPlayingRef = useRef(isPlaying)
68+
const currentSongRef = useRef(currentSong)
69+
const currentGroupRef = useRef(currentGroup)
70+
const serverTimeOffsetRef = useRef(serverTimeOffset)
71+
72+
useEffect(() => { isPlayingRef.current = isPlaying }, [isPlaying])
73+
useEffect(() => { currentSongRef.current = currentSong }, [currentSong])
74+
useEffect(() => { currentGroupRef.current = currentGroup }, [currentGroup])
75+
useEffect(() => { serverTimeOffsetRef.current = serverTimeOffset }, [serverTimeOffset])
6376

6477
// Derived state
6578
const currentQueueItem = useMemo(() => {
@@ -438,12 +451,21 @@ export function GroupMusicProvider({ children }) {
438451

439452
if (audioRef.current) {
440453
audioRef.current.pause()
441-
audioRef.current.currentTime = 0
454+
audioRef.current.removeAttribute('src')
455+
audioRef.current.load()
456+
}
457+
458+
if (syncTimerRef.current) {
459+
clearInterval(syncTimerRef.current)
460+
syncTimerRef.current = null
442461
}
443462

444463
setCurrentGroup(null)
464+
currentGroupRef.current = null
445465
setCurrentSong(null)
446466
setIsPlaying(false)
467+
setIsSyncing(false)
468+
setSyncCountdown(0)
447469
setMessages([])
448470
setGroupMembers([])
449471
setQueue([])
@@ -556,7 +578,7 @@ export function GroupMusicProvider({ children }) {
556578
}
557579

558580
// If we're playing a different song or no song, load the correct one
559-
if (!currentSong || currentSong.id !== serverTrack.id) {
581+
if (!currentSongRef.current || currentSongRef.current.id !== serverTrack.id) {
560582
console.log("Out of sync - loading correct song")
561583
setCurrentSong(serverTrack)
562584
const url = serverTrack.download_url?.find((u) => u.quality === "320kbps")?.link
@@ -568,7 +590,7 @@ export function GroupMusicProvider({ children }) {
568590
if (!audioRef.current) return
569591

570592
// Drift correction
571-
const serverNow = Date.now() + serverTimeOffset
593+
const serverNow = Date.now() + serverTimeOffsetRef.current
572594
const timePassed = (serverNow - playbackState.lastUpdate) / 1000
573595
const expectedTime = playbackState.currentTime + (playbackState.isPlaying ? timePassed : 0)
574596
const actualTime = audioRef.current.currentTime
@@ -643,23 +665,65 @@ export function GroupMusicProvider({ children }) {
643665
setLastSync(serverNow)
644666
})
645667

646-
socket.on("music-update", async ({ song, currentTime, queueItem, autoPlay }) => {
668+
socket.on("music-update", async ({ song, currentTime, queueItem, autoPlay, scheduledPlayTime }) => {
669+
if (syncTimerRef.current) clearInterval(syncTimerRef.current)
670+
647671
setCurrentSong(song)
648-
const url = song.download_url.find((url) => url.quality === "320kbps")?.link
672+
currentSongRef.current = song
673+
const url = song.download_url.find((u) => u.quality === "320kbps")?.link
674+
675+
if (scheduledPlayTime) {
676+
setIsSyncing(true)
677+
const serverNow = Date.now() + serverTimeOffsetRef.current
678+
const totalDelay = Math.max(0, scheduledPlayTime - serverNow)
679+
setSyncCountdown(Math.ceil(totalDelay / 1000))
680+
681+
syncTimerRef.current = setInterval(() => {
682+
const remaining = Math.max(0, scheduledPlayTime - (Date.now() + serverTimeOffsetRef.current))
683+
const secs = Math.ceil(remaining / 1000)
684+
setSyncCountdown(secs)
685+
if (secs <= 0) {
686+
clearInterval(syncTimerRef.current)
687+
syncTimerRef.current = null
688+
}
689+
}, 200)
649690

650-
if (url) {
651-
await loadAudio(url, queueItem?.id)
691+
if (url) {
692+
await loadAudio(url, queueItem?.id)
693+
}
652694

653-
if (audioRef.current) {
654-
audioRef.current.currentTime = currentTime
655-
if (autoPlay || isPlaying) {
695+
const nowAfterLoad = Date.now() + serverTimeOffsetRef.current
696+
const remainingDelay = Math.max(0, scheduledPlayTime - nowAfterLoad)
697+
698+
setTimeout(async () => {
699+
if (!currentGroupRef.current) return; // Prevent playing if user left
700+
701+
setIsSyncing(false)
702+
setSyncCountdown(0)
703+
if (audioRef.current && audioRef.current.src) {
704+
audioRef.current.currentTime = currentTime || 0
656705
try {
657706
await audioRef.current.play()
658707
setIsPlaying(true)
659708
} catch (err) {
660709
console.error("Autoplay blocked:", err)
661710
}
662711
}
712+
}, remainingDelay)
713+
} else {
714+
if (url) {
715+
await loadAudio(url, queueItem?.id)
716+
if (audioRef.current) {
717+
audioRef.current.currentTime = currentTime
718+
if (autoPlay || isPlayingRef.current) {
719+
try {
720+
await audioRef.current.play()
721+
setIsPlaying(true)
722+
} catch (err) {
723+
console.error("Autoplay blocked:", err)
724+
}
725+
}
726+
}
663727
}
664728
}
665729

@@ -810,9 +874,22 @@ export function GroupMusicProvider({ children }) {
810874
})
811875

812876
socket.on("group-disbanded", () => {
877+
if (audioRef.current) {
878+
audioRef.current.pause()
879+
audioRef.current.removeAttribute('src')
880+
audioRef.current.load()
881+
}
882+
if (syncTimerRef.current) {
883+
clearInterval(syncTimerRef.current)
884+
syncTimerRef.current = null
885+
}
886+
813887
setCurrentGroup(null)
888+
currentGroupRef.current = null
814889
setCurrentSong(null)
815890
setIsPlaying(false)
891+
setIsSyncing(false)
892+
setSyncCountdown(0)
816893
setMessages([])
817894
setGroupMembers([])
818895
setQueue([])
@@ -859,16 +936,7 @@ export function GroupMusicProvider({ children }) {
859936
socket.off("group-full")
860937
socket.off("feature-locked")
861938
}
862-
}, [
863-
socket,
864-
isPlaying,
865-
serverTimeOffset,
866-
user,
867-
loadAudio,
868-
saveSession,
869-
clearSession,
870-
getServerTime,
871-
])
939+
}, [socket, user, loadAudio, saveSession, clearSession, getServerTime])
872940

873941
const contextValue = {
874942
// Group state
@@ -901,6 +969,8 @@ export function GroupMusicProvider({ children }) {
901969
setVolume,
902970
isLoading,
903971
setIsLoading,
972+
isSyncing,
973+
syncCountdown,
904974

905975
// Search state
906976
searchResults,

client/src/Pages/Music/GroupMusic.jsx

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import { useState, useCallback, useMemo, memo } from "react"
2-
import { Card, CardContent, CardHeader } from "@/components/ui/card"
32
import { useProfile } from "@/Context/Context"
43
import { useGroupMusic } from "@/Context/GroupMusicContext"
54
import { useFeatureAccess } from "@/hooks/useFeatureAccess"
65
import { AnimatePresence, motion } from "framer-motion"
7-
import { cn } from "@/lib/utils"
86

97
import {
108
GroupModal,
@@ -59,6 +57,8 @@ const GroupMusic = () => {
5957
upcomingQueue,
6058
isQueueOpen,
6159
setIsQueueOpen,
60+
isSyncing,
61+
syncCountdown,
6262
} = useGroupMusic()
6363

6464
const [isQrCodeOpen, setQrCodeOpen] = useState(false)
@@ -85,24 +85,17 @@ const GroupMusic = () => {
8585
const handleOpenQueue = useCallback(() => setIsQueueOpen(true), [setIsQueueOpen])
8686

8787
const userId = useMemo(() => user?.userid, [user?.userid])
88-
8988
const groupMaxMembers = currentGroup?.maxMembers || maxGroupMembers
9089

9190
return (
92-
<div className="mx-auto max-w-7xl px-2 md:px-4 py-2 md:py-6">
91+
<div className="mx-auto max-w-7xl px-2 md:px-4 py-2 md:py-4">
9392
<motion.div
94-
initial={{ opacity: 0, y: 20 }}
93+
initial={{ opacity: 0, y: 12 }}
9594
animate={{ opacity: 1, y: 0 }}
96-
transition={{ duration: 0.5 }}
95+
transition={{ duration: 0.35 }}
9796
>
98-
<Card
99-
className={cn(
100-
"overflow-hidden border-border/50",
101-
"bg-linear-to-br from-background via-background to-accent/10",
102-
"shadow-xl",
103-
)}
104-
>
105-
<CardHeader className="px-3 md:px-6 py-2 md:pb-2">
97+
<div className="rounded-2xl border border-border/30 bg-background overflow-hidden">
98+
<div className="px-3 md:px-5 pt-3 md:pt-4 pb-2">
10699
<GroupHeader
107100
currentGroup={currentGroup}
108101
isRejoining={isRejoining}
@@ -112,25 +105,27 @@ const GroupMusic = () => {
112105
onQueueOpen={handleOpenQueue}
113106
queueCount={activeQueueCount}
114107
/>
115-
</CardHeader>
108+
</div>
116109

117-
<CardContent className="px-2 md:px-6 pb-4">
110+
<div className="px-3 md:px-5 pb-4">
118111
<AnimatePresence mode="wait">
119112
{!currentGroup ? (
120113
<WelcomeView key="welcome" onOpenModal={handleOpenGroupModal} />
121114
) : (
122115
<motion.div
123116
key="group-content"
124-
initial={{ opacity: 0, y: 20 }}
117+
initial={{ opacity: 0, y: 12 }}
125118
animate={{ opacity: 1, y: 0 }}
126-
exit={{ opacity: 0, y: -20 }}
127-
transition={{ duration: 0.4 }}
128-
className="space-y-3 md:space-y-6"
119+
exit={{ opacity: 0, y: -12 }}
120+
transition={{ duration: 0.3 }}
121+
className="space-y-3 md:space-y-4"
129122
>
130123
<NowPlayingCard
131124
currentSong={currentSong}
132125
isPlaying={isPlaying}
133126
isLoading={isLoading}
127+
isSyncing={isSyncing}
128+
syncCountdown={syncCountdown}
134129
currentTime={currentTime}
135130
duration={duration}
136131
volume={volume}
@@ -145,7 +140,7 @@ const GroupMusic = () => {
145140
queueCount={activeQueueCount}
146141
/>
147142

148-
<div className="grid grid-cols-1 lg:grid-cols-3 gap-3 md:gap-6">
143+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-3 md:gap-4">
149144
<div className="lg:col-span-2">
150145
<GroupChat
151146
messages={messages}
@@ -166,8 +161,8 @@ const GroupMusic = () => {
166161
</motion.div>
167162
)}
168163
</AnimatePresence>
169-
</CardContent>
170-
</Card>
164+
</div>
165+
</div>
171166
</motion.div>
172167

173168
{isGroupModalOpen && (

0 commit comments

Comments
 (0)