import 'package:auto_route/auto_route.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart' hide Consumer; import 'package:spotube/collections/routes.gr.dart'; import 'package:spotube/components/dialogs/select_device_dialog.dart'; import 'package:spotube/components/playbutton_view/playbutton_card.dart'; import 'package:spotube/components/playbutton_view/playbutton_tile.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/models/connect/connect.dart'; import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/provider/audio_player/querying_track_info.dart'; import 'package:spotube/provider/connect/connect.dart'; import 'package:spotube/provider/history/history.dart'; import 'package:spotube/provider/audio_player/audio_player.dart'; import 'package:spotube/provider/metadata_plugin/library/tracks.dart'; import 'package:spotube/provider/metadata_plugin/tracks/playlist.dart'; import 'package:spotube/provider/metadata_plugin/user.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; class PlaylistCard extends HookConsumerWidget { final SpotubeSimplePlaylistObject playlist; final bool _isTile; const PlaylistCard( this.playlist, { super.key, }) : _isTile = false; const PlaylistCard.tile( this.playlist, { super.key, }) : _isTile = true; @override Widget build(BuildContext context, ref) { final playlistQueue = ref.watch(audioPlayerProvider); final playlistNotifier = ref.watch(audioPlayerProvider.notifier); final isFetchingActiveTrack = ref.watch(queryingTrackInfoProvider); final historyNotifier = ref.read(playbackHistoryActionsProvider); final playing = useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying; bool isPlaylistPlaying = useMemoized( () => playlistQueue.containsCollection(playlist.id), [playlistQueue, playlist.id], ); final updating = useState(false); final me = ref.watch(metadataPluginUserProvider); Future> fetchInitialTracks() async { if (playlist.id == 'user-liked-tracks') { final tracks = await ref.read(metadataPluginSavedTracksProvider.future); return tracks.items; } final result = await ref .read(metadataPluginPlaylistTracksProvider(playlist.id).future); return result.items; } Future> fetchAllTracks() async { await fetchInitialTracks(); if (playlist.id == 'user-liked-tracks') { return ref.read(metadataPluginSavedTracksProvider.notifier).fetchAll(); } return ref .read(metadataPluginPlaylistTracksProvider(playlist.id).notifier) .fetchAll(); } void onTap() { context.navigateTo(PlaylistRoute(id: playlist.id, playlist: playlist)); } void onPlaybuttonPressed() async { try { updating.value = true; if (isPlaylistPlaying && playing) { return audioPlayer.pause(); } else if (isPlaylistPlaying && !playing) { return audioPlayer.resume(); } final fetchedInitialTracks = await fetchInitialTracks(); if (fetchedInitialTracks.isEmpty || !context.mounted) return; final isRemoteDevice = await showSelectDeviceDialog(context, ref); if (isRemoteDevice == null) return; if (isRemoteDevice) { final remotePlayback = ref.read(connectProvider.notifier); final allTracks = await fetchAllTracks(); await remotePlayback.load( WebSocketLoadEventData.playlist( tracks: allTracks as List, collection: playlist, ), ); } else { await playlistNotifier.load(fetchedInitialTracks, autoPlay: true); playlistNotifier.addCollection(playlist.id); historyNotifier.addPlaylists([playlist]); final allTracks = await fetchAllTracks(); await playlistNotifier .addTracks(allTracks.sublist(fetchedInitialTracks.length)); } } finally { if (context.mounted) { updating.value = false; } } } void onAddToQueuePressed() async { updating.value = true; try { if (isPlaylistPlaying) return; final fetchedInitialTracks = await fetchAllTracks(); if (fetchedInitialTracks.isEmpty) return; playlistNotifier.addTracks(fetchedInitialTracks); playlistNotifier.addCollection(playlist.id); historyNotifier.addPlaylists([playlist]); if (context.mounted) { showToast( context: context, builder: (context, overlay) { return SurfaceCard( child: Basic( content: Text( context.l10n .added_num_tracks_to_queue(fetchedInitialTracks.length), ), trailing: Button.outline( child: Text(context.l10n.undo), onPressed: () { playlistNotifier .removeTracks(fetchedInitialTracks.map((e) => e.id)); }, ), ), ); }, ); } } finally { updating.value = false; } } final imageUrl = playlist.images.asUrlString( placeholder: ImagePlaceholder.collection, ); final isLoading = (isPlaylistPlaying && isFetchingActiveTrack) || updating.value; final isOwner = playlist.owner.id == me.asData?.value?.id && me.asData?.value?.id != null; if (_isTile) { return PlaybuttonTile( title: playlist.name, description: playlist.description, image: null, imageUrl: imageUrl, isPlaying: isPlaylistPlaying, isLoading: isLoading, isOwner: isOwner, onTap: onTap, onPlaybuttonPressed: onPlaybuttonPressed, onAddToQueuePressed: onAddToQueuePressed, ); } return PlaybuttonCard( title: playlist.name, description: playlist.description, image: null, imageUrl: imageUrl, isPlaying: isPlaylistPlaying, isLoading: isLoading, isOwner: isOwner, onTap: onTap, onPlaybuttonPressed: onPlaybuttonPressed, onAddToQueuePressed: onAddToQueuePressed, ); } }