import 'package:auto_route/auto_route.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; import 'package:spotube/collections/routes.gr.dart'; import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/extensions/constrains.dart'; import 'package:spotube/modules/player/player_queue.dart'; import 'package:spotube/modules/player/sibling_tracks_sheet.dart'; import 'package:spotube/components/adaptive/adaptive_pop_sheet_list.dart'; import 'package:spotube/components/heart_button/heart_button.dart'; import 'package:spotube/extensions/artist_simple.dart'; import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/duration.dart'; import 'package:spotube/models/local_track.dart'; import 'package:spotube/provider/download_manager_provider.dart'; import 'package:spotube/provider/authentication/authentication.dart'; import 'package:spotube/provider/audio_player/audio_player.dart'; import 'package:spotube/provider/sleep_timer_provider.dart'; class PlayerActions extends HookConsumerWidget { final MainAxisAlignment mainAxisAlignment; final bool floatingQueue; final bool showQueue; final List? extraActions; const PlayerActions({ this.mainAxisAlignment = MainAxisAlignment.center, this.floatingQueue = true, this.showQueue = true, this.extraActions, super.key, }); @override Widget build(BuildContext context, ref) { final playlist = ref.watch(audioPlayerProvider); final isLocalTrack = playlist.activeTrack is LocalTrack; ref.watch(downloadManagerProvider); final downloader = ref.watch(downloadManagerProvider.notifier); final isInQueue = useMemoized(() { if (playlist.activeTrack == null) return false; return downloader.isActive(playlist.activeTrack!); }, [ playlist.activeTrack, downloader, ]); final localTracks = [] /* ref.watch(localTracksProvider).value */; final auth = ref.watch(authenticationProvider); final sleepTimer = ref.watch(sleepTimerProvider); final sleepTimerNotifier = ref.watch(sleepTimerProvider.notifier); final isDownloaded = useMemoized(() { return localTracks.any( (element) => element.name == playlist.activeTrack?.name && element.album?.name == playlist.activeTrack?.album?.name && element.artists?.asString() == playlist.activeTrack?.artists?.asString(), ) == true; }, [localTracks, playlist.activeTrack]); final sleepTimerEntries = useMemoized( () => { context.l10n.mins(15): const Duration(minutes: 15), context.l10n.mins(30): const Duration(minutes: 30), context.l10n.hour(1): const Duration(hours: 1), context.l10n.hour(2): const Duration(hours: 2), }, [context.l10n], ); var customHoursEnabled = sleepTimer == null || sleepTimerEntries.values.contains(sleepTimer); return Row( mainAxisAlignment: mainAxisAlignment, children: [ if (showQueue) Tooltip( tooltip: TooltipContainer(child: Text(context.l10n.queue)), child: IconButton.ghost( icon: const Icon(SpotubeIcons.queue), enabled: playlist.activeTrack != null, onPressed: () { openDrawer( context: context, position: OverlayPosition.right, transformBackdrop: false, draggable: false, surfaceBlur: context.theme.surfaceBlur, surfaceOpacity: 0.7, builder: (context) { return Container( constraints: const BoxConstraints(maxWidth: 800), child: Consumer( builder: (context, ref, _) { final playlist = ref.watch(audioPlayerProvider); final playlistNotifier = ref.read(audioPlayerProvider.notifier); return PlayerQueue.fromAudioPlayerNotifier( floating: true, playlist: playlist, notifier: playlistNotifier, ); }, ), ); }, ); }, ), ), if (!isLocalTrack) Tooltip( tooltip: TooltipContainer( child: Text(context.l10n.alternative_track_sources)), child: IconButton.ghost( enabled: playlist.activeTrack != null, icon: const Icon(SpotubeIcons.alternativeRoute), onPressed: () { final screenSize = MediaQuery.sizeOf(context); if (screenSize.mdAndUp) { showPopover( alignment: Alignment.bottomCenter, context: context, builder: (context) { return SurfaceCard( padding: EdgeInsets.zero, child: ConstrainedBox( constraints: const BoxConstraints( maxHeight: 600, maxWidth: 500, ), child: SiblingTracksSheet(floating: floatingQueue), ), ); }, ); } else { context.pushRoute(const PlayerTrackSourcesRoute()); } }, ), ), if (!kIsWeb && !isLocalTrack) if (isInQueue) const SizedBox( height: 20, width: 20, child: CircularProgressIndicator( size: 2, ), ) else Tooltip( tooltip: TooltipContainer(child: Text(context.l10n.download_track)), child: IconButton.ghost( icon: Icon( isDownloaded ? SpotubeIcons.done : SpotubeIcons.download, ), onPressed: playlist.activeTrack != null ? () => downloader.addToQueue(playlist.activeTrack!) : null, ), ), if (playlist.activeTrack != null && !isLocalTrack && auth.asData?.value != null) TrackHeartButton(track: playlist.activeTrack!), AdaptivePopSheetList( tooltip: context.l10n.sleep_timer, offset: Offset(0, -50 * (sleepTimerEntries.values.length + 2)), headings: [ Text(context.l10n.sleep_timer), ], icon: Icon( SpotubeIcons.timer, color: sleepTimer != null ? Colors.red : null, ), onSelected: (value) { if (value == Duration.zero) { sleepTimerNotifier.cancelSleepTimer(); } else { sleepTimerNotifier.setSleepTimer(value); } }, items: (context) => [ for (final entry in sleepTimerEntries.entries) AdaptiveMenuButton( value: entry.value, enabled: sleepTimer != entry.value, child: Text(entry.key), ), AdaptiveMenuButton( enabled: customHoursEnabled, onPressed: (context) async { final currentTime = TimeOfDay.now(); final time = await showDialog( context: context, builder: (context) => HookBuilder(builder: (context) { final timeRef = useRef(null); return AlertDialog( trailing: IconButton.ghost( size: ButtonSize.xSmall, icon: const Icon(SpotubeIcons.close), onPressed: () { Navigator.of(context).pop(); }, ), title: Text( ShadcnLocalizations.of(context).placeholderTimePicker, ), content: TimePickerDialog( use24HourFormat: false, initialValue: TimeOfDay.fromDateTime( DateTime.now().add(sleepTimer ?? Duration.zero), ), onChanged: (value) => timeRef.value = value, ), actions: [ Button.primary( onPressed: () { Navigator.of(context).pop(timeRef.value); }, child: Text(context.l10n.save), ), ], ); }), ); if (time != null) { sleepTimerNotifier.setSleepTimer( Duration( hours: (time.hour - currentTime.hour).abs(), minutes: (time.minute - currentTime.minute).abs(), ), ); } }, child: Text( customHoursEnabled ? context.l10n.custom_hours : sleepTimer.format(abbreviated: true), ), ), AdaptiveMenuButton( value: Duration.zero, enabled: sleepTimer != Duration.zero && sleepTimer != null, child: Text( context.l10n.cancel, style: const TextStyle(color: Colors.green), ), ), ], ), ...(extraActions ?? []) ], ); } }