mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: no appropriate output when playlist is empty #201
This commit is contained in:
parent
9b3ef2ffa2
commit
dbb81de763
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 [];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user