@@ -16,13 +16,13 @@ import {
1616 DropdownMenuTrigger ,
1717} from "@/components/ui/dropdown-menu" ;
1818import { cn } from "@/lib/utils" ;
19- import { useAccount , useCoState } from "jazz-tools/react" ;
2019import { MoreHorizontal , Pause , Play } from "lucide-react" ;
21- import { Fragment , useCallback , useState } from "react" ;
20+ import { Fragment , Suspense , useCallback , useState } from "react" ;
2221import { EditTrackDialog } from "./RenameTrackDialog" ;
2322import { Waveform } from "./Waveform" ;
2423import { Button } from "./ui/button" ;
2524import { useAccountSelector } from "@/components/AccountProvider.tsx" ;
25+ import { useSuspenseCoState , useSuspenseAccount } from "jazz-tools/react" ;
2626
2727function isPartOfThePlaylist ( trackId : string , playlist : PlaylistWithTracks ) {
2828 return Array . from ( playlist . tracks . $jazz . refs ) . some ( ( t ) => t . id === trackId ) ;
@@ -39,43 +39,32 @@ export function MusicTrackRow({
3939 onClick : ( track : MusicTrack ) => void ;
4040 index : number ;
4141} ) {
42- const track = useCoState ( MusicTrack , trackId ) ;
42+ const track = useSuspenseCoState ( MusicTrack , trackId ) ;
4343 const [ isEditDialogOpen , setIsEditDialogOpen ] = useState ( false ) ;
4444 const [ isDropdownOpen , setIsDropdownOpen ] = useState ( false ) ;
4545 const [ isHovered , setIsHovered ] = useState ( false ) ;
4646
47- const playlists = useAccount ( MusicaAccountWithPlaylists , {
48- select : ( account ) =>
49- account . $isLoaded && account . root . playlists . $isLoaded
50- ? account . root . playlists
51- : undefined ,
52- } ) ;
53-
5447 const isActiveTrack = useAccountSelector ( {
55- select : ( me ) => me . $isLoaded && me . root . activeTrack ?. $jazz . id === trackId ,
48+ select : ( me ) => me . root . activeTrack ?. $jazz . id === trackId ,
5649 } ) ;
5750
5851 const canEditTrack = useAccountSelector ( {
59- select : ( me ) => me . $isLoaded && track . $isLoaded && me . canWrite ( track ) ,
52+ select : ( me ) => me . canWrite ( track ) ,
6053 } ) ;
6154
6255 function handleTrackClick ( ) {
63- if ( ! track . $isLoaded ) return ;
6456 onClick ( track ) ;
6557 }
6658
6759 function handleAddToPlaylist ( playlist : Playlist ) {
68- if ( ! track . $isLoaded ) return ;
6960 addTrackToPlaylist ( playlist , track ) ;
7061 }
7162
7263 function handleRemoveFromPlaylist ( playlist : Playlist ) {
73- if ( ! track . $isLoaded ) return ;
7464 removeTrackFromPlaylist ( playlist , track ) ;
7565 }
7666
7767 function deleteTrack ( ) {
78- if ( ! track . $isLoaded ) return ;
7968 removeTrackFromAllPlaylists ( track ) ;
8069 }
8170
@@ -89,7 +78,6 @@ export function MusicTrackRow({
8978 } , [ ] ) ;
9079
9180 const showWaveform = isHovered || isActiveTrack ;
92- const trackTitle = track . $isLoaded ? track . title : "" ;
9381
9482 return (
9583 < li
@@ -112,7 +100,7 @@ export function MusicTrackRow({
112100 isActiveTrack && "md:opacity-100 opacity-100" ,
113101 ) }
114102 onClick = { handleTrackClick }
115- aria-label = { `${ isPlaying ? "Pause" : "Play" } ${ trackTitle } ` }
103+ aria-label = { `${ isPlaying ? "Pause" : "Play" } ${ track . title } ` }
116104 >
117105 { isPlaying ? (
118106 < Pause height = { 16 } width = { 16 } fill = "currentColor" />
@@ -133,11 +121,11 @@ export function MusicTrackRow({
133121 onClick = { handleTrackClick }
134122 className = "flex items-center overflow-hidden text-ellipsis whitespace-nowrap cursor-pointer flex-1 min-w-0"
135123 >
136- { trackTitle }
124+ { track . title }
137125 </ button >
138126
139127 { /* Waveform that appears on hover */ }
140- { track . $isLoaded && showWaveform && (
128+ { showWaveform && (
141129 < div className = "flex-1 min-w-0 px-2 items-center hidden md:flex" >
142130 < Waveform
143131 track = { track }
@@ -155,40 +143,28 @@ export function MusicTrackRow({
155143 < Button
156144 variant = "ghost"
157145 className = "h-8 w-8 p-0"
158- aria-label = { `Open ${ trackTitle } menu` }
146+ aria-label = { `Open ${ track . title } menu` }
159147 >
160148 < span className = "sr-only" > Open menu</ span >
161149 < MoreHorizontal className = "h-4 w-4" />
162150 </ Button >
163151 </ DropdownMenuTrigger >
164- < DropdownMenuContent align = "end" >
165- < DropdownMenuItem onSelect = { handleEdit } > Edit</ DropdownMenuItem >
166- { playlists
167- ?. filter ( ( playlist ) => playlist . $isLoaded )
168- . map ( ( playlist , playlistIndex ) => (
169- < Fragment key = { playlistIndex } >
170- { isPartOfThePlaylist ( trackId , playlist ) ? (
171- < DropdownMenuItem
172- key = { `remove-${ playlistIndex } ` }
173- onSelect = { ( ) => handleRemoveFromPlaylist ( playlist ) }
174- >
175- Remove from { playlist . title }
176- </ DropdownMenuItem >
177- ) : (
178- < DropdownMenuItem
179- key = { `add-${ playlistIndex } ` }
180- onSelect = { ( ) => handleAddToPlaylist ( playlist ) }
181- >
182- Add to { playlist . title }
183- </ DropdownMenuItem >
184- ) }
185- </ Fragment >
186- ) ) }
187- </ DropdownMenuContent >
152+ < Suspense >
153+ < DropdownMenuContent align = "end" >
154+ < DropdownMenuItem onSelect = { handleEdit } > Edit</ DropdownMenuItem >
155+ < PlaylistItems
156+ onRemove = { handleRemoveFromPlaylist }
157+ onAdd = { handleAddToPlaylist }
158+ isPartOfThePlaylist = { ( playlist ) =>
159+ isPartOfThePlaylist ( trackId , playlist )
160+ }
161+ />
162+ </ DropdownMenuContent >
163+ </ Suspense >
188164 </ DropdownMenu >
189165 </ div >
190166 ) }
191- { track . $isLoaded && isEditDialogOpen && (
167+ { isEditDialogOpen && (
192168 < EditTrackDialog
193169 track = { track }
194170 isOpen = { isEditDialogOpen }
@@ -199,3 +175,33 @@ export function MusicTrackRow({
199175 </ li >
200176 ) ;
201177}
178+
179+ function PlaylistItems ( props : {
180+ onRemove : ( playlist : PlaylistWithTracks ) => void ;
181+ onAdd : ( playlist : PlaylistWithTracks ) => void ;
182+ isPartOfThePlaylist : ( playlist : PlaylistWithTracks ) => boolean ;
183+ } ) {
184+ const playlists = useSuspenseAccount ( MusicaAccountWithPlaylists , {
185+ select : ( account ) => account . root . playlists ,
186+ } ) ;
187+
188+ const loadedPlaylists = playlists . filter ( ( playlist ) => playlist . $isLoaded ) ;
189+
190+ return (
191+ < >
192+ { loadedPlaylists . map ( ( playlist , playlistIndex ) => (
193+ < Fragment key = { playlistIndex } >
194+ { props . isPartOfThePlaylist ( playlist ) ? (
195+ < DropdownMenuItem onSelect = { ( ) => props . onRemove ( playlist ) } >
196+ Remove from { playlist . title }
197+ </ DropdownMenuItem >
198+ ) : (
199+ < DropdownMenuItem onSelect = { ( ) => props . onAdd ( playlist ) } >
200+ Add to { playlist . title }
201+ </ DropdownMenuItem >
202+ ) }
203+ </ Fragment >
204+ ) ) }
205+ </ >
206+ ) ;
207+ }
0 commit comments