1
- import React , { useState } from "react" ;
2
- import { useAtom } from "jotai" ;
1
+ import React , { Suspense , useState } from "react" ;
2
+ import { useAtom , useAtomValue } from "jotai" ;
3
3
import { Button } from "@/shadcn/ui/button" ;
4
4
import { Dialog , DialogContent } from "@/shadcn/ui/dialog" ;
5
5
import {
@@ -8,6 +8,11 @@ import {
8
8
DropdownMenuContent ,
9
9
DropdownMenuItem ,
10
10
DropdownMenuSeparator ,
11
+ DropdownMenuSub ,
12
+ DropdownMenuGroup ,
13
+ DropdownMenuSubTrigger ,
14
+ DropdownMenuPortal ,
15
+ DropdownMenuSubContent ,
11
16
} from "@/shadcn/ui/dropdown-menu" ;
12
17
import { useVideoSelection } from "@/hooks/useVideoSelection" ;
13
18
import { siteIsSmallAtom } from "@/hooks/useFrame" ;
@@ -16,6 +21,14 @@ import { makeThumbnailUrl } from "@/lib/utils";
16
21
import { VideoThumbnail } from "../video/VideoThumbnail" ;
17
22
import { SelectionEditShortcuts } from "../edit/selection/SelectionEditShortcuts" ;
18
23
import SelectionFooterTopicPicker from "../edit/selection/SelectionFooterTopicPicker" ;
24
+ import {
25
+ usePlaylistVideoMassAddMutation ,
26
+ usePlaylists ,
27
+ } from "@/services/playlist.service" ;
28
+ import { userAtom } from "@/store/auth" ;
29
+ import { Link } from "react-router-dom" ;
30
+ import { LazyNewPlaylistDialog } from "../video/LazyNewPlaylistDialog" ;
31
+ import { useToast } from "@/shadcn/ui/use-toast" ;
19
32
const SelectedVideosModal = ( {
20
33
isSmall,
21
34
open,
@@ -136,18 +149,27 @@ const SelectionFooter = () => {
136
149
disabled = { ! selectedVideos . length }
137
150
className = "flex items-center"
138
151
>
139
- < span className = "i-material-symbols:list-alt-outline mr-2" />
140
- Playlist
152
+ < span className = "i-heroicons:folder-open mr-2" />
153
+ { t ( "component.mainNav.playlist" ) }
141
154
< div className = "i-lucide:chevron-up ml-2 size-4" > </ div >
142
155
</ Button >
143
156
</ DropdownMenuTrigger >
144
157
< DropdownMenuContent >
145
158
< DropdownMenuItem onClick = { ( ) => setPage ( 1 ) } >
146
- Add to current Playlist
147
- </ DropdownMenuItem >
148
- < DropdownMenuItem onClick = { ( ) => setPage ( 2 ) } >
149
- Make into new Playlist
159
+ < div className = "i-heroicons:queue-list" />
160
+ Add to Queue
150
161
</ DropdownMenuItem >
162
+ < DropdownMenuGroup >
163
+ < DropdownMenuSub >
164
+ < DropdownMenuSubTrigger className = "bg-base-1" >
165
+ < div className = "i-solar:playlist-broken" />
166
+ Add to Playlist
167
+ </ DropdownMenuSubTrigger >
168
+ < DropdownMenuPortal >
169
+ < PlaylistMenuItems />
170
+ </ DropdownMenuPortal >
171
+ </ DropdownMenuSub >
172
+ </ DropdownMenuGroup >
151
173
</ DropdownMenuContent >
152
174
</ DropdownMenu >
153
175
@@ -241,4 +263,65 @@ const SelectionFooter = () => {
241
263
) ;
242
264
} ;
243
265
266
+ function PlaylistMenuItems ( ) {
267
+ const { t } = useTranslation ( ) ;
268
+ const { toast } = useToast ( ) ;
269
+
270
+ const { mutate } = usePlaylistVideoMassAddMutation ( {
271
+ onSuccess : ( ) => {
272
+ toast ( {
273
+ title : "Added to playlist" ,
274
+ } ) ;
275
+ } ,
276
+ } ) ;
277
+ const { data, isLoading } = usePlaylists ( ) ;
278
+ const user = useAtomValue ( userAtom ) ;
279
+ const { selectedVideos } = useVideoSelection ( ) ;
280
+ const videoIds = selectedVideos
281
+ . filter ( ( v ) => v . type === "stream" || v . type === "clip" )
282
+ . map ( ( v ) => v . id ) ;
283
+
284
+ return (
285
+ < DropdownMenuSubContent >
286
+ { ! user ? (
287
+ < DropdownMenuItem asChild >
288
+ < Link to = "/login" > { t ( "component.mainNav.login" ) } </ Link >
289
+ </ DropdownMenuItem >
290
+ ) : (
291
+ < >
292
+ { data ?. map ( ( { name, id } ) => (
293
+ < DropdownMenuItem key = { id } onClick = { ( ) => mutate ( { id, videoIds } ) } >
294
+ { name }
295
+ </ DropdownMenuItem >
296
+ ) ) }
297
+ { isLoading && (
298
+ < DropdownMenuItem className = "justify-center" disabled >
299
+ < div className = "i-lucide:loader-2 animate-spin leading-none" />
300
+ </ DropdownMenuItem >
301
+ ) }
302
+ { data ?. length || isLoading ? < DropdownMenuSeparator /> : null }
303
+ { videoIds && (
304
+ < Suspense
305
+ fallback = {
306
+ < div className = "i-lucide:loader-2 animate-spin leading-none" />
307
+ }
308
+ >
309
+ < LazyNewPlaylistDialog
310
+ triggerElement = {
311
+ < DropdownMenuItem
312
+ onSelect = { ( event ) => event . preventDefault ( ) }
313
+ >
314
+ { t ( "component.playlist.menu.new-playlist" ) }
315
+ </ DropdownMenuItem >
316
+ }
317
+ videoIds = { videoIds }
318
+ />
319
+ </ Suspense >
320
+ ) }
321
+ </ >
322
+ ) }
323
+ </ DropdownMenuSubContent >
324
+ ) ;
325
+ }
326
+
244
327
export default SelectionFooter ;
0 commit comments