fix: no appropriate output when playlist is empty #201

This commit is contained in:
Kingkor Roy Tirtho 2022-09-26 13:14:55 +06:00
parent 9b3ef2ffa2
commit dbb81de763
3 changed files with 161 additions and 144 deletions

View File

@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:queue/queue.dart'; import 'package:queue/queue.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/Shared/DownloadConfirmationDialog.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/components/Shared/TrackTile.dart';
import 'package:spotube/hooks/useBreakpoints.dart'; import 'package:spotube/hooks/useBreakpoints.dart';
import 'package:spotube/provider/Downloader.dart'; import 'package:spotube/provider/Downloader.dart';
@ -50,151 +51,157 @@ class TracksTableView extends HookConsumerWidget {
[tracks], [tracks],
); );
final children = [ final children = tracks.isEmpty
if (heading != null) heading!, ? [const NotFound(vertical: true)]
Row( : [
children: [ if (heading != null) heading!,
Checkbox( Row(
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(
children: [ children: [
Text( Checkbox(
"Title", value: selected.value.length == tracks.length,
style: tableHeadStyle, onChanged: (checked) {
overflow: TextOverflow.ellipsis, 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(
// used alignment of this table-head "#",
if (breakpoint.isMoreThan(Breakpoints.md)) ...[ textAlign: TextAlign.center,
const SizedBox(width: 100),
Expanded(
child: Row(
children: [
Text(
"Album",
overflow: TextOverflow.ellipsis,
style: tableHeadStyle, style: tableHeadStyle,
), ),
], ),
), Expanded(
)
],
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( child: Row(
children: [ children: [
const Icon(Icons.file_download_outlined),
Text( Text(
"Download ${selectedTracks.isNotEmpty ? "(${selectedTracks.length})" : ""}", "Title",
style: tableHeadStyle,
overflow: TextOverflow.ellipsis,
), ),
], ],
), ),
value: "download",
), ),
]; // used alignment of this table-head
}, if (breakpoint.isMoreThan(Breakpoints.md)) ...[
onSelected: (action) async { const SizedBox(width: 100),
switch (action) { Expanded(
case "download": child: Row(
{ children: [
final isConfirmed = await showDialog( Text(
context: context, "Album",
builder: (context) { overflow: TextOverflow.ellipsis,
return const DownloadConfirmationDialog(); style: tableHeadStyle,
}); ),
if (isConfirmed != true) return; ],
for (final selectedTrack in selectedTracks) { ),
downloader.addToQueue(selectedTrack); )
],
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: },
} child: TrackTile(
}, playback,
), playlistId: playlistId,
], track: track,
), duration: duration,
...tracks.asMap().entries.map((track) { thumbnailUrl: thumbnailUrl,
String? thumbnailUrl = TypeConversionUtils.image_X_UrlString( userPlaylist: userPlaylist,
track.value.album?.images, isActive: playback.track?.id == track.value.id,
index: (track.value.album?.images?.length ?? 1) - 1, onTrackPlayButtonPressed: onTrackPlayButtonPressed,
placeholder: ImagePlaceholder.albumArt, isChecked: selected.value.contains(track.value.id),
); showCheck: showCheck.value,
String duration = onCheckChange: (checked) {
"${track.value.duration?.inMinutes.remainder(60)}:${PrimitiveUtils.zeroPadNumStr(track.value.duration?.inSeconds.remainder(60) ?? 0)}"; if (checked == true) {
return InkWell( selected.value = [...selected.value, track.value.id!];
onLongPress: () { } else {
showCheck.value = true; selected.value = selected.value
selected.value = [...selected.value, track.value.id!]; .where((id) => id != track.value.id)
}, .toList();
onTap: () { }
if (showCheck.value) { },
final alreadyChecked = selected.value.contains(track.value.id); ),
if (alreadyChecked) { );
selected.value = }).toList(),
selected.value.where((id) => id != track.value.id).toList(); if (bottomSpace) const SizedBox(height: 70),
} 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),
];
if (isSliver) { if (isSliver) {
return SliverList(delegate: SliverChildListDelegate(children)); return SliverList(delegate: SliverChildListDelegate(children));
} }

View File

@ -6,16 +6,16 @@ import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/platform.dart';
final _loggerFactory = _SpotubeLogger(); final _loggerFactory = SpotubeLogger();
_SpotubeLogger getLogger<T>(T owner) { SpotubeLogger getLogger<T>(T owner) {
_loggerFactory.owner = owner is String ? owner : owner.toString(); _loggerFactory.owner = owner is String ? owner : owner.toString();
return _loggerFactory; return _loggerFactory;
} }
class _SpotubeLogger extends Logger { class SpotubeLogger extends Logger {
String? owner; String? owner;
_SpotubeLogger([this.owner]) : super(filter: _SpotubeLogFilter()); SpotubeLogger([this.owner]) : super(filter: _SpotubeLogFilter());
@override @override
void log(Level level, message, [error, StackTrace? stackTrace]) async { void log(Level level, message, [error, StackTrace? stackTrace]) async {

View File

@ -1,6 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotube/models/Logger.dart';
import 'package:spotube/models/LyricsModels.dart'; import 'package:spotube/models/LyricsModels.dart';
import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/Playback.dart';
import 'package:spotube/provider/SpotifyDI.dart'; import 'package:spotube/provider/SpotifyDI.dart';
@ -106,14 +107,23 @@ final currentUserSavedTracksQuery = FutureProvider<List<Track>>((ref) {
final playlistTracksQuery = FutureProvider.family<List<Track>, String>( final playlistTracksQuery = FutureProvider.family<List<Track>, String>(
(ref, id) { (ref, id) {
final spotify = ref.watch(spotifyProvider); try {
return id != "user-liked-tracks" final spotify = ref.watch(spotifyProvider);
? spotify.playlists.getTracksByPlaylistId(id).all().then( return id != "user-liked-tracks"
(value) => value.toList(), ? spotify.playlists.getTracksByPlaylistId(id).all().then(
) (value) => value.toList(),
: spotify.tracks.me.saved.all().then( )
(tracks) => tracks.map((e) => e.track!).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 [];
}
}, },
); );