From dbb81de763df60eba62ef1256a7161ea6ca59b66 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Mon, 26 Sep 2022 13:14:55 +0600 Subject: [PATCH] fix: no appropriate output when playlist is empty #201 --- lib/components/Shared/TracksTableView.dart | 271 +++++++++++---------- lib/models/Logger.dart | 8 +- lib/provider/SpotifyRequests.dart | 26 +- 3 files changed, 161 insertions(+), 144 deletions(-) diff --git a/lib/components/Shared/TracksTableView.dart b/lib/components/Shared/TracksTableView.dart index ff9bbbab2..ca1cbcb9e 100644 --- a/lib/components/Shared/TracksTableView.dart +++ b/lib/components/Shared/TracksTableView.dart @@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:queue/queue.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/components/Shared/DownloadConfirmationDialog.dart'; +import 'package:spotube/components/Shared/NotFound.dart'; import 'package:spotube/components/Shared/TrackTile.dart'; import 'package:spotube/hooks/useBreakpoints.dart'; import 'package:spotube/provider/Downloader.dart'; @@ -50,151 +51,157 @@ class TracksTableView extends HookConsumerWidget { [tracks], ); - final children = [ - if (heading != null) heading!, - Row( - children: [ - Checkbox( - value: selected.value.length == tracks.length, - onChanged: (checked) { - if (!showCheck.value) showCheck.value = true; - if (checked == true) { - selected.value = tracks.map((s) => s.id!).toList(); - } else { - selected.value = []; - showCheck.value = false; - } - }, - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - "#", - textAlign: TextAlign.center, - style: tableHeadStyle, - ), - ), - Expanded( - child: Row( + final children = tracks.isEmpty + ? [const NotFound(vertical: true)] + : [ + if (heading != null) heading!, + Row( children: [ - Text( - "Title", - style: tableHeadStyle, - overflow: TextOverflow.ellipsis, + Checkbox( + value: selected.value.length == tracks.length, + onChanged: (checked) { + if (!showCheck.value) showCheck.value = true; + if (checked == true) { + selected.value = tracks.map((s) => s.id!).toList(); + } else { + selected.value = []; + showCheck.value = false; + } + }, ), - ], - ), - ), - // used alignment of this table-head - if (breakpoint.isMoreThan(Breakpoints.md)) ...[ - const SizedBox(width: 100), - Expanded( - child: Row( - children: [ - Text( - "Album", - overflow: TextOverflow.ellipsis, + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + "#", + textAlign: TextAlign.center, style: tableHeadStyle, ), - ], - ), - ) - ], - if (!breakpoint.isSm) ...[ - const SizedBox(width: 10), - Text("Time", style: tableHeadStyle), - const SizedBox(width: 10), - ], - PopupMenuButton( - itemBuilder: (context) { - return [ - PopupMenuItem( - enabled: selected.value.isNotEmpty, + ), + Expanded( child: Row( children: [ - const Icon(Icons.file_download_outlined), Text( - "Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}", + "Title", + style: tableHeadStyle, + overflow: TextOverflow.ellipsis, ), ], ), - value: "download", ), - ]; - }, - onSelected: (action) async { - switch (action) { - case "download": - { - final isConfirmed = await showDialog( - context: context, - builder: (context) { - return const DownloadConfirmationDialog(); - }); - if (isConfirmed != true) return; - for (final selectedTrack in selectedTracks) { - downloader.addToQueue(selectedTrack); + // used alignment of this table-head + if (breakpoint.isMoreThan(Breakpoints.md)) ...[ + const SizedBox(width: 100), + Expanded( + child: Row( + children: [ + Text( + "Album", + overflow: TextOverflow.ellipsis, + style: tableHeadStyle, + ), + ], + ), + ) + ], + if (!breakpoint.isSm) ...[ + const SizedBox(width: 10), + Text("Time", style: tableHeadStyle), + const SizedBox(width: 10), + ], + PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem( + enabled: selected.value.isNotEmpty, + child: Row( + children: [ + const Icon(Icons.file_download_outlined), + Text( + "Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}", + ), + ], + ), + value: "download", + ), + ]; + }, + onSelected: (action) async { + switch (action) { + case "download": + { + final isConfirmed = await showDialog( + context: context, + builder: (context) { + return const DownloadConfirmationDialog(); + }); + if (isConfirmed != true) return; + for (final selectedTrack in selectedTracks) { + downloader.addToQueue(selectedTrack); + } + selected.value = []; + showCheck.value = false; + break; + } + default: } - selected.value = []; - showCheck.value = false; - break; + }, + ), + ], + ), + ...tracks.asMap().entries.map((track) { + String? thumbnailUrl = TypeConversionUtils.image_X_UrlString( + track.value.album?.images, + index: (track.value.album?.images?.length ?? 1) - 1, + placeholder: ImagePlaceholder.albumArt, + ); + String duration = + "${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; + return InkWell( + onLongPress: () { + showCheck.value = true; + selected.value = [...selected.value, track.value.id!]; + }, + onTap: () { + if (showCheck.value) { + final alreadyChecked = + selected.value.contains(track.value.id); + if (alreadyChecked) { + selected.value = selected.value + .where((id) => id != track.value.id) + .toList(); + } else { + selected.value = [...selected.value, track.value.id!]; + } + } else { + onTrackPlayButtonPressed?.call(track.value); } - default: - } - }, - ), - ], - ), - ...tracks.asMap().entries.map((track) { - String? thumbnailUrl = TypeConversionUtils.image_X_UrlString( - track.value.album?.images, - index: (track.value.album?.images?.length ?? 1) - 1, - placeholder: ImagePlaceholder.albumArt, - ); - String duration = - "${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; - return InkWell( - onLongPress: () { - showCheck.value = true; - selected.value = [...selected.value, track.value.id!]; - }, - onTap: () { - if (showCheck.value) { - final alreadyChecked = selected.value.contains(track.value.id); - if (alreadyChecked) { - selected.value = - selected.value.where((id) => id != track.value.id).toList(); - } else { - selected.value = [...selected.value, track.value.id!]; - } - } else { - onTrackPlayButtonPressed?.call(track.value); - } - }, - child: TrackTile( - playback, - playlistId: playlistId, - track: track, - duration: duration, - thumbnailUrl: thumbnailUrl, - userPlaylist: userPlaylist, - isActive: playback.track?.id == track.value.id, - onTrackPlayButtonPressed: onTrackPlayButtonPressed, - isChecked: selected.value.contains(track.value.id), - showCheck: showCheck.value, - onCheckChange: (checked) { - if (checked == true) { - selected.value = [...selected.value, track.value.id!]; - } else { - selected.value = - selected.value.where((id) => id != track.value.id).toList(); - } - }, - ), - ); - }).toList(), - if (bottomSpace) const SizedBox(height: 70), - ]; + }, + child: TrackTile( + playback, + playlistId: playlistId, + track: track, + duration: duration, + thumbnailUrl: thumbnailUrl, + userPlaylist: userPlaylist, + isActive: playback.track?.id == track.value.id, + onTrackPlayButtonPressed: onTrackPlayButtonPressed, + isChecked: selected.value.contains(track.value.id), + showCheck: showCheck.value, + onCheckChange: (checked) { + if (checked == true) { + selected.value = [...selected.value, track.value.id!]; + } else { + selected.value = selected.value + .where((id) => id != track.value.id) + .toList(); + } + }, + ), + ); + }).toList(), + if (bottomSpace) const SizedBox(height: 70), + ]; + if (isSliver) { return SliverList(delegate: SliverChildListDelegate(children)); } diff --git a/lib/models/Logger.dart b/lib/models/Logger.dart index 3467c44d9..ab20e0808 100644 --- a/lib/models/Logger.dart +++ b/lib/models/Logger.dart @@ -6,16 +6,16 @@ import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart' as path; import 'package:spotube/utils/platform.dart'; -final _loggerFactory = _SpotubeLogger(); +final _loggerFactory = SpotubeLogger(); -_SpotubeLogger getLogger(T owner) { +SpotubeLogger getLogger(T owner) { _loggerFactory.owner = owner is String ? owner : owner.toString(); return _loggerFactory; } -class _SpotubeLogger extends Logger { +class SpotubeLogger extends Logger { String? owner; - _SpotubeLogger([this.owner]) : super(filter: _SpotubeLogFilter()); + SpotubeLogger([this.owner]) : super(filter: _SpotubeLogFilter()); @override void log(Level level, message, [error, StackTrace? stackTrace]) async { diff --git a/lib/provider/SpotifyRequests.dart b/lib/provider/SpotifyRequests.dart index b46a1f65e..5fbf527c6 100644 --- a/lib/provider/SpotifyRequests.dart +++ b/lib/provider/SpotifyRequests.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:spotube/models/Logger.dart'; import 'package:spotube/models/LyricsModels.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/SpotifyDI.dart'; @@ -106,14 +107,23 @@ final currentUserSavedTracksQuery = FutureProvider>((ref) { final playlistTracksQuery = FutureProvider.family, String>( (ref, id) { - final spotify = ref.watch(spotifyProvider); - return id != "user-liked-tracks" - ? spotify.playlists.getTracksByPlaylistId(id).all().then( - (value) => value.toList(), - ) - : spotify.tracks.me.saved.all().then( - (tracks) => tracks.map((e) => e.track!).toList(), - ); + try { + final spotify = ref.watch(spotifyProvider); + return id != "user-liked-tracks" + ? spotify.playlists.getTracksByPlaylistId(id).all().then( + (value) => value.toList(), + ) + : spotify.tracks.me.saved.all().then( + (tracks) => tracks.map((e) => e.track!).toList(), + ); + } catch (e, stack) { + getLogger("playlistTracksQuery").e( + "Fetching playlist tracks", + e, + stack, + ); + return []; + } }, );