import 'dart:math'; import 'package:flutter/widgets.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/components/dialogs/select_device_dialog.dart'; import 'package:spotube/components/track_presentation/presentation_actions.dart'; import 'package:spotube/components/track_presentation/presentation_props.dart'; import 'package:spotube/models/connect/connect.dart'; import 'package:spotube/models/metadata/metadata.dart'; import 'package:spotube/provider/audio_player/audio_player.dart'; import 'package:spotube/provider/connect/connect.dart'; import 'package:spotube/provider/history/history.dart'; import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/logger/logger.dart'; typedef UseActionCallbacks = ({ bool isActive, bool isLoading, Future Function() onShuffle, Future Function() onPlay, VoidCallback onAddToQueue, }); UseActionCallbacks useActionCallbacks(WidgetRef ref) { final isLoading = useState(false); final context = useContext(); final options = TrackPresentationOptions.of(context); final playlist = ref.watch(audioPlayerProvider); final playlistNotifier = ref.watch(audioPlayerProvider.notifier); final historyNotifier = ref.watch(playbackHistoryActionsProvider); final isActive = useMemoized( () => playlist.collections.contains(options.collectionId), [playlist.collections, options.collectionId], ); final onShuffle = useCallback(() async { try { isLoading.value = true; final initialTracks = options.tracks; if (!context.mounted) return; final isRemoteDevice = await showSelectDeviceDialog(context, ref); if (isRemoteDevice == null) return; if (isRemoteDevice) { final allTracks = await options.pagination.onFetchAll(); final remotePlayback = ref.read(connectProvider.notifier); await remotePlayback.load( options.collection is SpotubeSimpleAlbumObject ? WebSocketLoadEventData.album( tracks: allTracks, collection: options.collection as SpotubeSimpleAlbumObject, initialIndex: Random().nextInt(allTracks.length)) : WebSocketLoadEventData.playlist( tracks: allTracks, collection: options.collection as SpotubeSimplePlaylistObject, initialIndex: Random().nextInt(allTracks.length), ), ); await remotePlayback.setShuffle(true); } else { await playlistNotifier.load( initialTracks, autoPlay: true, initialIndex: Random().nextInt(initialTracks.length), ); await audioPlayer.setShuffle(true); playlistNotifier.addCollection(options.collectionId); if (options.collection is SpotubeSimpleAlbumObject) { historyNotifier .addAlbums([options.collection as SpotubeSimpleAlbumObject]); } else { historyNotifier.addPlaylists( [options.collection as SpotubeSimplePlaylistObject]); } final allTracks = await options.pagination.onFetchAll(); await playlistNotifier.addTracks( allTracks.sublist(initialTracks.length), ); } } catch (e, stack) { AppLogger.reportError(e, stack); rethrow; } finally { isLoading.value = false; } }, [options, playlistNotifier, historyNotifier]); final onPlay = useCallback(() async { try { isLoading.value = true; final initialTracks = options.tracks; if (!context.mounted) return; final isRemoteDevice = await showSelectDeviceDialog(context, ref); if (isRemoteDevice == null) return; if (isRemoteDevice) { final allTracks = await options.pagination.onFetchAll(); final remotePlayback = ref.read(connectProvider.notifier); await remotePlayback.load( options.collection is SpotubeSimpleAlbumObject ? WebSocketLoadEventData.album( tracks: allTracks, collection: options.collection as SpotubeSimpleAlbumObject, ) : WebSocketLoadEventData.playlist( tracks: allTracks, collection: options.collection as SpotubeSimplePlaylistObject, ), ); } else { if (initialTracks.isEmpty) return; await playlistNotifier.load(initialTracks, autoPlay: true); playlistNotifier.addCollection(options.collectionId); if (options.collection is SpotubeSimpleAlbumObject) { historyNotifier.addAlbums( [options.collection as SpotubeSimpleAlbumObject], ); } else { historyNotifier.addPlaylists( [options.collection as SpotubeSimplePlaylistObject], ); } final allTracks = await options.pagination.onFetchAll(); await playlistNotifier.addTracks( allTracks.sublist(initialTracks.length), ); } } catch (e, stack) { AppLogger.reportError(e, stack); rethrow; } finally { if (context.mounted) { isLoading.value = false; } } }, [options, playlistNotifier, historyNotifier]); final onAddToQueue = useCallback(() { final tracks = options.tracks; playlistNotifier.addTracks(tracks); playlistNotifier.addCollection(options.collectionId); if (options.collection is SpotubeSimpleAlbumObject) { historyNotifier .addAlbums([options.collection as SpotubeSimpleAlbumObject]); } else { historyNotifier .addPlaylists([options.collection as SpotubeSimplePlaylistObject]); } if (!context.mounted) return; showToastForAction(context, "add-to-queue", tracks.length); }, [options, playlistNotifier, historyNotifier]); return ( isActive: isActive, isLoading: isLoading.value, onShuffle: onShuffle, onPlay: onPlay, onAddToQueue: onAddToQueue, ); }