mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
refactor: use metadata album & playlist object for card and pages
This commit is contained in:
parent
758b0bc9d9
commit
4b09f6c96b
@ -1,5 +1,6 @@
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/models/spotify/home_feed.dart';
|
import 'package:spotube/models/spotify/home_feed.dart';
|
||||||
import 'package:spotube/models/spotify_friends.dart';
|
import 'package:spotube/models/spotify_friends.dart';
|
||||||
import 'package:spotube/provider/history/summary.dart';
|
import 'package:spotube/provider/history/summary.dart';
|
||||||
@ -64,24 +65,26 @@ abstract class FakeData {
|
|||||||
..uri = "uri"
|
..uri = "uri"
|
||||||
..externalUrls = externalUrls;
|
..externalUrls = externalUrls;
|
||||||
|
|
||||||
static final AlbumSimple albumSimple = AlbumSimple()
|
static final SpotubeSimpleAlbumObject albumSimple = SpotubeSimpleAlbumObject(
|
||||||
..id = "1"
|
albumType: SpotubeAlbumType.album,
|
||||||
..albumType = AlbumType.album
|
artists: [],
|
||||||
..artists = [artistSimple]
|
externalUri: "https://example.com",
|
||||||
..availableMarkets = [Market.BD]
|
id: "1",
|
||||||
..externalUrls = externalUrls
|
name: "A good album",
|
||||||
..href = "text"
|
releaseDate: "2021-01-01",
|
||||||
..images = [image]
|
images: [
|
||||||
..name = "A good album"
|
SpotubeImageObject(
|
||||||
..releaseDate = "2021-01-01"
|
height: 1,
|
||||||
..releaseDatePrecision = DatePrecision.day
|
width: 1,
|
||||||
..type = "type"
|
url: "https://dummyimage.com/100x100/cfcfcf/cfcfcf.jpg",
|
||||||
..uri = "uri";
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
static final Track track = Track()
|
static final Track track = Track()
|
||||||
..id = "1"
|
..id = "1"
|
||||||
..artists = [artist, artist, artist]
|
..artists = [artist, artist, artist]
|
||||||
..album = albumSimple
|
// ..album = albumSimple
|
||||||
..availableMarkets = [Market.BD]
|
..availableMarkets = [Market.BD]
|
||||||
..discNumber = 1
|
..discNumber = 1
|
||||||
..durationMs = 50000
|
..durationMs = 50000
|
||||||
|
@ -10,9 +10,10 @@
|
|||||||
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
// ignore_for_file: no_leading_underscores_for_library_prefixes
|
||||||
import 'package:auto_route/auto_route.dart' as _i44;
|
import 'package:auto_route/auto_route.dart' as _i44;
|
||||||
import 'package:flutter/material.dart' as _i45;
|
import 'package:flutter/material.dart' as _i45;
|
||||||
import 'package:shadcn_flutter/shadcn_flutter.dart' as _i47;
|
import 'package:shadcn_flutter/shadcn_flutter.dart' as _i48;
|
||||||
import 'package:spotify/spotify.dart' as _i46;
|
import 'package:spotify/spotify.dart' as _i47;
|
||||||
import 'package:spotube/models/spotify/recommendation_seeds.dart' as _i48;
|
import 'package:spotube/models/metadata/metadata.dart' as _i46;
|
||||||
|
import 'package:spotube/models/spotify/recommendation_seeds.dart' as _i49;
|
||||||
import 'package:spotube/pages/album/album.dart' as _i2;
|
import 'package:spotube/pages/album/album.dart' as _i2;
|
||||||
import 'package:spotube/pages/artist/artist.dart' as _i3;
|
import 'package:spotube/pages/artist/artist.dart' as _i3;
|
||||||
import 'package:spotube/pages/connect/connect.dart' as _i6;
|
import 'package:spotube/pages/connect/connect.dart' as _i6;
|
||||||
@ -86,7 +87,7 @@ class AlbumRoute extends _i44.PageRouteInfo<AlbumRouteArgs> {
|
|||||||
AlbumRoute({
|
AlbumRoute({
|
||||||
_i45.Key? key,
|
_i45.Key? key,
|
||||||
required String id,
|
required String id,
|
||||||
required _i46.AlbumSimple album,
|
required _i46.SpotubeSimpleAlbumObject album,
|
||||||
List<_i44.PageRouteInfo>? children,
|
List<_i44.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
AlbumRoute.name,
|
AlbumRoute.name,
|
||||||
@ -125,7 +126,7 @@ class AlbumRouteArgs {
|
|||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
final _i46.AlbumSimple album;
|
final _i46.SpotubeSimpleAlbumObject album;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -264,7 +265,7 @@ class GenrePlaylistsRoute extends _i44.PageRouteInfo<GenrePlaylistsRouteArgs> {
|
|||||||
GenrePlaylistsRoute({
|
GenrePlaylistsRoute({
|
||||||
_i45.Key? key,
|
_i45.Key? key,
|
||||||
required String id,
|
required String id,
|
||||||
required _i46.Category category,
|
required _i47.Category category,
|
||||||
List<_i44.PageRouteInfo>? children,
|
List<_i44.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
GenrePlaylistsRoute.name,
|
GenrePlaylistsRoute.name,
|
||||||
@ -303,7 +304,7 @@ class GenrePlaylistsRouteArgs {
|
|||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
final _i46.Category category;
|
final _i47.Category category;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -335,7 +336,7 @@ class GettingStartedRoute extends _i44.PageRouteInfo<void> {
|
|||||||
class HomeFeedSectionRoute
|
class HomeFeedSectionRoute
|
||||||
extends _i44.PageRouteInfo<HomeFeedSectionRouteArgs> {
|
extends _i44.PageRouteInfo<HomeFeedSectionRouteArgs> {
|
||||||
HomeFeedSectionRoute({
|
HomeFeedSectionRoute({
|
||||||
_i47.Key? key,
|
_i48.Key? key,
|
||||||
required String sectionUri,
|
required String sectionUri,
|
||||||
List<_i44.PageRouteInfo>? children,
|
List<_i44.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
@ -371,7 +372,7 @@ class HomeFeedSectionRouteArgs {
|
|||||||
required this.sectionUri,
|
required this.sectionUri,
|
||||||
});
|
});
|
||||||
|
|
||||||
final _i47.Key? key;
|
final _i48.Key? key;
|
||||||
|
|
||||||
final String sectionUri;
|
final String sectionUri;
|
||||||
|
|
||||||
@ -443,7 +444,7 @@ class LibraryRoute extends _i44.PageRouteInfo<void> {
|
|||||||
class LikedPlaylistRoute extends _i44.PageRouteInfo<LikedPlaylistRouteArgs> {
|
class LikedPlaylistRoute extends _i44.PageRouteInfo<LikedPlaylistRouteArgs> {
|
||||||
LikedPlaylistRoute({
|
LikedPlaylistRoute({
|
||||||
_i45.Key? key,
|
_i45.Key? key,
|
||||||
required _i46.PlaylistSimple playlist,
|
required _i46.SpotubeSimplePlaylistObject playlist,
|
||||||
List<_i44.PageRouteInfo>? children,
|
List<_i44.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
LikedPlaylistRoute.name,
|
LikedPlaylistRoute.name,
|
||||||
@ -476,7 +477,7 @@ class LikedPlaylistRouteArgs {
|
|||||||
|
|
||||||
final _i45.Key? key;
|
final _i45.Key? key;
|
||||||
|
|
||||||
final _i46.PlaylistSimple playlist;
|
final _i46.SpotubeSimplePlaylistObject playlist;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -584,8 +585,8 @@ class LyricsRoute extends _i44.PageRouteInfo<void> {
|
|||||||
/// [_i18.MiniLyricsPage]
|
/// [_i18.MiniLyricsPage]
|
||||||
class MiniLyricsRoute extends _i44.PageRouteInfo<MiniLyricsRouteArgs> {
|
class MiniLyricsRoute extends _i44.PageRouteInfo<MiniLyricsRouteArgs> {
|
||||||
MiniLyricsRoute({
|
MiniLyricsRoute({
|
||||||
_i47.Key? key,
|
_i48.Key? key,
|
||||||
required _i47.Size prevSize,
|
required _i48.Size prevSize,
|
||||||
List<_i44.PageRouteInfo>? children,
|
List<_i44.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
MiniLyricsRoute.name,
|
MiniLyricsRoute.name,
|
||||||
@ -616,9 +617,9 @@ class MiniLyricsRouteArgs {
|
|||||||
required this.prevSize,
|
required this.prevSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
final _i47.Key? key;
|
final _i48.Key? key;
|
||||||
|
|
||||||
final _i47.Size prevSize;
|
final _i48.Size prevSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -688,8 +689,8 @@ class PlayerTrackSourcesRoute extends _i44.PageRouteInfo<void> {
|
|||||||
class PlaylistGenerateResultRoute
|
class PlaylistGenerateResultRoute
|
||||||
extends _i44.PageRouteInfo<PlaylistGenerateResultRouteArgs> {
|
extends _i44.PageRouteInfo<PlaylistGenerateResultRouteArgs> {
|
||||||
PlaylistGenerateResultRoute({
|
PlaylistGenerateResultRoute({
|
||||||
_i47.Key? key,
|
_i48.Key? key,
|
||||||
required _i48.GeneratePlaylistProviderInput state,
|
required _i49.GeneratePlaylistProviderInput state,
|
||||||
List<_i44.PageRouteInfo>? children,
|
List<_i44.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
PlaylistGenerateResultRoute.name,
|
PlaylistGenerateResultRoute.name,
|
||||||
@ -720,9 +721,9 @@ class PlaylistGenerateResultRouteArgs {
|
|||||||
required this.state,
|
required this.state,
|
||||||
});
|
});
|
||||||
|
|
||||||
final _i47.Key? key;
|
final _i48.Key? key;
|
||||||
|
|
||||||
final _i48.GeneratePlaylistProviderInput state;
|
final _i49.GeneratePlaylistProviderInput state;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -755,7 +756,7 @@ class PlaylistRoute extends _i44.PageRouteInfo<PlaylistRouteArgs> {
|
|||||||
PlaylistRoute({
|
PlaylistRoute({
|
||||||
_i45.Key? key,
|
_i45.Key? key,
|
||||||
required String id,
|
required String id,
|
||||||
required _i46.PlaylistSimple playlist,
|
required _i46.SpotubeSimplePlaylistObject playlist,
|
||||||
List<_i44.PageRouteInfo>? children,
|
List<_i44.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
PlaylistRoute.name,
|
PlaylistRoute.name,
|
||||||
@ -794,7 +795,7 @@ class PlaylistRouteArgs {
|
|||||||
|
|
||||||
final String id;
|
final String id;
|
||||||
|
|
||||||
final _i46.PlaylistSimple playlist;
|
final _i46.SpotubeSimplePlaylistObject playlist;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@ -1034,7 +1035,7 @@ class StatsStreamsRoute extends _i44.PageRouteInfo<void> {
|
|||||||
/// [_i37.TrackPage]
|
/// [_i37.TrackPage]
|
||||||
class TrackRoute extends _i44.PageRouteInfo<TrackRouteArgs> {
|
class TrackRoute extends _i44.PageRouteInfo<TrackRouteArgs> {
|
||||||
TrackRoute({
|
TrackRoute({
|
||||||
_i47.Key? key,
|
_i48.Key? key,
|
||||||
required String trackId,
|
required String trackId,
|
||||||
List<_i44.PageRouteInfo>? children,
|
List<_i44.PageRouteInfo>? children,
|
||||||
}) : super(
|
}) : super(
|
||||||
@ -1069,7 +1070,7 @@ class TrackRouteArgs {
|
|||||||
required this.trackId,
|
required this.trackId,
|
||||||
});
|
});
|
||||||
|
|
||||||
final _i47.Key? key;
|
final _i48.Key? key;
|
||||||
|
|
||||||
final String trackId;
|
final String trackId;
|
||||||
|
|
||||||
|
@ -31,12 +31,12 @@ class TrackDetailsDialog extends HookWidget {
|
|||||||
textStyle: const TextStyle(color: Colors.blue),
|
textStyle: const TextStyle(color: Colors.blue),
|
||||||
hideOverflowArtist: false,
|
hideOverflowArtist: false,
|
||||||
),
|
),
|
||||||
context.l10n.album: LinkText(
|
// context.l10n.album: LinkText(
|
||||||
track.album!.name!,
|
// track.album!.name!,
|
||||||
AlbumRoute(album: track.album!, id: track.album!.id!),
|
// AlbumRoute(album: track.album!, id: track.album!.id!),
|
||||||
overflow: TextOverflow.ellipsis,
|
// overflow: TextOverflow.ellipsis,
|
||||||
style: const TextStyle(color: Colors.blue),
|
// style: const TextStyle(color: Colors.blue),
|
||||||
),
|
// ),
|
||||||
context.l10n.duration: (track is SourcedTrack
|
context.l10n.duration: (track is SourcedTrack
|
||||||
? (track as SourcedTrack).sourceInfo.duration
|
? (track as SourcedTrack).sourceInfo.duration
|
||||||
: track.duration!)
|
: track.duration!)
|
||||||
|
@ -6,6 +6,7 @@ import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
|
|||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/collections/fake.dart';
|
import 'package:spotube/collections/fake.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/modules/album/album_card.dart';
|
import 'package:spotube/modules/album/album_card.dart';
|
||||||
import 'package:spotube/modules/artist/artist_card.dart';
|
import 'package:spotube/modules/artist/artist_card.dart';
|
||||||
import 'package:spotube/modules/playlist/playlist_card.dart';
|
import 'package:spotube/modules/playlist/playlist_card.dart';
|
||||||
@ -99,8 +100,9 @@ class HorizontalPlaybuttonCardView<T> extends HookWidget {
|
|||||||
|
|
||||||
return switch (item) {
|
return switch (item) {
|
||||||
PlaylistSimple() =>
|
PlaylistSimple() =>
|
||||||
PlaylistCard(item as PlaylistSimple),
|
PlaylistCard(item as SpotubeSimplePlaylistObject),
|
||||||
AlbumSimple() => AlbumCard(item as AlbumSimple),
|
AlbumSimple() =>
|
||||||
|
AlbumCard(item as SpotubeSimpleAlbumObject),
|
||||||
Artist() => ArtistCard(item as Artist),
|
Artist() => ArtistCard(item as Artist),
|
||||||
_ => const SizedBox.shrink(),
|
_ => const SizedBox.shrink(),
|
||||||
};
|
};
|
||||||
|
@ -215,9 +215,9 @@ class TrackOptions extends HookConsumerWidget {
|
|||||||
onSelected: (value) async {
|
onSelected: (value) async {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case TrackOptionValue.album:
|
case TrackOptionValue.album:
|
||||||
await context.navigateTo(
|
// await context.navigateTo(
|
||||||
AlbumRoute(id: track.album!.id!, album: track.album!),
|
// AlbumRoute(id: track.album!.id!, album: track.album!),
|
||||||
);
|
// );
|
||||||
break;
|
break;
|
||||||
case TrackOptionValue.delete:
|
case TrackOptionValue.delete:
|
||||||
await File((track as LocalTrack).path).delete();
|
await File((track as LocalTrack).path).delete();
|
||||||
|
@ -258,13 +258,15 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
_ => Align(
|
_ => Align(
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: LinkText(
|
/* child: LinkText(
|
||||||
track.album!.name!,
|
track.album!.name!,
|
||||||
AlbumRoute(
|
AlbumRoute(
|
||||||
album: track.album!, id: track.album!.id!),
|
album: track.album!,
|
||||||
|
id: track.album!.id!,
|
||||||
|
),
|
||||||
push: true,
|
push: true,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
), */
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -30,9 +30,9 @@ void useDeepLinking(WidgetRef ref, AppRouter router) {
|
|||||||
final album = await spotify.invoke((api) {
|
final album = await spotify.invoke((api) {
|
||||||
return api.albums.get(url.pathSegments.last);
|
return api.albums.get(url.pathSegments.last);
|
||||||
});
|
});
|
||||||
router.navigate(
|
// router.navigate(
|
||||||
AlbumRoute(id: album.id!, album: album),
|
// AlbumRoute(id: album.id!, album: album),
|
||||||
);
|
// );
|
||||||
break;
|
break;
|
||||||
case "artist":
|
case "artist":
|
||||||
router.navigate(ArtistRoute(artistId: url.pathSegments.last));
|
router.navigate(ArtistRoute(artistId: url.pathSegments.last));
|
||||||
@ -41,8 +41,8 @@ void useDeepLinking(WidgetRef ref, AppRouter router) {
|
|||||||
final playlist = await spotify.invoke((api) {
|
final playlist = await spotify.invoke((api) {
|
||||||
return api.playlists.get(url.pathSegments.last);
|
return api.playlists.get(url.pathSegments.last);
|
||||||
});
|
});
|
||||||
router
|
// router
|
||||||
.navigate(PlaylistRoute(id: playlist.id!, playlist: playlist));
|
// .navigate(PlaylistRoute(id: playlist.id!, playlist: playlist));
|
||||||
break;
|
break;
|
||||||
case "track":
|
case "track":
|
||||||
router.navigate(TrackRoute(trackId: url.pathSegments.last));
|
router.navigate(TrackRoute(trackId: url.pathSegments.last));
|
||||||
@ -72,9 +72,9 @@ void useDeepLinking(WidgetRef ref, AppRouter router) {
|
|||||||
final album = await spotify.invoke((api) {
|
final album = await spotify.invoke((api) {
|
||||||
return api.albums.get(endSegment);
|
return api.albums.get(endSegment);
|
||||||
});
|
});
|
||||||
await router.navigate(
|
// await router.navigate(
|
||||||
AlbumRoute(id: album.id!, album: album),
|
// AlbumRoute(id: album.id!, album: album),
|
||||||
);
|
// );
|
||||||
break;
|
break;
|
||||||
case "spotify:artist":
|
case "spotify:artist":
|
||||||
await router.navigate(ArtistRoute(artistId: endSegment));
|
await router.navigate(ArtistRoute(artistId: endSegment));
|
||||||
@ -86,9 +86,9 @@ void useDeepLinking(WidgetRef ref, AppRouter router) {
|
|||||||
final playlist = await spotify.invoke((api) {
|
final playlist = await spotify.invoke((api) {
|
||||||
return api.playlists.get(endSegment);
|
return api.playlists.get(endSegment);
|
||||||
});
|
});
|
||||||
await router.navigate(
|
// await router.navigate(
|
||||||
PlaylistRoute(id: playlist.id!, playlist: playlist),
|
// PlaylistRoute(id: playlist.id!, playlist: playlist),
|
||||||
);
|
// );
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -26,3 +26,15 @@ class SpotubeSimpleArtistObject with _$SpotubeSimpleArtistObject {
|
|||||||
factory SpotubeSimpleArtistObject.fromJson(Map<String, dynamic> json) =>
|
factory SpotubeSimpleArtistObject.fromJson(Map<String, dynamic> json) =>
|
||||||
_$SpotubeSimpleArtistObjectFromJson(json);
|
_$SpotubeSimpleArtistObjectFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SpotubeFullArtistObjectAsString on List<SpotubeFullArtistObject> {
|
||||||
|
String asString() {
|
||||||
|
return map((e) => e.name).join(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SpotubeSimpleArtistObjectAsString on List<SpotubeSimpleArtistObject> {
|
||||||
|
String asString() {
|
||||||
|
return map((e) => e.name).join(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,3 +11,33 @@ class SpotubeImageObject with _$SpotubeImageObject {
|
|||||||
factory SpotubeImageObject.fromJson(Map<String, dynamic> json) =>
|
factory SpotubeImageObject.fromJson(Map<String, dynamic> json) =>
|
||||||
_$SpotubeImageObjectFromJson(json);
|
_$SpotubeImageObjectFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ImagePlaceholder {
|
||||||
|
albumArt,
|
||||||
|
artist,
|
||||||
|
collection,
|
||||||
|
online,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SpotifyImageExtensions on List<SpotubeImageObject>? {
|
||||||
|
String asUrlString({
|
||||||
|
int index = 1,
|
||||||
|
required ImagePlaceholder placeholder,
|
||||||
|
}) {
|
||||||
|
final String placeholderUrl = {
|
||||||
|
ImagePlaceholder.albumArt: Assets.albumPlaceholder.path,
|
||||||
|
ImagePlaceholder.artist: Assets.userPlaceholder.path,
|
||||||
|
ImagePlaceholder.collection: Assets.placeholder.path,
|
||||||
|
ImagePlaceholder.online:
|
||||||
|
"https://avatars.dicebear.com/api/bottts/${PrimitiveUtils.uuid.v4()}.png",
|
||||||
|
}[placeholder]!;
|
||||||
|
|
||||||
|
final sortedImage = this?.sorted((a, b) => a.width!.compareTo(b.width!));
|
||||||
|
|
||||||
|
return sortedImage != null && sortedImage.isNotEmpty
|
||||||
|
? sortedImage[
|
||||||
|
index > sortedImage.length - 1 ? sortedImage.length - 1 : index]
|
||||||
|
.url
|
||||||
|
: placeholderUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
library metadata_objects;
|
library metadata_objects;
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
|
import 'package:spotube/utils/primitive_utils.dart';
|
||||||
|
|
||||||
part 'metadata.g.dart';
|
part 'metadata.g.dart';
|
||||||
part 'metadata.freezed.dart';
|
part 'metadata.freezed.dart';
|
||||||
|
@ -2,20 +2,20 @@ part of 'metadata.dart';
|
|||||||
|
|
||||||
@Freezed(genericArgumentFactories: true)
|
@Freezed(genericArgumentFactories: true)
|
||||||
class SpotubePaginationResponseObject<T>
|
class SpotubePaginationResponseObject<T>
|
||||||
with _$SpotubePaginationResponseObject {
|
with _$SpotubePaginationResponseObject<T> {
|
||||||
factory SpotubePaginationResponseObject({
|
factory SpotubePaginationResponseObject({
|
||||||
required int limit,
|
required int limit,
|
||||||
required int? nextOffset,
|
required int? nextOffset,
|
||||||
required int total,
|
required int total,
|
||||||
required bool hasMore,
|
required bool hasMore,
|
||||||
required List<T> items,
|
required List<T> items,
|
||||||
}) = _SpotubePaginationResponseObject;
|
}) = _SpotubePaginationResponseObject<T>;
|
||||||
|
|
||||||
factory SpotubePaginationResponseObject.fromJson(
|
factory SpotubePaginationResponseObject.fromJson(
|
||||||
Map<String, Object?> json,
|
Map<String, Object?> json,
|
||||||
T Function(Map<String, dynamic> json) fromJsonT,
|
T Function(Map<String, dynamic> json) fromJsonT,
|
||||||
) =>
|
) =>
|
||||||
_$SpotubePaginationResponseObjectFromJson(
|
_$SpotubePaginationResponseObjectFromJson<T>(
|
||||||
json,
|
json,
|
||||||
(json) => fromJsonT(json as Map<String, dynamic>),
|
(json) => fromJsonT(json as Map<String, dynamic>),
|
||||||
);
|
);
|
||||||
|
@ -9,13 +9,14 @@ import 'package:spotube/components/playbutton_view/playbutton_card.dart';
|
|||||||
import 'package:spotube/components/playbutton_view/playbutton_tile.dart';
|
import 'package:spotube/components/playbutton_view/playbutton_tile.dart';
|
||||||
import 'package:spotube/extensions/artist_simple.dart';
|
import 'package:spotube/extensions/artist_simple.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
|
||||||
import 'package:spotube/extensions/track.dart';
|
import 'package:spotube/extensions/track.dart';
|
||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/provider/audio_player/querying_track_info.dart';
|
import 'package:spotube/provider/audio_player/querying_track_info.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
import 'package:spotube/provider/audio_player/audio_player.dart';
|
import 'package:spotube/provider/audio_player/audio_player.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/tracks/album.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:spotube/services/audio_player/audio_player.dart';
|
import 'package:spotube/services/audio_player/audio_player.dart';
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ extension FormattedAlbumType on AlbumType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AlbumCard extends HookConsumerWidget {
|
class AlbumCard extends HookConsumerWidget {
|
||||||
final AlbumSimple album;
|
final SpotubeSimpleAlbumObject album;
|
||||||
final bool _isTile;
|
final bool _isTile;
|
||||||
const AlbumCard(
|
const AlbumCard(
|
||||||
this.album, {
|
this.album, {
|
||||||
@ -46,18 +47,15 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
final isFetchingActiveTrack = ref.watch(queryingTrackInfoProvider);
|
final isFetchingActiveTrack = ref.watch(queryingTrackInfoProvider);
|
||||||
|
|
||||||
bool isPlaylistPlaying = useMemoized(
|
bool isPlaylistPlaying = useMemoized(
|
||||||
() => playlist.containsCollection(album.id!),
|
() => playlist.containsCollection(album.id),
|
||||||
[playlist, album.id],
|
[playlist, album.id],
|
||||||
);
|
);
|
||||||
|
|
||||||
final updating = useState(false);
|
final updating = useState(false);
|
||||||
|
|
||||||
Future<List<Track>> fetchAllTrack() async {
|
Future<List<Track>> fetchAllTrack() async {
|
||||||
if (album.tracks != null && album.tracks!.isNotEmpty) {
|
// return ref.read(metadataPluginAlbumTracksProvider(album).notifier).fetchAll();
|
||||||
return album.tracks!.asTracks(album, ref);
|
return [];
|
||||||
}
|
|
||||||
await ref.read(albumTracksProvider(album).future);
|
|
||||||
return ref.read(albumTracksProvider(album).notifier).fetchAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageUrl = album.images.asUrlString(
|
var imageUrl = album.images.asUrlString(
|
||||||
@ -65,11 +63,10 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
var isLoading =
|
var isLoading =
|
||||||
(isPlaylistPlaying && isFetchingActiveTrack) || updating.value;
|
(isPlaylistPlaying && isFetchingActiveTrack) || updating.value;
|
||||||
var description =
|
var description = "${album.albumType} • ${album.artists.asString()}";
|
||||||
"${album.albumType?.formatted} • ${album.artists?.asString() ?? ""}";
|
|
||||||
|
|
||||||
void onTap() {
|
void onTap() {
|
||||||
context.navigateTo(AlbumRoute(id: album.id!, album: album));
|
context.navigateTo(AlbumRoute(id: album.id, album: album));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onPlaybuttonPressed() async {
|
void onPlaybuttonPressed() async {
|
||||||
@ -90,13 +87,13 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
await remotePlayback.load(
|
await remotePlayback.load(
|
||||||
WebSocketLoadEventData.album(
|
WebSocketLoadEventData.album(
|
||||||
tracks: fetchedTracks,
|
tracks: fetchedTracks,
|
||||||
collection: album,
|
// collection: album,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await playlistNotifier.load(fetchedTracks, autoPlay: true);
|
await playlistNotifier.load(fetchedTracks, autoPlay: true);
|
||||||
playlistNotifier.addCollection(album.id!);
|
playlistNotifier.addCollection(album.id);
|
||||||
historyNotifier.addAlbums([album]);
|
// historyNotifier.addAlbums([album]);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
updating.value = false;
|
updating.value = false;
|
||||||
@ -114,8 +111,8 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
|
|
||||||
if (fetchedTracks.isEmpty) return;
|
if (fetchedTracks.isEmpty) return;
|
||||||
playlistNotifier.addTracks(fetchedTracks);
|
playlistNotifier.addTracks(fetchedTracks);
|
||||||
playlistNotifier.addCollection(album.id!);
|
playlistNotifier.addCollection(album.id);
|
||||||
historyNotifier.addAlbums([album]);
|
// historyNotifier.addAlbums([album]);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
showToast(
|
showToast(
|
||||||
context: context,
|
context: context,
|
||||||
@ -147,7 +144,7 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
isPlaying: isPlaylistPlaying,
|
isPlaying: isPlaylistPlaying,
|
||||||
isLoading: isLoading,
|
isLoading: isLoading,
|
||||||
title: album.name!,
|
title: album.name,
|
||||||
description: description,
|
description: description,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onPlaybuttonPressed: onPlaybuttonPressed,
|
onPlaybuttonPressed: onPlaybuttonPressed,
|
||||||
@ -159,7 +156,7 @@ class AlbumCard extends HookConsumerWidget {
|
|||||||
imageUrl: imageUrl,
|
imageUrl: imageUrl,
|
||||||
isPlaying: isPlaylistPlaying,
|
isPlaying: isPlaylistPlaying,
|
||||||
isLoading: isLoading,
|
isLoading: isLoading,
|
||||||
title: album.name!,
|
title: album.name,
|
||||||
description: description,
|
description: description,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
onPlaybuttonPressed: onPlaybuttonPressed,
|
onPlaybuttonPressed: onPlaybuttonPressed,
|
||||||
|
@ -99,9 +99,9 @@ class FriendItem extends HookConsumerWidget {
|
|||||||
(api) => api.albums.get(friend.track.album.id),
|
(api) => api.albums.get(friend.track.album.id),
|
||||||
);
|
);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.navigateTo(
|
// context.navigateTo(
|
||||||
AlbumRoute(id: album.id!, album: album),
|
// AlbumRoute(id: album.id!, album: album),
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -47,9 +47,9 @@ class GenreSectionCardPlaylistCard extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.navigateTo(
|
// context.navigateTo(
|
||||||
PlaylistRoute(id: playlist.id!, playlist: playlist),
|
// PlaylistRoute(id: playlist.id!, playlist: playlist),
|
||||||
);
|
// );
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -9,8 +9,8 @@ import 'package:spotube/components/dialogs/select_device_dialog.dart';
|
|||||||
import 'package:spotube/components/playbutton_view/playbutton_card.dart';
|
import 'package:spotube/components/playbutton_view/playbutton_card.dart';
|
||||||
import 'package:spotube/components/playbutton_view/playbutton_tile.dart';
|
import 'package:spotube/components/playbutton_view/playbutton_tile.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
|
||||||
import 'package:spotube/models/connect/connect.dart';
|
import 'package:spotube/models/connect/connect.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/provider/audio_player/querying_track_info.dart';
|
import 'package:spotube/provider/audio_player/querying_track_info.dart';
|
||||||
import 'package:spotube/provider/connect/connect.dart';
|
import 'package:spotube/provider/connect/connect.dart';
|
||||||
import 'package:spotube/provider/history/history.dart';
|
import 'package:spotube/provider/history/history.dart';
|
||||||
@ -20,7 +20,7 @@ import 'package:spotube/services/audio_player/audio_player.dart';
|
|||||||
import 'package:stroke_text/stroke_text.dart';
|
import 'package:stroke_text/stroke_text.dart';
|
||||||
|
|
||||||
class PlaylistCard extends HookConsumerWidget {
|
class PlaylistCard extends HookConsumerWidget {
|
||||||
final PlaylistSimple playlist;
|
final SpotubeSimplePlaylistObject playlist;
|
||||||
final bool _isTile;
|
final bool _isTile;
|
||||||
|
|
||||||
const PlaylistCard(
|
const PlaylistCard(
|
||||||
@ -43,7 +43,7 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
final playing =
|
final playing =
|
||||||
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
|
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
|
||||||
bool isPlaylistPlaying = useMemoized(
|
bool isPlaylistPlaying = useMemoized(
|
||||||
() => playlistQueue.containsCollection(playlist.id!),
|
() => playlistQueue.containsCollection(playlist.id),
|
||||||
[playlistQueue, playlist.id],
|
[playlistQueue, playlist.id],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -55,8 +55,7 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
return await ref.read(likedTracksProvider.future);
|
return await ref.read(likedTracksProvider.future);
|
||||||
}
|
}
|
||||||
|
|
||||||
final result =
|
final result = await ref.read(playlistTracksProvider(playlist.id).future);
|
||||||
await ref.read(playlistTracksProvider(playlist.id!).future);
|
|
||||||
|
|
||||||
return result.items;
|
return result.items;
|
||||||
}
|
}
|
||||||
@ -68,11 +67,11 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
return initialTracks;
|
return initialTracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ref.read(playlistTracksProvider(playlist.id!).notifier).fetchAll();
|
return ref.read(playlistTracksProvider(playlist.id).notifier).fetchAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTap() {
|
void onTap() {
|
||||||
context.navigateTo(PlaylistRoute(id: playlist.id!, playlist: playlist));
|
context.navigateTo(PlaylistRoute(id: playlist.id, playlist: playlist));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onPlaybuttonPressed() async {
|
void onPlaybuttonPressed() async {
|
||||||
@ -96,13 +95,13 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
await remotePlayback.load(
|
await remotePlayback.load(
|
||||||
WebSocketLoadEventData.playlist(
|
WebSocketLoadEventData.playlist(
|
||||||
tracks: allTracks,
|
tracks: allTracks,
|
||||||
collection: playlist,
|
// collection: playlist,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await playlistNotifier.load(fetchedInitialTracks, autoPlay: true);
|
await playlistNotifier.load(fetchedInitialTracks, autoPlay: true);
|
||||||
playlistNotifier.addCollection(playlist.id!);
|
playlistNotifier.addCollection(playlist.id);
|
||||||
historyNotifier.addPlaylists([playlist]);
|
// historyNotifier.addPlaylists([playlist]);
|
||||||
|
|
||||||
final allTracks = await fetchAllTracks();
|
final allTracks = await fetchAllTracks();
|
||||||
|
|
||||||
@ -126,8 +125,8 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
if (fetchedInitialTracks.isEmpty) return;
|
if (fetchedInitialTracks.isEmpty) return;
|
||||||
|
|
||||||
playlistNotifier.addTracks(fetchedInitialTracks);
|
playlistNotifier.addTracks(fetchedInitialTracks);
|
||||||
playlistNotifier.addCollection(playlist.id!);
|
playlistNotifier.addCollection(playlist.id);
|
||||||
historyNotifier.addPlaylists([playlist]);
|
// historyNotifier.addPlaylists([playlist]);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
showToast(
|
showToast(
|
||||||
context: context,
|
context: context,
|
||||||
@ -160,50 +159,49 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
final isLoading =
|
final isLoading =
|
||||||
(isPlaylistPlaying && isFetchingActiveTrack) || updating.value;
|
(isPlaylistPlaying && isFetchingActiveTrack) || updating.value;
|
||||||
final isOwner = playlist.owner?.id == me.asData?.value.id &&
|
final isOwner =
|
||||||
me.asData?.value.id != null;
|
playlist.owner.id == me.asData?.value.id && me.asData?.value.id != null;
|
||||||
|
|
||||||
final image =
|
final image = playlist.owner.name == "Spotify" && Env.disableSpotifyImages
|
||||||
playlist.owner?.displayName == "Spotify" && Env.disableSpotifyImages
|
? Consumer(
|
||||||
? Consumer(
|
builder: (context, ref, child) {
|
||||||
builder: (context, ref, child) {
|
final (:color, :colorBlendMode, :src, :placement) =
|
||||||
final (:color, :colorBlendMode, :src, :placement) =
|
ref.watch(playlistImageProvider(playlist.id));
|
||||||
ref.watch(playlistImageProvider(playlist.id!));
|
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
src,
|
src,
|
||||||
color: color,
|
color: color,
|
||||||
colorBlendMode: colorBlendMode,
|
colorBlendMode: colorBlendMode,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
top: placement == Alignment.topLeft ? 10 : null,
|
||||||
|
left: 10,
|
||||||
|
bottom: placement == Alignment.bottomLeft ? 10 : null,
|
||||||
|
child: StrokeText(
|
||||||
|
text: playlist.name,
|
||||||
|
strokeColor: Colors.white,
|
||||||
|
strokeWidth: 3,
|
||||||
|
textColor: Colors.black,
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
),
|
||||||
top: placement == Alignment.topLeft ? 10 : null,
|
),
|
||||||
left: 10,
|
],
|
||||||
bottom: placement == Alignment.bottomLeft ? 10 : null,
|
);
|
||||||
child: StrokeText(
|
},
|
||||||
text: playlist.name!,
|
)
|
||||||
strokeColor: Colors.white,
|
: null;
|
||||||
strokeWidth: 3,
|
|
||||||
textColor: Colors.black,
|
|
||||||
textStyle: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontStyle: FontStyle.italic,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (_isTile) {
|
if (_isTile) {
|
||||||
return PlaybuttonTile(
|
return PlaybuttonTile(
|
||||||
title: playlist.name!,
|
title: playlist.name,
|
||||||
description: playlist.description,
|
description: playlist.description,
|
||||||
image: image,
|
image: image,
|
||||||
imageUrl: image == null ? imageUrl : null,
|
imageUrl: image == null ? imageUrl : null,
|
||||||
@ -217,7 +215,7 @@ class PlaylistCard extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return PlaybuttonCard(
|
return PlaybuttonCard(
|
||||||
title: playlist.name!,
|
title: playlist.name,
|
||||||
description: playlist.description,
|
description: playlist.description,
|
||||||
image: image,
|
image: image,
|
||||||
imageUrl: image == null ? imageUrl : null,
|
imageUrl: image == null ? imageUrl : null,
|
||||||
|
@ -32,19 +32,19 @@ class StatsAlbumItem extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text("${album.albumType?.formatted} • "),
|
Text("${album.albumType?.formatted} • "),
|
||||||
Flexible(
|
// Flexible(
|
||||||
child: ArtistLink(
|
// child: ArtistLink(
|
||||||
artists: album.artists ?? [],
|
// artists: album.artists ?? [],
|
||||||
mainAxisAlignment: WrapAlignment.start,
|
// mainAxisAlignment: WrapAlignment.start,
|
||||||
onOverflowArtistClick: () =>
|
// onOverflowArtistClick: () =>
|
||||||
context.navigateTo(AlbumRoute(id: album.id!, album: album)),
|
// context.navigateTo(AlbumRoute(id: album.id!, album: album)),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
trailing: info,
|
trailing: info,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.navigateTo(AlbumRoute(id: album.id!, album: album));
|
// context.navigateTo(AlbumRoute(id: album.id!, album: album));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class StatsPlaylistItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
trailing: info,
|
trailing: info,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.navigateTo(PlaylistRoute(id: playlist.id!, playlist: playlist));
|
// context.navigateTo(PlaylistRoute(id: playlist.id!, playlist: playlist));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,19 @@ import 'package:flutter/material.dart' as material;
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
|
||||||
import 'package:spotube/components/track_presentation/presentation_props.dart';
|
import 'package:spotube/components/track_presentation/presentation_props.dart';
|
||||||
import 'package:spotube/components/track_presentation/track_presentation.dart';
|
import 'package:spotube/components/track_presentation/track_presentation.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/library/albums.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/tracks/album.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class AlbumPage extends HookConsumerWidget {
|
class AlbumPage extends HookConsumerWidget {
|
||||||
static const name = "album";
|
static const name = "album";
|
||||||
|
|
||||||
final AlbumSimple album;
|
final SpotubeSimpleAlbumObject album;
|
||||||
final String id;
|
final String id;
|
||||||
const AlbumPage({
|
const AlbumPage({
|
||||||
super.key,
|
super.key,
|
||||||
@ -23,16 +24,19 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final tracks = ref.watch(albumTracksProvider(album));
|
final tracks = ref.watch(metadataPluginAlbumTracksProvider(album.id));
|
||||||
final tracksNotifier = ref.watch(albumTracksProvider(album).notifier);
|
final tracksNotifier =
|
||||||
final favoriteAlbumsNotifier = ref.watch(favoriteAlbumsProvider.notifier);
|
ref.watch(metadataPluginAlbumTracksProvider(album.id).notifier);
|
||||||
final isSavedAlbum = ref.watch(albumsIsSavedProvider(album.id!));
|
final favoriteAlbumsNotifier =
|
||||||
|
ref.watch(metadataPluginSavedAlbumsProvider.notifier);
|
||||||
|
final isSavedAlbum =
|
||||||
|
ref.watch(metadataPluginIsSavedAlbumProvider(album.id));
|
||||||
|
|
||||||
return material.RefreshIndicator.adaptive(
|
return material.RefreshIndicator.adaptive(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
ref.invalidate(albumTracksProvider(album));
|
ref.invalidate(metadataPluginAlbumTracksProvider(album.id));
|
||||||
ref.invalidate(favoriteAlbumsProvider);
|
ref.invalidate(metadataPluginIsSavedAlbumProvider(album.id));
|
||||||
ref.invalidate(albumsIsSavedProvider(album.id!));
|
ref.invalidate(metadataPluginSavedAlbumsProvider);
|
||||||
},
|
},
|
||||||
child: TrackPresentation(
|
child: TrackPresentation(
|
||||||
options: TrackPresentationOptions(
|
options: TrackPresentationOptions(
|
||||||
@ -40,10 +44,10 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
image: album.images.asUrlString(
|
image: album.images.asUrlString(
|
||||||
placeholder: ImagePlaceholder.albumArt,
|
placeholder: ImagePlaceholder.albumArt,
|
||||||
),
|
),
|
||||||
title: album.name!,
|
title: album.name,
|
||||||
description:
|
description:
|
||||||
"${context.l10n.released} • ${album.releaseDate} • ${album.artists!.first.name}",
|
"${context.l10n.released} • ${album.releaseDate} • ${album.artists.first.name}",
|
||||||
tracks: tracks.asData?.value.items ?? [],
|
tracks: [],
|
||||||
pagination: PaginationProps(
|
pagination: PaginationProps(
|
||||||
hasNextPage: tracks.asData?.value.hasMore ?? false,
|
hasNextPage: tracks.asData?.value.hasMore ?? false,
|
||||||
isLoading: tracks.isLoading || tracks.isLoadingNextPage,
|
isLoading: tracks.isLoading || tracks.isLoadingNextPage,
|
||||||
@ -51,24 +55,24 @@ class AlbumPage extends HookConsumerWidget {
|
|||||||
await tracksNotifier.fetchMore();
|
await tracksNotifier.fetchMore();
|
||||||
},
|
},
|
||||||
onFetchAll: () async {
|
onFetchAll: () async {
|
||||||
return tracksNotifier.fetchAll();
|
// return tracksNotifier.fetchAll();
|
||||||
|
return [];
|
||||||
},
|
},
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
ref.invalidate(albumTracksProvider(album));
|
// ref.invalidate(albumTracksProvider(album));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
routePath: "/album/${album.id}",
|
routePath: "/album/${album.id}",
|
||||||
shareUrl: album.externalUrls?.spotify ??
|
shareUrl: album.externalUri,
|
||||||
"https://open.spotify.com/album/${album.id}",
|
|
||||||
isLiked: isSavedAlbum.asData?.value ?? false,
|
isLiked: isSavedAlbum.asData?.value ?? false,
|
||||||
owner: album.artists!.first.name,
|
owner: album.artists.first.name,
|
||||||
onHeart: isSavedAlbum.asData?.value == null
|
onHeart: isSavedAlbum.asData?.value == null
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
if (isSavedAlbum.asData!.value) {
|
if (isSavedAlbum.asData!.value) {
|
||||||
await favoriteAlbumsNotifier.removeFavorites([album.id!]);
|
await favoriteAlbumsNotifier.removeFavorite([album]);
|
||||||
} else {
|
} else {
|
||||||
await favoriteAlbumsNotifier.addFavorites([album.id!]);
|
await favoriteAlbumsNotifier.addFavorite([album]);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
@ -43,49 +43,49 @@ class HomeFeedSectionPage extends HookConsumerWidget {
|
|||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
slivers: [
|
slivers: [
|
||||||
if (isArtist)
|
// if (isArtist)
|
||||||
SliverGrid.builder(
|
// SliverGrid.builder(
|
||||||
gridDelegate:
|
// gridDelegate:
|
||||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
// const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
maxCrossAxisExtent: 200,
|
// maxCrossAxisExtent: 200,
|
||||||
mainAxisExtent: 250,
|
// mainAxisExtent: 250,
|
||||||
crossAxisSpacing: 8,
|
// crossAxisSpacing: 8,
|
||||||
mainAxisSpacing: 8,
|
// mainAxisSpacing: 8,
|
||||||
),
|
// ),
|
||||||
itemCount: section.items.length,
|
// itemCount: section.items.length,
|
||||||
itemBuilder: (context, index) {
|
// itemBuilder: (context, index) {
|
||||||
final item = section.items[index];
|
// final item = section.items[index];
|
||||||
return ArtistCard(item.artist!.asArtist);
|
// return ArtistCard(item.artist!.asArtist);
|
||||||
},
|
// },
|
||||||
)
|
// )
|
||||||
else
|
// else
|
||||||
PlaybuttonView(
|
// PlaybuttonView(
|
||||||
controller: controller,
|
// controller: controller,
|
||||||
itemCount: section.items.length,
|
// itemCount: section.items.length,
|
||||||
hasMore: false,
|
// hasMore: false,
|
||||||
isLoading: false,
|
// isLoading: false,
|
||||||
onRequestMore: () => {},
|
// onRequestMore: () => {},
|
||||||
listItemBuilder: (context, index) {
|
// listItemBuilder: (context, index) {
|
||||||
final item = section.items[index];
|
// final item = section.items[index];
|
||||||
if (item.album != null) {
|
// if (item.album != null) {
|
||||||
return AlbumCard.tile(item.album!.asAlbum);
|
// return AlbumCard.tile(item.album!.asAlbum);
|
||||||
}
|
// }
|
||||||
if (item.playlist != null) {
|
// if (item.playlist != null) {
|
||||||
return PlaylistCard.tile(item.playlist!.asPlaylist);
|
// return PlaylistCard.tile(item.playlist!.asPlaylist);
|
||||||
}
|
// }
|
||||||
return const SizedBox.shrink();
|
// return const SizedBox.shrink();
|
||||||
},
|
// },
|
||||||
gridItemBuilder: (context, index) {
|
// gridItemBuilder: (context, index) {
|
||||||
final item = section.items[index];
|
// final item = section.items[index];
|
||||||
if (item.album != null) {
|
// if (item.album != null) {
|
||||||
return AlbumCard(item.album!.asAlbum);
|
// return AlbumCard(item.album!.asAlbum);
|
||||||
}
|
// }
|
||||||
if (item.playlist != null) {
|
// if (item.playlist != null) {
|
||||||
return PlaylistCard(item.playlist!.asPlaylist);
|
// return PlaylistCard(item.playlist!.asPlaylist);
|
||||||
}
|
// }
|
||||||
return const SizedBox.shrink();
|
// return const SizedBox.shrink();
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
const SliverToBoxAdapter(
|
const SliverToBoxAdapter(
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: SizedBox(),
|
child: SizedBox(),
|
||||||
|
@ -115,25 +115,25 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SliverGap(20),
|
const SliverGap(20),
|
||||||
SliverSafeArea(
|
// SliverSafeArea(
|
||||||
top: false,
|
// top: false,
|
||||||
sliver: SliverPadding(
|
// sliver: SliverPadding(
|
||||||
padding: EdgeInsets.symmetric(
|
// padding: EdgeInsets.symmetric(
|
||||||
horizontal: mediaQuery.mdAndDown ? 12 : 24,
|
// horizontal: mediaQuery.mdAndDown ? 12 : 24,
|
||||||
),
|
// ),
|
||||||
sliver: PlaybuttonView(
|
// sliver: PlaybuttonView(
|
||||||
controller: scrollController,
|
// controller: scrollController,
|
||||||
itemCount: playlists.asData?.value.items.length ?? 0,
|
// itemCount: playlists.asData?.value.items.length ?? 0,
|
||||||
isLoading: playlists.isLoading,
|
// isLoading: playlists.isLoading,
|
||||||
hasMore: playlists.asData?.value.hasMore == true,
|
// hasMore: playlists.asData?.value.hasMore == true,
|
||||||
onRequestMore: playlistsNotifier.fetchMore,
|
// onRequestMore: playlistsNotifier.fetchMore,
|
||||||
listItemBuilder: (context, index) => PlaylistCard.tile(
|
// listItemBuilder: (context, index) => PlaylistCard.tile(
|
||||||
playlists.asData!.value.items[index]),
|
// playlists.asData!.value.items[index]),
|
||||||
gridItemBuilder: (context, index) =>
|
// gridItemBuilder: (context, index) =>
|
||||||
PlaylistCard(playlists.asData!.value.items[index]),
|
// PlaylistCard(playlists.asData!.value.items[index]),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
const SliverGap(20),
|
const SliverGap(20),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -137,14 +137,14 @@ class PlaylistGenerateResultPage extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (playlist != null && context.mounted) {
|
// if (playlist != null && context.mounted) {
|
||||||
context.navigateTo(
|
// context.navigateTo(
|
||||||
PlaylistRoute(
|
// PlaylistRoute(
|
||||||
id: playlist.id!,
|
// id: playlist.id!,
|
||||||
playlist: playlist,
|
// playlist: playlist,
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
},
|
},
|
||||||
child: Text(context.l10n.create_a_playlist),
|
child: Text(context.l10n.create_a_playlist),
|
||||||
),
|
),
|
||||||
|
@ -15,6 +15,8 @@ import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
|
|||||||
import 'package:spotube/components/fallbacks/anonymous_fallback.dart';
|
import 'package:spotube/components/fallbacks/anonymous_fallback.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/authentication/authentication.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/auth.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/library/albums.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
|
||||||
@ -25,9 +27,10 @@ class UserAlbumsPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final auth = ref.watch(authenticationProvider);
|
final authenticated = ref.watch(metadataPluginAuthenticatedProvider);
|
||||||
final albumsQuery = ref.watch(favoriteAlbumsProvider);
|
final albumsQuery = ref.watch(metadataPluginSavedAlbumsProvider);
|
||||||
final albumsQueryNotifier = ref.watch(favoriteAlbumsProvider.notifier);
|
final albumsQueryNotifier =
|
||||||
|
ref.watch(metadataPluginSavedAlbumsProvider.notifier);
|
||||||
|
|
||||||
final controller = useScrollController();
|
final controller = useScrollController();
|
||||||
|
|
||||||
@ -39,7 +42,7 @@ class UserAlbumsPage extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
return albumsQuery.asData?.value.items
|
return albumsQuery.asData?.value.items
|
||||||
.map((e) => (
|
.map((e) => (
|
||||||
weightedRatio(e.name!, searchText.value),
|
weightedRatio(e.name, searchText.value),
|
||||||
e,
|
e,
|
||||||
))
|
))
|
||||||
.sorted((a, b) => b.$1.compareTo(a.$1))
|
.sorted((a, b) => b.$1.compareTo(a.$1))
|
||||||
@ -49,7 +52,7 @@ class UserAlbumsPage extends HookConsumerWidget {
|
|||||||
[];
|
[];
|
||||||
}, [albumsQuery.asData?.value, searchText.value]);
|
}, [albumsQuery.asData?.value, searchText.value]);
|
||||||
|
|
||||||
if (auth.asData?.value == null) {
|
if (authenticated.asData?.value != true) {
|
||||||
return const AnonymousFallback();
|
return const AnonymousFallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,18 +5,20 @@ import 'package:collection/collection.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shadcn_flutter/shadcn_flutter.dart' hide Image;
|
import 'package:shadcn_flutter/shadcn_flutter.dart' hide Image;
|
||||||
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
|
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
|
||||||
|
import 'package:spotube/collections/assets.gen.dart';
|
||||||
|
|
||||||
import 'package:spotify/spotify.dart';
|
|
||||||
import 'package:spotube/collections/routes.gr.dart';
|
import 'package:spotube/collections/routes.gr.dart';
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/playbutton_view/playbutton_view.dart';
|
import 'package:spotube/components/playbutton_view/playbutton_view.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/modules/playlist/playlist_create_dialog.dart';
|
import 'package:spotube/modules/playlist/playlist_create_dialog.dart';
|
||||||
import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
|
import 'package:spotube/components/inter_scrollbar/inter_scrollbar.dart';
|
||||||
import 'package:spotube/components/fallbacks/anonymous_fallback.dart';
|
import 'package:spotube/components/fallbacks/anonymous_fallback.dart';
|
||||||
import 'package:spotube/modules/playlist/playlist_card.dart';
|
import 'package:spotube/modules/playlist/playlist_card.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/provider/authentication/authentication.dart';
|
import 'package:spotube/provider/metadata_plugin/auth.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/metadata_plugin/library/playlists.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/user.dart';
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
@ -28,42 +30,47 @@ class UserPlaylistsPage extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final searchText = useState('');
|
final searchText = useState('');
|
||||||
|
|
||||||
final auth = ref.watch(authenticationProvider);
|
final authenticated = ref.watch(metadataPluginAuthenticatedProvider);
|
||||||
|
|
||||||
final playlistsQuery = ref.watch(favoritePlaylistsProvider);
|
final me = ref.watch(metadataPluginUserProvider);
|
||||||
|
final playlistsQuery = ref.watch(metadataPluginSavedPlaylistsProvider);
|
||||||
final playlistsQueryNotifier =
|
final playlistsQueryNotifier =
|
||||||
ref.watch(favoritePlaylistsProvider.notifier);
|
ref.watch(metadataPluginSavedPlaylistsProvider.notifier);
|
||||||
|
|
||||||
final likedTracksPlaylist = useMemoized(
|
final likedTracksPlaylist = useMemoized(
|
||||||
() => PlaylistSimple()
|
() => me.asData?.value == null
|
||||||
..name = context.l10n.liked_tracks
|
? null
|
||||||
..description = context.l10n.liked_tracks_description
|
: SpotubeSimplePlaylistObject(
|
||||||
..type = "playlist"
|
id: "liked-tracks",
|
||||||
..collaborative = false
|
name: context.l10n.liked_tracks,
|
||||||
..public = false
|
description: context.l10n.liked_tracks_description,
|
||||||
..id = "user-liked-tracks"
|
externalUri: "",
|
||||||
..images = [
|
owner: me.asData!.value!,
|
||||||
Image()
|
images: [
|
||||||
..height = 300
|
SpotubeImageObject(
|
||||||
..width = 300
|
url: Assets.likedTracks.path,
|
||||||
..url = "assets/liked-tracks.jpg"
|
width: 300,
|
||||||
],
|
height: 300,
|
||||||
[context.l10n],
|
)
|
||||||
|
]),
|
||||||
|
[context.l10n, me.asData?.value],
|
||||||
);
|
);
|
||||||
|
|
||||||
final playlists = useMemoized(
|
final playlists = useMemoized(
|
||||||
() {
|
() {
|
||||||
if (searchText.value.isEmpty) {
|
if (searchText.value.isEmpty) {
|
||||||
return [
|
return [
|
||||||
likedTracksPlaylist,
|
if (likedTracksPlaylist != null) likedTracksPlaylist,
|
||||||
...?playlistsQuery.asData?.value.items,
|
...?playlistsQuery.asData?.value.items
|
||||||
|
as List<SpotubeSimplePlaylistObject>?,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
likedTracksPlaylist,
|
if (likedTracksPlaylist != null) likedTracksPlaylist,
|
||||||
...?playlistsQuery.asData?.value.items,
|
...?playlistsQuery.asData?.value.items
|
||||||
|
as List<SpotubeSimplePlaylistObject>?,
|
||||||
]
|
]
|
||||||
.map((e) => (weightedRatio(e.name!, searchText.value), e))
|
.map((e) => (weightedRatio(e.name, searchText.value), e))
|
||||||
.sorted((a, b) => b.$1.compareTo(a.$1))
|
.sorted((a, b) => b.$1.compareTo(a.$1))
|
||||||
.where((e) => e.$1 > 50)
|
.where((e) => e.$1 > 50)
|
||||||
.map((e) => e.$2)
|
.map((e) => e.$2)
|
||||||
@ -74,13 +81,13 @@ class UserPlaylistsPage extends HookConsumerWidget {
|
|||||||
|
|
||||||
final controller = useScrollController();
|
final controller = useScrollController();
|
||||||
|
|
||||||
if (auth.asData?.value == null) {
|
if (authenticated.asData?.value != true) {
|
||||||
return const AnonymousFallback();
|
return const AnonymousFallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
return material.RefreshIndicator.adaptive(
|
return material.RefreshIndicator.adaptive(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
ref.invalidate(favoritePlaylistsProvider);
|
ref.invalidate(metadataPluginSavedPlaylistsProvider);
|
||||||
},
|
},
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
bottom: false,
|
bottom: false,
|
||||||
|
@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:spotify/spotify.dart';
|
import 'package:spotify/spotify.dart';
|
||||||
import 'package:spotube/components/track_presentation/presentation_props.dart';
|
import 'package:spotube/components/track_presentation/presentation_props.dart';
|
||||||
import 'package:spotube/components/track_presentation/track_presentation.dart';
|
import 'package:spotube/components/track_presentation/track_presentation.dart';
|
||||||
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
import 'package:spotube/pages/playlist/playlist.dart';
|
import 'package:spotube/pages/playlist/playlist.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
@ -12,7 +13,7 @@ import 'package:auto_route/auto_route.dart';
|
|||||||
class LikedPlaylistPage extends HookConsumerWidget {
|
class LikedPlaylistPage extends HookConsumerWidget {
|
||||||
static const name = PlaylistPage.name;
|
static const name = PlaylistPage.name;
|
||||||
|
|
||||||
final PlaylistSimple playlist;
|
final SpotubeSimplePlaylistObject playlist;
|
||||||
const LikedPlaylistPage({
|
const LikedPlaylistPage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.playlist,
|
required this.playlist,
|
||||||
@ -42,14 +43,14 @@ class LikedPlaylistPage extends HookConsumerWidget {
|
|||||||
ref.invalidate(likedTracksProvider);
|
ref.invalidate(likedTracksProvider);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: playlist.name!,
|
title: playlist.name,
|
||||||
description: playlist.description,
|
description: playlist.description,
|
||||||
tracks: tracks,
|
tracks: tracks,
|
||||||
routePath: '/playlist/${playlist.id}',
|
routePath: '/playlist/${playlist.id}',
|
||||||
isLiked: false,
|
isLiked: false,
|
||||||
shareUrl: null,
|
shareUrl: null,
|
||||||
onHeart: null,
|
onHeart: null,
|
||||||
owner: playlist.owner?.displayName,
|
owner: playlist.owner.name,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -2,13 +2,13 @@ import 'package:flutter/material.dart' as material;
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart' hide Page;
|
import 'package:flutter/material.dart' hide Page;
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotify/spotify.dart';
|
|
||||||
import 'package:spotube/components/dialogs/prompt_dialog.dart';
|
import 'package:spotube/components/dialogs/prompt_dialog.dart';
|
||||||
import 'package:spotube/components/track_presentation/presentation_props.dart';
|
import 'package:spotube/components/track_presentation/presentation_props.dart';
|
||||||
import 'package:spotube/components/track_presentation/track_presentation.dart';
|
import 'package:spotube/components/track_presentation/track_presentation.dart';
|
||||||
import 'package:spotube/components/track_presentation/use_is_user_playlist.dart';
|
import 'package:spotube/components/track_presentation/use_is_user_playlist.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
|
import 'package:spotube/provider/metadata_plugin/library/playlists.dart';
|
||||||
import 'package:spotube/provider/spotify/spotify.dart';
|
import 'package:spotube/provider/spotify/spotify.dart';
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
|
||||||
@ -16,22 +16,22 @@ import 'package:auto_route/auto_route.dart';
|
|||||||
class PlaylistPage extends HookConsumerWidget {
|
class PlaylistPage extends HookConsumerWidget {
|
||||||
static const name = "playlist";
|
static const name = "playlist";
|
||||||
|
|
||||||
final PlaylistSimple _playlist;
|
final SpotubeSimplePlaylistObject _playlist;
|
||||||
final String id;
|
final String id;
|
||||||
const PlaylistPage({
|
const PlaylistPage({
|
||||||
super.key,
|
super.key,
|
||||||
@PathParam("id") required this.id,
|
@PathParam("id") required this.id,
|
||||||
required PlaylistSimple playlist,
|
required SpotubeSimplePlaylistObject playlist,
|
||||||
}) : _playlist = playlist;
|
}) : _playlist = playlist;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final playlist = ref
|
final playlist = ref
|
||||||
.watch(
|
.watch(
|
||||||
favoritePlaylistsProvider.select(
|
metadataPluginSavedPlaylistsProvider.select(
|
||||||
(value) => value.whenData(
|
(value) => value.whenData(
|
||||||
(value) =>
|
(value) => (value.items as List<SpotubeSimplePlaylistObject>)
|
||||||
value.items.firstWhereOrNull((s) => s.id == _playlist.id),
|
.firstWhereOrNull((s) => s.id == _playlist.id),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -39,21 +39,21 @@ class PlaylistPage extends HookConsumerWidget {
|
|||||||
?.value ??
|
?.value ??
|
||||||
_playlist;
|
_playlist;
|
||||||
|
|
||||||
final tracks = ref.watch(playlistTracksProvider(playlist.id!));
|
final tracks = ref.watch(playlistTracksProvider(playlist.id));
|
||||||
final tracksNotifier =
|
final tracksNotifier =
|
||||||
ref.watch(playlistTracksProvider(playlist.id!).notifier);
|
ref.watch(playlistTracksProvider(playlist.id).notifier);
|
||||||
final isFavoritePlaylist =
|
final isFavoritePlaylist =
|
||||||
ref.watch(isFavoritePlaylistProvider(playlist.id!));
|
ref.watch(isFavoritePlaylistProvider(playlist.id));
|
||||||
|
|
||||||
final favoritePlaylistsNotifier =
|
final favoritePlaylistsNotifier =
|
||||||
ref.watch(favoritePlaylistsProvider.notifier);
|
ref.watch(metadataPluginSavedPlaylistsProvider.notifier);
|
||||||
|
|
||||||
final isUserPlaylist = useIsUserPlaylist(ref, playlist.id!);
|
final isUserPlaylist = useIsUserPlaylist(ref, playlist.id);
|
||||||
|
|
||||||
return material.RefreshIndicator.adaptive(
|
return material.RefreshIndicator.adaptive(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
ref.invalidate(playlistTracksProvider(playlist.id!));
|
ref.invalidate(playlistTracksProvider(playlist.id));
|
||||||
ref.invalidate(isFavoritePlaylistProvider(playlist.id!));
|
ref.invalidate(isFavoritePlaylistProvider(playlist.id));
|
||||||
ref.invalidate(favoritePlaylistsProvider);
|
ref.invalidate(favoritePlaylistsProvider);
|
||||||
},
|
},
|
||||||
child: TrackPresentation(
|
child: TrackPresentation(
|
||||||
@ -67,21 +67,20 @@ class PlaylistPage extends HookConsumerWidget {
|
|||||||
isLoading: tracks.isLoading || tracks.isLoadingNextPage,
|
isLoading: tracks.isLoading || tracks.isLoadingNextPage,
|
||||||
onFetchMore: tracksNotifier.fetchMore,
|
onFetchMore: tracksNotifier.fetchMore,
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
ref.invalidate(playlistTracksProvider(playlist.id!));
|
ref.invalidate(playlistTracksProvider(playlist.id));
|
||||||
},
|
},
|
||||||
onFetchAll: () async {
|
onFetchAll: () async {
|
||||||
return await tracksNotifier.fetchAll();
|
return await tracksNotifier.fetchAll();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: playlist.name!,
|
title: playlist.name,
|
||||||
description: playlist.description,
|
description: playlist.description,
|
||||||
owner: playlist.owner?.displayName,
|
owner: playlist.owner.name,
|
||||||
ownerImage: playlist.owner?.images?.lastOrNull?.url,
|
ownerImage: playlist.owner.images.lastOrNull?.url,
|
||||||
tracks: tracks.asData?.value.items ?? [],
|
tracks: tracks.asData?.value.items ?? [],
|
||||||
routePath: '/playlist/${playlist.id}',
|
routePath: '/playlist/${playlist.id}',
|
||||||
isLiked: isFavoritePlaylist.asData?.value ?? false,
|
isLiked: isFavoritePlaylist.asData?.value ?? false,
|
||||||
shareUrl: playlist.externalUrls?.spotify ??
|
shareUrl: playlist.externalUri,
|
||||||
"https://open.spotify.com/playlist/${playlist.id}",
|
|
||||||
onHeart: isFavoritePlaylist.asData?.value == null
|
onHeart: isFavoritePlaylist.asData?.value == null
|
||||||
? null
|
? null
|
||||||
: () async {
|
: () async {
|
||||||
|
@ -142,16 +142,16 @@ class TrackPage extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(SpotubeIcons.album),
|
const Icon(SpotubeIcons.album),
|
||||||
const Gap(5),
|
const Gap(5),
|
||||||
Flexible(
|
// Flexible(
|
||||||
child: LinkText(
|
// child: LinkText(
|
||||||
track.album!.name!,
|
// track.album!.name!,
|
||||||
AlbumRoute(
|
// AlbumRoute(
|
||||||
id: track.album!.id!,
|
// id: track.album!.id!,
|
||||||
album: track.album!,
|
// album: track.album!,
|
||||||
),
|
// ),
|
||||||
push: true,
|
// push: true,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Gap(10),
|
const Gap(10),
|
||||||
|
@ -26,8 +26,8 @@ class MetadataPluginSavedAlbumNotifier
|
|||||||
await update((state) async {
|
await update((state) async {
|
||||||
(await metadataPlugin).album.save(albums.map((e) => e.id).toList());
|
(await metadataPlugin).album.save(albums.map((e) => e.id).toList());
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: [...state.items, albums],
|
items: [...state.items, ...albums],
|
||||||
) as SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>;
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (final album in albums) {
|
for (final album in albums) {
|
||||||
@ -42,12 +42,10 @@ class MetadataPluginSavedAlbumNotifier
|
|||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: state.items
|
items: state.items
|
||||||
.where(
|
.where(
|
||||||
(e) =>
|
(e) => albumIds.contains((e).id) == false,
|
||||||
albumIds.contains((e as SpotubeSimpleAlbumObject).id) ==
|
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
.toList() as List<SpotubeSimpleAlbumObject>,
|
.toList(),
|
||||||
) as SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>;
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (final album in albums) {
|
for (final album in albums) {
|
||||||
|
@ -26,8 +26,8 @@ class MetadataPluginSavedArtistNotifier
|
|||||||
await update((state) async {
|
await update((state) async {
|
||||||
(await metadataPlugin).artist.save(artists.map((e) => e.id).toList());
|
(await metadataPlugin).artist.save(artists.map((e) => e.id).toList());
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: [...state.items, artists],
|
items: [...state.items, ...artists],
|
||||||
) as SpotubePaginationResponseObject<SpotubeFullArtistObject>;
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (final artist in artists) {
|
for (final artist in artists) {
|
||||||
@ -42,12 +42,10 @@ class MetadataPluginSavedArtistNotifier
|
|||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: state.items
|
items: state.items
|
||||||
.where(
|
.where(
|
||||||
(e) =>
|
(e) => artistIds.contains((e).id) == false,
|
||||||
artistIds.contains((e as SpotubeFullArtistObject).id) ==
|
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
.toList() as List<SpotubeFullArtistObject>,
|
.toList(),
|
||||||
) as SpotubePaginationResponseObject<SpotubeFullArtistObject>;
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (final artist in artists) {
|
for (final artist in artists) {
|
||||||
|
@ -36,8 +36,8 @@ class MetadataPluginSavedPlaylistsNotifier
|
|||||||
state.value!.copyWith(
|
state.value!.copyWith(
|
||||||
items: state.value!.items
|
items: state.value!.items
|
||||||
.map((element) => element.id == playlist.id ? playlist : element)
|
.map((element) => element.id == playlist.id ? playlist : element)
|
||||||
.toList() as List<SpotubeSimplePlaylistObject>,
|
.toList(),
|
||||||
) as SpotubePaginationResponseObject<SpotubeSimplePlaylistObject>,
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ class MetadataPluginSavedPlaylistsNotifier
|
|||||||
(await metadataPlugin).playlist.save(playlist.id);
|
(await metadataPlugin).playlist.save(playlist.id);
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: [...state.items, playlist],
|
items: [...state.items, playlist],
|
||||||
) as SpotubePaginationResponseObject<SpotubeSimplePlaylistObject>;
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ref.invalidate(metadataPluginIsSavedPlaylistProvider(playlist.id));
|
ref.invalidate(metadataPluginIsSavedPlaylistProvider(playlist.id));
|
||||||
@ -56,10 +56,8 @@ class MetadataPluginSavedPlaylistsNotifier
|
|||||||
await update((state) async {
|
await update((state) async {
|
||||||
(await metadataPlugin).playlist.unsave(playlist.id);
|
(await metadataPlugin).playlist.unsave(playlist.id);
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: state.items
|
items: state.items.where((e) => (e).id != playlist.id).toList(),
|
||||||
.where((e) => (e as SpotubeSimplePlaylistObject).id != playlist.id)
|
);
|
||||||
.toList() as List<SpotubeSimplePlaylistObject>,
|
|
||||||
) as SpotubePaginationResponseObject<SpotubeSimplePlaylistObject>;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ref.invalidate(metadataPluginIsSavedPlaylistProvider(playlist.id));
|
ref.invalidate(metadataPluginIsSavedPlaylistProvider(playlist.id));
|
||||||
@ -69,10 +67,8 @@ class MetadataPluginSavedPlaylistsNotifier
|
|||||||
await update((state) async {
|
await update((state) async {
|
||||||
(await metadataPlugin).playlist.deletePlaylist(playlist.id);
|
(await metadataPlugin).playlist.deletePlaylist(playlist.id);
|
||||||
return state.copyWith(
|
return state.copyWith(
|
||||||
items: state.items
|
items: state.items.where((e) => (e).id != playlist.id).toList(),
|
||||||
.where((e) => (e as SpotubeSimplePlaylistObject).id != playlist.id)
|
);
|
||||||
.toList() as List<SpotubeSimplePlaylistObject>,
|
|
||||||
) as SpotubePaginationResponseObject<SpotubeSimplePlaylistObject>;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ref.invalidate(metadataPluginIsSavedPlaylistProvider(playlist.id));
|
ref.invalidate(metadataPluginIsSavedPlaylistProvider(playlist.id));
|
||||||
|
@ -2,8 +2,6 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/models/metadata/metadata.dart';
|
import 'package:spotube/models/metadata/metadata.dart';
|
||||||
// ignore: implementation_imports
|
|
||||||
import 'package:riverpod/src/async_notifier.dart';
|
|
||||||
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
|
import 'package:spotube/provider/metadata_plugin/utils/common.dart';
|
||||||
|
|
||||||
abstract class FamilyPaginatedAsyncNotifier<K, A>
|
abstract class FamilyPaginatedAsyncNotifier<K, A>
|
||||||
@ -22,17 +20,20 @@ abstract class FamilyPaginatedAsyncNotifier<K, A>
|
|||||||
state.value!.nextOffset!,
|
state.value!.nextOffset!,
|
||||||
state.value!.limit,
|
state.value!.limit,
|
||||||
);
|
);
|
||||||
return newState.copyWith(items: [
|
|
||||||
...state.value!.items as List<K>,
|
final oldItems =
|
||||||
...newState.items as List<K>,
|
state.value!.items.isEmpty ? <K>[] : state.value!.items.cast<K>();
|
||||||
]) as SpotubePaginationResponseObject<K>;
|
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
|
||||||
|
|
||||||
|
return newState.copyWith(items: <K>[...oldItems, ...items])
|
||||||
|
as SpotubePaginationResponseObject<K>;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<K>> fetchAll() async {
|
Future<List<K>> fetchAll() async {
|
||||||
if (state.value == null) return [];
|
if (state.value == null) return [];
|
||||||
if (!state.value!.hasMore) return state.value!.items as List<K>;
|
if (!state.value!.hasMore) return state.value!.items.cast<K>();
|
||||||
|
|
||||||
bool hasMore = true;
|
bool hasMore = true;
|
||||||
while (hasMore) {
|
while (hasMore) {
|
||||||
@ -43,14 +44,14 @@ abstract class FamilyPaginatedAsyncNotifier<K, A>
|
|||||||
);
|
);
|
||||||
|
|
||||||
hasMore = newState.hasMore;
|
hasMore = newState.hasMore;
|
||||||
return newState.copyWith(items: [
|
final oldItems = state.items.isEmpty ? <K>[] : state.items.cast<K>();
|
||||||
...state.items as List<K>,
|
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
|
||||||
...newState.items as List<K>,
|
return newState.copyWith(items: <K>[...oldItems, ...items])
|
||||||
]) as SpotubePaginationResponseObject<K>;
|
as SpotubePaginationResponseObject<K>;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.value!.items as List<K>;
|
return state.value!.items.cast<K>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,8 +72,8 @@ abstract class AutoDisposeFamilyPaginatedAsyncNotifier<K, A>
|
|||||||
state.value!.limit,
|
state.value!.limit,
|
||||||
);
|
);
|
||||||
return newState.copyWith(items: [
|
return newState.copyWith(items: [
|
||||||
...state.value!.items as List<K>,
|
...state.value!.items.cast<K>(),
|
||||||
...newState.items as List<K>,
|
...newState.items.cast<K>(),
|
||||||
]) as SpotubePaginationResponseObject<K>;
|
]) as SpotubePaginationResponseObject<K>;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -80,7 +81,7 @@ abstract class AutoDisposeFamilyPaginatedAsyncNotifier<K, A>
|
|||||||
|
|
||||||
Future<List<K>> fetchAll() async {
|
Future<List<K>> fetchAll() async {
|
||||||
if (state.value == null) return [];
|
if (state.value == null) return [];
|
||||||
if (!state.value!.hasMore) return state.value!.items as List<K>;
|
if (!state.value!.hasMore) return state.value!.items.cast<K>();
|
||||||
|
|
||||||
bool hasMore = true;
|
bool hasMore = true;
|
||||||
while (hasMore) {
|
while (hasMore) {
|
||||||
@ -92,12 +93,12 @@ abstract class AutoDisposeFamilyPaginatedAsyncNotifier<K, A>
|
|||||||
|
|
||||||
hasMore = newState.hasMore;
|
hasMore = newState.hasMore;
|
||||||
return newState.copyWith(items: [
|
return newState.copyWith(items: [
|
||||||
...state.items as List<K>,
|
...state.items.cast<K>(),
|
||||||
...newState.items as List<K>,
|
...newState.items.cast<K>(),
|
||||||
]) as SpotubePaginationResponseObject<K>;
|
]) as SpotubePaginationResponseObject<K>;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.value!.items as List<K>;
|
return state.value!.items.cast<K>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,17 +22,20 @@ mixin PaginatedAsyncNotifierMixin<K>
|
|||||||
state.value!.nextOffset!,
|
state.value!.nextOffset!,
|
||||||
state.value!.limit,
|
state.value!.limit,
|
||||||
);
|
);
|
||||||
return newState.copyWith(items: [
|
|
||||||
...state.value!.items as List<K>,
|
final oldItems =
|
||||||
...newState.items as List<K>,
|
state.value!.items.isEmpty ? <K>[] : state.value!.items.cast<K>();
|
||||||
]) as SpotubePaginationResponseObject<K>;
|
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
|
||||||
|
|
||||||
|
return newState.copyWith(items: <K>[...oldItems, ...items])
|
||||||
|
as SpotubePaginationResponseObject<K>;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<K>> fetchAll() async {
|
Future<List<K>> fetchAll() async {
|
||||||
if (state.value == null) return [];
|
if (state.value == null) return [];
|
||||||
if (!state.value!.hasMore) return state.value!.items as List<K>;
|
if (!state.value!.hasMore) return state.value!.items.cast<K>();
|
||||||
|
|
||||||
bool hasMore = true;
|
bool hasMore = true;
|
||||||
while (hasMore) {
|
while (hasMore) {
|
||||||
@ -43,14 +46,14 @@ mixin PaginatedAsyncNotifierMixin<K>
|
|||||||
);
|
);
|
||||||
|
|
||||||
hasMore = newState.hasMore;
|
hasMore = newState.hasMore;
|
||||||
return newState.copyWith(items: [
|
final oldItems = state.items.isEmpty ? <K>[] : state.items.cast<K>();
|
||||||
...state.items as List<K>,
|
final items = newState.items.isEmpty ? <K>[] : newState.items.cast<K>();
|
||||||
...newState.items as List<K>,
|
return newState.copyWith(items: <K>[...oldItems, ...items])
|
||||||
]) as SpotubePaginationResponseObject<K>;
|
as SpotubePaginationResponseObject<K>;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return state.value!.items as List<K>;
|
return state.value!.items.cast<K>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class MetadataPluginAlbumEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<SpotubeFullTrackObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) =>
|
(Map json) =>
|
||||||
SpotubeFullTrackObject.fromJson(json.cast<String, dynamic>()),
|
SpotubeFullTrackObject.fromJson(json.cast<String, dynamic>()),
|
||||||
@ -52,7 +52,7 @@ class MetadataPluginAlbumEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) =>
|
(Map json) =>
|
||||||
SpotubeSimpleAlbumObject.fromJson(json.cast<String, dynamic>()),
|
SpotubeSimpleAlbumObject.fromJson(json.cast<String, dynamic>()),
|
||||||
|
@ -33,7 +33,7 @@ class MetadataPluginArtistEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<SpotubeFullTrackObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) => SpotubeFullTrackObject.fromJson(
|
(Map json) => SpotubeFullTrackObject.fromJson(
|
||||||
json.cast<String, dynamic>(),
|
json.cast<String, dynamic>(),
|
||||||
@ -55,7 +55,7 @@ class MetadataPluginArtistEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) => SpotubeSimpleAlbumObject.fromJson(
|
(Map json) => SpotubeSimpleAlbumObject.fromJson(
|
||||||
json.cast<String, dynamic>(),
|
json.cast<String, dynamic>(),
|
||||||
|
@ -33,7 +33,7 @@ class MetadataPluginPlaylistEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<SpotubeFullTrackObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) =>
|
(Map json) =>
|
||||||
SpotubeFullTrackObject.fromJson(json.cast<String, dynamic>()),
|
SpotubeFullTrackObject.fromJson(json.cast<String, dynamic>()),
|
||||||
|
@ -30,7 +30,7 @@ class MetadataPluginUserEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<SpotubeFullTrackObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) =>
|
(Map json) =>
|
||||||
SpotubeFullTrackObject.fromJson(json.cast<String, dynamic>()),
|
SpotubeFullTrackObject.fromJson(json.cast<String, dynamic>()),
|
||||||
@ -50,7 +50,8 @@ class MetadataPluginUserEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<
|
||||||
|
SpotubeSimplePlaylistObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) =>
|
(Map json) =>
|
||||||
SpotubeSimplePlaylistObject.fromJson(json.cast<String, dynamic>()),
|
SpotubeSimplePlaylistObject.fromJson(json.cast<String, dynamic>()),
|
||||||
@ -70,7 +71,7 @@ class MetadataPluginUserEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<SpotubeSimpleAlbumObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) =>
|
(Map json) =>
|
||||||
SpotubeSimpleAlbumObject.fromJson(json.cast<String, dynamic>()),
|
SpotubeSimpleAlbumObject.fromJson(json.cast<String, dynamic>()),
|
||||||
@ -90,7 +91,7 @@ class MetadataPluginUserEndpoint {
|
|||||||
}..removeWhere((key, value) => value == null),
|
}..removeWhere((key, value) => value == null),
|
||||||
) as Map;
|
) as Map;
|
||||||
|
|
||||||
return SpotubePaginationResponseObject.fromJson(
|
return SpotubePaginationResponseObject<SpotubeFullArtistObject>.fromJson(
|
||||||
raw.cast<String, dynamic>(),
|
raw.cast<String, dynamic>(),
|
||||||
(Map json) =>
|
(Map json) =>
|
||||||
SpotubeFullArtistObject.fromJson(json.cast<String, dynamic>()),
|
SpotubeFullArtistObject.fromJson(json.cast<String, dynamic>()),
|
||||||
|
@ -112,7 +112,6 @@ dependencies:
|
|||||||
sliding_up_panel: ^2.0.0+1
|
sliding_up_panel: ^2.0.0+1
|
||||||
sliver_tools: ^0.2.12
|
sliver_tools: ^0.2.12
|
||||||
smtc_windows: ^1.0.0
|
smtc_windows: ^1.0.0
|
||||||
spotify: ^0.13.7
|
|
||||||
sqlite3: ^2.4.3
|
sqlite3: ^2.4.3
|
||||||
sqlite3_flutter_libs: ^0.5.23
|
sqlite3_flutter_libs: ^0.5.23
|
||||||
stroke_text: ^0.0.2
|
stroke_text: ^0.0.2
|
||||||
@ -157,6 +156,7 @@ dependencies:
|
|||||||
url: https://github.com/KRTirtho/hetu_spotube_plugin.git
|
url: https://github.com/KRTirtho/hetu_spotube_plugin.git
|
||||||
ref: main
|
ref: main
|
||||||
get_it: ^8.0.3
|
get_it: ^8.0.3
|
||||||
|
spotify: ^0.13.7
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.13
|
build_runner: ^2.4.13
|
||||||
|
Loading…
Reference in New Issue
Block a user