From 7569c37739be0bcf119321d979d66b4c7409bda8 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Sun, 15 Jun 2025 22:14:04 +0600 Subject: [PATCH] refactor: home browse and browse section --- lib/collections/routes.dart | 4 +- lib/collections/routes.gr.dart | 47 +- lib/models/metadata/album.dart | 2 +- lib/models/metadata/browse.dart | 58 +- lib/models/metadata/metadata.freezed.dart | 927 +++--------------- lib/models/metadata/metadata.g.dart | 82 +- lib/modules/album/album_card.dart | 2 +- lib/modules/home/sections/feed.dart | 50 - lib/modules/home/sections/sections.dart | 46 + lib/pages/home/feed/feed_section.dart | 101 -- lib/pages/home/home.dart | 29 +- lib/pages/home/sections/section_items.dart | 122 +++ .../metadata_plugin/browse/section_items.dart | 32 + .../metadata_plugin/browse/sections.dart | 31 + .../metadata_plugin/utils/common.dart | 4 + lib/services/metadata/endpoints/browse.dart | 87 ++ lib/services/metadata/metadata.dart | 3 + 17 files changed, 530 insertions(+), 1097 deletions(-) delete mode 100644 lib/modules/home/sections/feed.dart create mode 100644 lib/modules/home/sections/sections.dart delete mode 100644 lib/pages/home/feed/feed_section.dart create mode 100644 lib/pages/home/sections/section_items.dart create mode 100644 lib/provider/metadata_plugin/browse/section_items.dart create mode 100644 lib/provider/metadata_plugin/browse/sections.dart diff --git a/lib/collections/routes.dart b/lib/collections/routes.dart index 835260bc..2c1ad8ad 100644 --- a/lib/collections/routes.dart +++ b/lib/collections/routes.dart @@ -48,8 +48,8 @@ class AppRouter extends RootStackRouter { page: GenrePlaylistsRoute.page, ), AutoRoute( - path: "home/feeds/:feedId", - page: HomeFeedSectionRoute.page, + path: "home/sections/:sectionId", + page: HomeBrowseSectionItemsRoute.page, ), AutoRoute( path: "search", diff --git a/lib/collections/routes.gr.dart b/lib/collections/routes.gr.dart index 798c0ad4..c97e4ce9 100644 --- a/lib/collections/routes.gr.dart +++ b/lib/collections/routes.gr.dart @@ -19,10 +19,10 @@ import 'package:spotube/pages/artist/artist.dart' as _i3; import 'package:spotube/pages/connect/connect.dart' as _i6; import 'package:spotube/pages/connect/control/control.dart' as _i5; import 'package:spotube/pages/getting_started/getting_started.dart' as _i9; -import 'package:spotube/pages/home/feed/feed_section.dart' as _i10; import 'package:spotube/pages/home/genres/genre_playlists.dart' as _i8; import 'package:spotube/pages/home/genres/genres.dart' as _i7; import 'package:spotube/pages/home/home.dart' as _i11; +import 'package:spotube/pages/home/sections/section_items.dart' as _i10; import 'package:spotube/pages/lastfm_login/lastfm_login.dart' as _i12; import 'package:spotube/pages/library/library.dart' as _i13; import 'package:spotube/pages/library/playlist_generate/playlist_generate.dart' @@ -332,53 +332,56 @@ class GettingStartedRoute extends _i44.PageRouteInfo { } /// generated route for -/// [_i10.HomeFeedSectionPage] -class HomeFeedSectionRoute - extends _i44.PageRouteInfo { - HomeFeedSectionRoute({ +/// [_i10.HomeBrowseSectionItemsPage] +class HomeBrowseSectionItemsRoute + extends _i44.PageRouteInfo { + HomeBrowseSectionItemsRoute({ _i48.Key? key, - required String sectionUri, + required String sectionId, + required _i46.SpotubeBrowseSectionObject section, List<_i44.PageRouteInfo>? children, }) : super( - HomeFeedSectionRoute.name, - args: HomeFeedSectionRouteArgs( + HomeBrowseSectionItemsRoute.name, + args: HomeBrowseSectionItemsRouteArgs( key: key, - sectionUri: sectionUri, + sectionId: sectionId, + section: section, ), - rawPathParams: {'feedId': sectionUri}, + rawPathParams: {'sectionId': sectionId}, initialChildren: children, ); - static const String name = 'HomeFeedSectionRoute'; + static const String name = 'HomeBrowseSectionItemsRoute'; static _i44.PageInfo page = _i44.PageInfo( name, builder: (data) { - final pathParams = data.inheritedPathParams; - final args = data.argsAs( - orElse: () => HomeFeedSectionRouteArgs( - sectionUri: pathParams.getString('feedId'))); - return _i10.HomeFeedSectionPage( + final args = data.argsAs(); + return _i10.HomeBrowseSectionItemsPage( key: args.key, - sectionUri: args.sectionUri, + sectionId: args.sectionId, + section: args.section, ); }, ); } -class HomeFeedSectionRouteArgs { - const HomeFeedSectionRouteArgs({ +class HomeBrowseSectionItemsRouteArgs { + const HomeBrowseSectionItemsRouteArgs({ this.key, - required this.sectionUri, + required this.sectionId, + required this.section, }); final _i48.Key? key; - final String sectionUri; + final String sectionId; + + final _i46.SpotubeBrowseSectionObject section; @override String toString() { - return 'HomeFeedSectionRouteArgs{key: $key, sectionUri: $sectionUri}'; + return 'HomeBrowseSectionItemsRouteArgs{key: $key, sectionId: $sectionId, section: $section}'; } } diff --git a/lib/models/metadata/album.dart b/lib/models/metadata/album.dart index da2c21fd..bc9022de 100644 --- a/lib/models/metadata/album.dart +++ b/lib/models/metadata/album.dart @@ -33,8 +33,8 @@ class SpotubeSimpleAlbumObject with _$SpotubeSimpleAlbumObject { required String externalUri, required List artists, @Default([]) List images, - required String releaseDate, required SpotubeAlbumType albumType, + String? releaseDate, }) = _SpotubeSimpleAlbumObject; factory SpotubeSimpleAlbumObject.fromJson(Map json) => diff --git a/lib/models/metadata/browse.dart b/lib/models/metadata/browse.dart index fc323d01..e2a69181 100644 --- a/lib/models/metadata/browse.dart +++ b/lib/models/metadata/browse.dart @@ -1,43 +1,21 @@ part of 'metadata.dart'; -enum SectionItemType { - @JsonValue("Playlist") - playlist, - @JsonValue("Album") - album, - @JsonValue("Artist") - artist -} - -@Freezed(unionKey: "itemType") -class SpotubeBrowseSectionObject with _$SpotubeBrowseSectionObject { - @FreezedUnionValue("Album") - factory SpotubeBrowseSectionObject.album({ - required String id, - required String title, - required String externalUri, - required SectionItemType itemType, - required List items, - }) = SpotubeBrowseAlbumSectionObject; - - @FreezedUnionValue("Artist") - factory SpotubeBrowseSectionObject.artist({ - required String id, - required String title, - required String externalUri, - required SectionItemType itemType, - required List items, - }) = SpotubeBrowseArtistSectionObject; - - @FreezedUnionValue("Playlist") - factory SpotubeBrowseSectionObject.playlist({ - required String id, - required String title, - required String externalUri, - required SectionItemType itemType, - required List items, - }) = SpotubeBrowsePlaylistSectionObject; - - factory SpotubeBrowseSectionObject.fromJson(Map json) => - _$SpotubeBrowseSectionObjectFromJson(json); +@Freezed(genericArgumentFactories: true) +class SpotubeBrowseSectionObject with _$SpotubeBrowseSectionObject { + factory SpotubeBrowseSectionObject({ + required String id, + required String title, + required String externalUri, + required bool browseMore, + required List items, + }) = _SpotubeBrowseSectionObject; + + factory SpotubeBrowseSectionObject.fromJson( + Map json, + T Function(Map json) fromJsonT, + ) => + _$SpotubeBrowseSectionObjectFromJson( + json, + (json) => fromJsonT(json as Map), + ); } diff --git a/lib/models/metadata/metadata.freezed.dart b/lib/models/metadata/metadata.freezed.dart index 7774224c..2bf50c6c 100644 --- a/lib/models/metadata/metadata.freezed.dart +++ b/lib/models/metadata/metadata.freezed.dart @@ -406,8 +406,8 @@ mixin _$SpotubeSimpleAlbumObject { List get artists => throw _privateConstructorUsedError; List get images => throw _privateConstructorUsedError; - String get releaseDate => throw _privateConstructorUsedError; SpotubeAlbumType get albumType => throw _privateConstructorUsedError; + String? get releaseDate => throw _privateConstructorUsedError; /// Serializes this SpotubeSimpleAlbumObject to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -431,8 +431,8 @@ abstract class $SpotubeSimpleAlbumObjectCopyWith<$Res> { String externalUri, List artists, List images, - String releaseDate, - SpotubeAlbumType albumType}); + SpotubeAlbumType albumType, + String? releaseDate}); } /// @nodoc @@ -456,8 +456,8 @@ class _$SpotubeSimpleAlbumObjectCopyWithImpl<$Res, Object? externalUri = null, Object? artists = null, Object? images = null, - Object? releaseDate = null, Object? albumType = null, + Object? releaseDate = freezed, }) { return _then(_value.copyWith( id: null == id @@ -480,14 +480,14 @@ class _$SpotubeSimpleAlbumObjectCopyWithImpl<$Res, ? _value.images : images // ignore: cast_nullable_to_non_nullable as List, - releaseDate: null == releaseDate - ? _value.releaseDate - : releaseDate // ignore: cast_nullable_to_non_nullable - as String, albumType: null == albumType ? _value.albumType : albumType // ignore: cast_nullable_to_non_nullable as SpotubeAlbumType, + releaseDate: freezed == releaseDate + ? _value.releaseDate + : releaseDate // ignore: cast_nullable_to_non_nullable + as String?, ) as $Val); } } @@ -507,8 +507,8 @@ abstract class _$$SpotubeSimpleAlbumObjectImplCopyWith<$Res> String externalUri, List artists, List images, - String releaseDate, - SpotubeAlbumType albumType}); + SpotubeAlbumType albumType, + String? releaseDate}); } /// @nodoc @@ -531,8 +531,8 @@ class __$$SpotubeSimpleAlbumObjectImplCopyWithImpl<$Res> Object? externalUri = null, Object? artists = null, Object? images = null, - Object? releaseDate = null, Object? albumType = null, + Object? releaseDate = freezed, }) { return _then(_$SpotubeSimpleAlbumObjectImpl( id: null == id @@ -555,14 +555,14 @@ class __$$SpotubeSimpleAlbumObjectImplCopyWithImpl<$Res> ? _value._images : images // ignore: cast_nullable_to_non_nullable as List, - releaseDate: null == releaseDate - ? _value.releaseDate - : releaseDate // ignore: cast_nullable_to_non_nullable - as String, albumType: null == albumType ? _value.albumType : albumType // ignore: cast_nullable_to_non_nullable as SpotubeAlbumType, + releaseDate: freezed == releaseDate + ? _value.releaseDate + : releaseDate // ignore: cast_nullable_to_non_nullable + as String?, )); } } @@ -576,8 +576,8 @@ class _$SpotubeSimpleAlbumObjectImpl implements _SpotubeSimpleAlbumObject { required this.externalUri, required final List artists, final List images = const [], - required this.releaseDate, - required this.albumType}) + required this.albumType, + this.releaseDate}) : _artists = artists, _images = images; @@ -607,14 +607,14 @@ class _$SpotubeSimpleAlbumObjectImpl implements _SpotubeSimpleAlbumObject { return EqualUnmodifiableListView(_images); } - @override - final String releaseDate; @override final SpotubeAlbumType albumType; + @override + final String? releaseDate; @override String toString() { - return 'SpotubeSimpleAlbumObject(id: $id, name: $name, externalUri: $externalUri, artists: $artists, images: $images, releaseDate: $releaseDate, albumType: $albumType)'; + return 'SpotubeSimpleAlbumObject(id: $id, name: $name, externalUri: $externalUri, artists: $artists, images: $images, albumType: $albumType, releaseDate: $releaseDate)'; } @override @@ -628,10 +628,10 @@ class _$SpotubeSimpleAlbumObjectImpl implements _SpotubeSimpleAlbumObject { other.externalUri == externalUri) && const DeepCollectionEquality().equals(other._artists, _artists) && const DeepCollectionEquality().equals(other._images, _images) && - (identical(other.releaseDate, releaseDate) || - other.releaseDate == releaseDate) && (identical(other.albumType, albumType) || - other.albumType == albumType)); + other.albumType == albumType) && + (identical(other.releaseDate, releaseDate) || + other.releaseDate == releaseDate)); } @JsonKey(includeFromJson: false, includeToJson: false) @@ -643,8 +643,8 @@ class _$SpotubeSimpleAlbumObjectImpl implements _SpotubeSimpleAlbumObject { externalUri, const DeepCollectionEquality().hash(_artists), const DeepCollectionEquality().hash(_images), - releaseDate, - albumType); + albumType, + releaseDate); /// Create a copy of SpotubeSimpleAlbumObject /// with the given fields replaced by the non-null parameter values. @@ -665,14 +665,13 @@ class _$SpotubeSimpleAlbumObjectImpl implements _SpotubeSimpleAlbumObject { abstract class _SpotubeSimpleAlbumObject implements SpotubeSimpleAlbumObject { factory _SpotubeSimpleAlbumObject( - {required final String id, - required final String name, - required final String externalUri, - required final List artists, - final List images, - required final String releaseDate, - required final SpotubeAlbumType albumType}) = - _$SpotubeSimpleAlbumObjectImpl; + {required final String id, + required final String name, + required final String externalUri, + required final List artists, + final List images, + required final SpotubeAlbumType albumType, + final String? releaseDate}) = _$SpotubeSimpleAlbumObjectImpl; factory _SpotubeSimpleAlbumObject.fromJson(Map json) = _$SpotubeSimpleAlbumObjectImpl.fromJson; @@ -688,9 +687,9 @@ abstract class _SpotubeSimpleAlbumObject implements SpotubeSimpleAlbumObject { @override List get images; @override - String get releaseDate; - @override SpotubeAlbumType get albumType; + @override + String? get releaseDate; /// Create a copy of SpotubeSimpleAlbumObject /// with the given fields replaced by the non-null parameter values. @@ -1177,121 +1176,50 @@ abstract class _SpotubeSimpleArtistObject implements SpotubeSimpleArtistObject { get copyWith => throw _privateConstructorUsedError; } -SpotubeBrowseSectionObject _$SpotubeBrowseSectionObjectFromJson( - Map json) { - switch (json['itemType']) { - case 'Album': - return SpotubeBrowseAlbumSectionObject.fromJson(json); - case 'Artist': - return SpotubeBrowseArtistSectionObject.fromJson(json); - case 'Playlist': - return SpotubeBrowsePlaylistSectionObject.fromJson(json); - - default: - throw CheckedFromJsonException( - json, - 'itemType', - 'SpotubeBrowseSectionObject', - 'Invalid union type "${json['itemType']}"!'); - } +SpotubeBrowseSectionObject _$SpotubeBrowseSectionObjectFromJson( + Map json, T Function(Object?) fromJsonT) { + return _SpotubeBrowseSectionObject.fromJson(json, fromJsonT); } /// @nodoc -mixin _$SpotubeBrowseSectionObject { +mixin _$SpotubeBrowseSectionObject { String get id => throw _privateConstructorUsedError; String get title => throw _privateConstructorUsedError; String get externalUri => throw _privateConstructorUsedError; - SectionItemType get itemType => throw _privateConstructorUsedError; - List get items => throw _privateConstructorUsedError; - @optionalTypeArgs - TResult when({ - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - album, - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - artist, - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - playlist, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - album, - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - artist, - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - playlist, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - album, - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - artist, - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - playlist, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult map({ - required TResult Function(SpotubeBrowseAlbumSectionObject value) album, - required TResult Function(SpotubeBrowseArtistSectionObject value) artist, - required TResult Function(SpotubeBrowsePlaylistSectionObject value) - playlist, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(SpotubeBrowseAlbumSectionObject value)? album, - TResult? Function(SpotubeBrowseArtistSectionObject value)? artist, - TResult? Function(SpotubeBrowsePlaylistSectionObject value)? playlist, - }) => - throw _privateConstructorUsedError; - @optionalTypeArgs - TResult maybeMap({ - TResult Function(SpotubeBrowseAlbumSectionObject value)? album, - TResult Function(SpotubeBrowseArtistSectionObject value)? artist, - TResult Function(SpotubeBrowsePlaylistSectionObject value)? playlist, - required TResult orElse(), - }) => - throw _privateConstructorUsedError; + bool get browseMore => throw _privateConstructorUsedError; + List get items => throw _privateConstructorUsedError; /// Serializes this SpotubeBrowseSectionObject to a JSON map. - Map toJson() => throw _privateConstructorUsedError; + Map toJson(Object? Function(T) toJsonT) => + throw _privateConstructorUsedError; /// Create a copy of SpotubeBrowseSectionObject /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) - $SpotubeBrowseSectionObjectCopyWith + $SpotubeBrowseSectionObjectCopyWith> get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $SpotubeBrowseSectionObjectCopyWith<$Res> { - factory $SpotubeBrowseSectionObjectCopyWith(SpotubeBrowseSectionObject value, - $Res Function(SpotubeBrowseSectionObject) then) = - _$SpotubeBrowseSectionObjectCopyWithImpl<$Res, - SpotubeBrowseSectionObject>; +abstract class $SpotubeBrowseSectionObjectCopyWith { + factory $SpotubeBrowseSectionObjectCopyWith( + SpotubeBrowseSectionObject value, + $Res Function(SpotubeBrowseSectionObject) then) = + _$SpotubeBrowseSectionObjectCopyWithImpl>; @useResult $Res call( - {String id, String title, String externalUri, SectionItemType itemType}); + {String id, + String title, + String externalUri, + bool browseMore, + List items}); } /// @nodoc -class _$SpotubeBrowseSectionObjectCopyWithImpl<$Res, - $Val extends SpotubeBrowseSectionObject> - implements $SpotubeBrowseSectionObjectCopyWith<$Res> { +class _$SpotubeBrowseSectionObjectCopyWithImpl> + implements $SpotubeBrowseSectionObjectCopyWith { _$SpotubeBrowseSectionObjectCopyWithImpl(this._value, this._then); // ignore: unused_field @@ -1307,7 +1235,8 @@ class _$SpotubeBrowseSectionObjectCopyWithImpl<$Res, Object? id = null, Object? title = null, Object? externalUri = null, - Object? itemType = null, + Object? browseMore = null, + Object? items = null, }) { return _then(_value.copyWith( id: null == id @@ -1322,39 +1251,43 @@ class _$SpotubeBrowseSectionObjectCopyWithImpl<$Res, ? _value.externalUri : externalUri // ignore: cast_nullable_to_non_nullable as String, - itemType: null == itemType - ? _value.itemType - : itemType // ignore: cast_nullable_to_non_nullable - as SectionItemType, + browseMore: null == browseMore + ? _value.browseMore + : browseMore // ignore: cast_nullable_to_non_nullable + as bool, + items: null == items + ? _value.items + : items // ignore: cast_nullable_to_non_nullable + as List, ) as $Val); } } /// @nodoc -abstract class _$$SpotubeBrowseAlbumSectionObjectImplCopyWith<$Res> - implements $SpotubeBrowseSectionObjectCopyWith<$Res> { - factory _$$SpotubeBrowseAlbumSectionObjectImplCopyWith( - _$SpotubeBrowseAlbumSectionObjectImpl value, - $Res Function(_$SpotubeBrowseAlbumSectionObjectImpl) then) = - __$$SpotubeBrowseAlbumSectionObjectImplCopyWithImpl<$Res>; +abstract class _$$SpotubeBrowseSectionObjectImplCopyWith + implements $SpotubeBrowseSectionObjectCopyWith { + factory _$$SpotubeBrowseSectionObjectImplCopyWith( + _$SpotubeBrowseSectionObjectImpl value, + $Res Function(_$SpotubeBrowseSectionObjectImpl) then) = + __$$SpotubeBrowseSectionObjectImplCopyWithImpl; @override @useResult $Res call( {String id, String title, String externalUri, - SectionItemType itemType, - List items}); + bool browseMore, + List items}); } /// @nodoc -class __$$SpotubeBrowseAlbumSectionObjectImplCopyWithImpl<$Res> - extends _$SpotubeBrowseSectionObjectCopyWithImpl<$Res, - _$SpotubeBrowseAlbumSectionObjectImpl> - implements _$$SpotubeBrowseAlbumSectionObjectImplCopyWith<$Res> { - __$$SpotubeBrowseAlbumSectionObjectImplCopyWithImpl( - _$SpotubeBrowseAlbumSectionObjectImpl _value, - $Res Function(_$SpotubeBrowseAlbumSectionObjectImpl) _then) +class __$$SpotubeBrowseSectionObjectImplCopyWithImpl + extends _$SpotubeBrowseSectionObjectCopyWithImpl> + implements _$$SpotubeBrowseSectionObjectImplCopyWith { + __$$SpotubeBrowseSectionObjectImplCopyWithImpl( + _$SpotubeBrowseSectionObjectImpl _value, + $Res Function(_$SpotubeBrowseSectionObjectImpl) _then) : super(_value, _then); /// Create a copy of SpotubeBrowseSectionObject @@ -1365,10 +1298,10 @@ class __$$SpotubeBrowseAlbumSectionObjectImplCopyWithImpl<$Res> Object? id = null, Object? title = null, Object? externalUri = null, - Object? itemType = null, + Object? browseMore = null, Object? items = null, }) { - return _then(_$SpotubeBrowseAlbumSectionObjectImpl( + return _then(_$SpotubeBrowseSectionObjectImpl( id: null == id ? _value.id : id // ignore: cast_nullable_to_non_nullable @@ -1381,33 +1314,33 @@ class __$$SpotubeBrowseAlbumSectionObjectImplCopyWithImpl<$Res> ? _value.externalUri : externalUri // ignore: cast_nullable_to_non_nullable as String, - itemType: null == itemType - ? _value.itemType - : itemType // ignore: cast_nullable_to_non_nullable - as SectionItemType, + browseMore: null == browseMore + ? _value.browseMore + : browseMore // ignore: cast_nullable_to_non_nullable + as bool, items: null == items ? _value._items : items // ignore: cast_nullable_to_non_nullable - as List, + as List, )); } } /// @nodoc -@JsonSerializable() -class _$SpotubeBrowseAlbumSectionObjectImpl - implements SpotubeBrowseAlbumSectionObject { - _$SpotubeBrowseAlbumSectionObjectImpl( +@JsonSerializable(genericArgumentFactories: true) +class _$SpotubeBrowseSectionObjectImpl + implements _SpotubeBrowseSectionObject { + _$SpotubeBrowseSectionObjectImpl( {required this.id, required this.title, required this.externalUri, - required this.itemType, - required final List items}) + required this.browseMore, + required final List items}) : _items = items; - factory _$SpotubeBrowseAlbumSectionObjectImpl.fromJson( - Map json) => - _$$SpotubeBrowseAlbumSectionObjectImplFromJson(json); + factory _$SpotubeBrowseSectionObjectImpl.fromJson( + Map json, T Function(Object?) fromJsonT) => + _$$SpotubeBrowseSectionObjectImplFromJson(json, fromJsonT); @override final String id; @@ -1416,10 +1349,10 @@ class _$SpotubeBrowseAlbumSectionObjectImpl @override final String externalUri; @override - final SectionItemType itemType; - final List _items; + final bool browseMore; + final List _items; @override - List get items { + List get items { if (_items is EqualUnmodifiableListView) return _items; // ignore: implicit_dynamic_type return EqualUnmodifiableListView(_items); @@ -1427,145 +1360,56 @@ class _$SpotubeBrowseAlbumSectionObjectImpl @override String toString() { - return 'SpotubeBrowseSectionObject.album(id: $id, title: $title, externalUri: $externalUri, itemType: $itemType, items: $items)'; + return 'SpotubeBrowseSectionObject<$T>(id: $id, title: $title, externalUri: $externalUri, browseMore: $browseMore, items: $items)'; } @override bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && - other is _$SpotubeBrowseAlbumSectionObjectImpl && + other is _$SpotubeBrowseSectionObjectImpl && (identical(other.id, id) || other.id == id) && (identical(other.title, title) || other.title == title) && (identical(other.externalUri, externalUri) || other.externalUri == externalUri) && - (identical(other.itemType, itemType) || - other.itemType == itemType) && + (identical(other.browseMore, browseMore) || + other.browseMore == browseMore) && const DeepCollectionEquality().equals(other._items, _items)); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => Object.hash(runtimeType, id, title, externalUri, itemType, - const DeepCollectionEquality().hash(_items)); + int get hashCode => Object.hash(runtimeType, id, title, externalUri, + browseMore, const DeepCollectionEquality().hash(_items)); /// Create a copy of SpotubeBrowseSectionObject /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') - _$$SpotubeBrowseAlbumSectionObjectImplCopyWith< - _$SpotubeBrowseAlbumSectionObjectImpl> - get copyWith => __$$SpotubeBrowseAlbumSectionObjectImplCopyWithImpl< - _$SpotubeBrowseAlbumSectionObjectImpl>(this, _$identity); + _$$SpotubeBrowseSectionObjectImplCopyWith> + get copyWith => __$$SpotubeBrowseSectionObjectImplCopyWithImpl>(this, _$identity); @override - @optionalTypeArgs - TResult when({ - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - album, - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - artist, - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - playlist, - }) { - return album(id, title, externalUri, itemType, items); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - album, - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - artist, - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - playlist, - }) { - return album?.call(id, title, externalUri, itemType, items); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - album, - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - artist, - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - playlist, - required TResult orElse(), - }) { - if (album != null) { - return album(id, title, externalUri, itemType, items); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(SpotubeBrowseAlbumSectionObject value) album, - required TResult Function(SpotubeBrowseArtistSectionObject value) artist, - required TResult Function(SpotubeBrowsePlaylistSectionObject value) - playlist, - }) { - return album(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(SpotubeBrowseAlbumSectionObject value)? album, - TResult? Function(SpotubeBrowseArtistSectionObject value)? artist, - TResult? Function(SpotubeBrowsePlaylistSectionObject value)? playlist, - }) { - return album?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(SpotubeBrowseAlbumSectionObject value)? album, - TResult Function(SpotubeBrowseArtistSectionObject value)? artist, - TResult Function(SpotubeBrowsePlaylistSectionObject value)? playlist, - required TResult orElse(), - }) { - if (album != null) { - return album(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$SpotubeBrowseAlbumSectionObjectImplToJson( - this, - ); + Map toJson(Object? Function(T) toJsonT) { + return _$$SpotubeBrowseSectionObjectImplToJson(this, toJsonT); } } -abstract class SpotubeBrowseAlbumSectionObject - implements SpotubeBrowseSectionObject { - factory SpotubeBrowseAlbumSectionObject( - {required final String id, - required final String title, - required final String externalUri, - required final SectionItemType itemType, - required final List items}) = - _$SpotubeBrowseAlbumSectionObjectImpl; +abstract class _SpotubeBrowseSectionObject + implements SpotubeBrowseSectionObject { + factory _SpotubeBrowseSectionObject( + {required final String id, + required final String title, + required final String externalUri, + required final bool browseMore, + required final List items}) = _$SpotubeBrowseSectionObjectImpl; - factory SpotubeBrowseAlbumSectionObject.fromJson(Map json) = - _$SpotubeBrowseAlbumSectionObjectImpl.fromJson; + factory _SpotubeBrowseSectionObject.fromJson( + Map json, T Function(Object?) fromJsonT) = + _$SpotubeBrowseSectionObjectImpl.fromJson; @override String get id; @@ -1574,531 +1418,16 @@ abstract class SpotubeBrowseAlbumSectionObject @override String get externalUri; @override - SectionItemType get itemType; + bool get browseMore; @override - List get items; + List get items; /// Create a copy of SpotubeBrowseSectionObject /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) - _$$SpotubeBrowseAlbumSectionObjectImplCopyWith< - _$SpotubeBrowseAlbumSectionObjectImpl> - get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$SpotubeBrowseArtistSectionObjectImplCopyWith<$Res> - implements $SpotubeBrowseSectionObjectCopyWith<$Res> { - factory _$$SpotubeBrowseArtistSectionObjectImplCopyWith( - _$SpotubeBrowseArtistSectionObjectImpl value, - $Res Function(_$SpotubeBrowseArtistSectionObjectImpl) then) = - __$$SpotubeBrowseArtistSectionObjectImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String id, - String title, - String externalUri, - SectionItemType itemType, - List items}); -} - -/// @nodoc -class __$$SpotubeBrowseArtistSectionObjectImplCopyWithImpl<$Res> - extends _$SpotubeBrowseSectionObjectCopyWithImpl<$Res, - _$SpotubeBrowseArtistSectionObjectImpl> - implements _$$SpotubeBrowseArtistSectionObjectImplCopyWith<$Res> { - __$$SpotubeBrowseArtistSectionObjectImplCopyWithImpl( - _$SpotubeBrowseArtistSectionObjectImpl _value, - $Res Function(_$SpotubeBrowseArtistSectionObjectImpl) _then) - : super(_value, _then); - - /// Create a copy of SpotubeBrowseSectionObject - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? title = null, - Object? externalUri = null, - Object? itemType = null, - Object? items = null, - }) { - return _then(_$SpotubeBrowseArtistSectionObjectImpl( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as String, - title: null == title - ? _value.title - : title // ignore: cast_nullable_to_non_nullable - as String, - externalUri: null == externalUri - ? _value.externalUri - : externalUri // ignore: cast_nullable_to_non_nullable - as String, - itemType: null == itemType - ? _value.itemType - : itemType // ignore: cast_nullable_to_non_nullable - as SectionItemType, - items: null == items - ? _value._items - : items // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$SpotubeBrowseArtistSectionObjectImpl - implements SpotubeBrowseArtistSectionObject { - _$SpotubeBrowseArtistSectionObjectImpl( - {required this.id, - required this.title, - required this.externalUri, - required this.itemType, - required final List items}) - : _items = items; - - factory _$SpotubeBrowseArtistSectionObjectImpl.fromJson( - Map json) => - _$$SpotubeBrowseArtistSectionObjectImplFromJson(json); - - @override - final String id; - @override - final String title; - @override - final String externalUri; - @override - final SectionItemType itemType; - final List _items; - @override - List get items { - if (_items is EqualUnmodifiableListView) return _items; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_items); - } - - @override - String toString() { - return 'SpotubeBrowseSectionObject.artist(id: $id, title: $title, externalUri: $externalUri, itemType: $itemType, items: $items)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$SpotubeBrowseArtistSectionObjectImpl && - (identical(other.id, id) || other.id == id) && - (identical(other.title, title) || other.title == title) && - (identical(other.externalUri, externalUri) || - other.externalUri == externalUri) && - (identical(other.itemType, itemType) || - other.itemType == itemType) && - const DeepCollectionEquality().equals(other._items, _items)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, id, title, externalUri, itemType, - const DeepCollectionEquality().hash(_items)); - - /// Create a copy of SpotubeBrowseSectionObject - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$SpotubeBrowseArtistSectionObjectImplCopyWith< - _$SpotubeBrowseArtistSectionObjectImpl> - get copyWith => __$$SpotubeBrowseArtistSectionObjectImplCopyWithImpl< - _$SpotubeBrowseArtistSectionObjectImpl>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - album, - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - artist, - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - playlist, - }) { - return artist(id, title, externalUri, itemType, items); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - album, - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - artist, - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - playlist, - }) { - return artist?.call(id, title, externalUri, itemType, items); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - album, - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - artist, - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - playlist, - required TResult orElse(), - }) { - if (artist != null) { - return artist(id, title, externalUri, itemType, items); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(SpotubeBrowseAlbumSectionObject value) album, - required TResult Function(SpotubeBrowseArtistSectionObject value) artist, - required TResult Function(SpotubeBrowsePlaylistSectionObject value) - playlist, - }) { - return artist(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(SpotubeBrowseAlbumSectionObject value)? album, - TResult? Function(SpotubeBrowseArtistSectionObject value)? artist, - TResult? Function(SpotubeBrowsePlaylistSectionObject value)? playlist, - }) { - return artist?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(SpotubeBrowseAlbumSectionObject value)? album, - TResult Function(SpotubeBrowseArtistSectionObject value)? artist, - TResult Function(SpotubeBrowsePlaylistSectionObject value)? playlist, - required TResult orElse(), - }) { - if (artist != null) { - return artist(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$SpotubeBrowseArtistSectionObjectImplToJson( - this, - ); - } -} - -abstract class SpotubeBrowseArtistSectionObject - implements SpotubeBrowseSectionObject { - factory SpotubeBrowseArtistSectionObject( - {required final String id, - required final String title, - required final String externalUri, - required final SectionItemType itemType, - required final List items}) = - _$SpotubeBrowseArtistSectionObjectImpl; - - factory SpotubeBrowseArtistSectionObject.fromJson(Map json) = - _$SpotubeBrowseArtistSectionObjectImpl.fromJson; - - @override - String get id; - @override - String get title; - @override - String get externalUri; - @override - SectionItemType get itemType; - @override - List get items; - - /// Create a copy of SpotubeBrowseSectionObject - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$SpotubeBrowseArtistSectionObjectImplCopyWith< - _$SpotubeBrowseArtistSectionObjectImpl> - get copyWith => throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class _$$SpotubeBrowsePlaylistSectionObjectImplCopyWith<$Res> - implements $SpotubeBrowseSectionObjectCopyWith<$Res> { - factory _$$SpotubeBrowsePlaylistSectionObjectImplCopyWith( - _$SpotubeBrowsePlaylistSectionObjectImpl value, - $Res Function(_$SpotubeBrowsePlaylistSectionObjectImpl) then) = - __$$SpotubeBrowsePlaylistSectionObjectImplCopyWithImpl<$Res>; - @override - @useResult - $Res call( - {String id, - String title, - String externalUri, - SectionItemType itemType, - List items}); -} - -/// @nodoc -class __$$SpotubeBrowsePlaylistSectionObjectImplCopyWithImpl<$Res> - extends _$SpotubeBrowseSectionObjectCopyWithImpl<$Res, - _$SpotubeBrowsePlaylistSectionObjectImpl> - implements _$$SpotubeBrowsePlaylistSectionObjectImplCopyWith<$Res> { - __$$SpotubeBrowsePlaylistSectionObjectImplCopyWithImpl( - _$SpotubeBrowsePlaylistSectionObjectImpl _value, - $Res Function(_$SpotubeBrowsePlaylistSectionObjectImpl) _then) - : super(_value, _then); - - /// Create a copy of SpotubeBrowseSectionObject - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? title = null, - Object? externalUri = null, - Object? itemType = null, - Object? items = null, - }) { - return _then(_$SpotubeBrowsePlaylistSectionObjectImpl( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as String, - title: null == title - ? _value.title - : title // ignore: cast_nullable_to_non_nullable - as String, - externalUri: null == externalUri - ? _value.externalUri - : externalUri // ignore: cast_nullable_to_non_nullable - as String, - itemType: null == itemType - ? _value.itemType - : itemType // ignore: cast_nullable_to_non_nullable - as SectionItemType, - items: null == items - ? _value._items - : items // ignore: cast_nullable_to_non_nullable - as List, - )); - } -} - -/// @nodoc -@JsonSerializable() -class _$SpotubeBrowsePlaylistSectionObjectImpl - implements SpotubeBrowsePlaylistSectionObject { - _$SpotubeBrowsePlaylistSectionObjectImpl( - {required this.id, - required this.title, - required this.externalUri, - required this.itemType, - required final List items}) - : _items = items; - - factory _$SpotubeBrowsePlaylistSectionObjectImpl.fromJson( - Map json) => - _$$SpotubeBrowsePlaylistSectionObjectImplFromJson(json); - - @override - final String id; - @override - final String title; - @override - final String externalUri; - @override - final SectionItemType itemType; - final List _items; - @override - List get items { - if (_items is EqualUnmodifiableListView) return _items; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_items); - } - - @override - String toString() { - return 'SpotubeBrowseSectionObject.playlist(id: $id, title: $title, externalUri: $externalUri, itemType: $itemType, items: $items)'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && - other is _$SpotubeBrowsePlaylistSectionObjectImpl && - (identical(other.id, id) || other.id == id) && - (identical(other.title, title) || other.title == title) && - (identical(other.externalUri, externalUri) || - other.externalUri == externalUri) && - (identical(other.itemType, itemType) || - other.itemType == itemType) && - const DeepCollectionEquality().equals(other._items, _items)); - } - - @JsonKey(includeFromJson: false, includeToJson: false) - @override - int get hashCode => Object.hash(runtimeType, id, title, externalUri, itemType, - const DeepCollectionEquality().hash(_items)); - - /// Create a copy of SpotubeBrowseSectionObject - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - @override - @pragma('vm:prefer-inline') - _$$SpotubeBrowsePlaylistSectionObjectImplCopyWith< - _$SpotubeBrowsePlaylistSectionObjectImpl> - get copyWith => __$$SpotubeBrowsePlaylistSectionObjectImplCopyWithImpl< - _$SpotubeBrowsePlaylistSectionObjectImpl>(this, _$identity); - - @override - @optionalTypeArgs - TResult when({ - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - album, - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - artist, - required TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items) - playlist, - }) { - return playlist(id, title, externalUri, itemType, items); - } - - @override - @optionalTypeArgs - TResult? whenOrNull({ - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - album, - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - artist, - TResult? Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - playlist, - }) { - return playlist?.call(id, title, externalUri, itemType, items); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - album, - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - artist, - TResult Function(String id, String title, String externalUri, - SectionItemType itemType, List items)? - playlist, - required TResult orElse(), - }) { - if (playlist != null) { - return playlist(id, title, externalUri, itemType, items); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(SpotubeBrowseAlbumSectionObject value) album, - required TResult Function(SpotubeBrowseArtistSectionObject value) artist, - required TResult Function(SpotubeBrowsePlaylistSectionObject value) - playlist, - }) { - return playlist(this); - } - - @override - @optionalTypeArgs - TResult? mapOrNull({ - TResult? Function(SpotubeBrowseAlbumSectionObject value)? album, - TResult? Function(SpotubeBrowseArtistSectionObject value)? artist, - TResult? Function(SpotubeBrowsePlaylistSectionObject value)? playlist, - }) { - return playlist?.call(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(SpotubeBrowseAlbumSectionObject value)? album, - TResult Function(SpotubeBrowseArtistSectionObject value)? artist, - TResult Function(SpotubeBrowsePlaylistSectionObject value)? playlist, - required TResult orElse(), - }) { - if (playlist != null) { - return playlist(this); - } - return orElse(); - } - - @override - Map toJson() { - return _$$SpotubeBrowsePlaylistSectionObjectImplToJson( - this, - ); - } -} - -abstract class SpotubeBrowsePlaylistSectionObject - implements SpotubeBrowseSectionObject { - factory SpotubeBrowsePlaylistSectionObject( - {required final String id, - required final String title, - required final String externalUri, - required final SectionItemType itemType, - required final List items}) = - _$SpotubeBrowsePlaylistSectionObjectImpl; - - factory SpotubeBrowsePlaylistSectionObject.fromJson( - Map json) = - _$SpotubeBrowsePlaylistSectionObjectImpl.fromJson; - - @override - String get id; - @override - String get title; - @override - String get externalUri; - @override - SectionItemType get itemType; - @override - List get items; - - /// Create a copy of SpotubeBrowseSectionObject - /// with the given fields replaced by the non-null parameter values. - @override - @JsonKey(includeFromJson: false, includeToJson: false) - _$$SpotubeBrowsePlaylistSectionObjectImplCopyWith< - _$SpotubeBrowsePlaylistSectionObjectImpl> + _$$SpotubeBrowseSectionObjectImplCopyWith> get copyWith => throw _privateConstructorUsedError; } diff --git a/lib/models/metadata/metadata.g.dart b/lib/models/metadata/metadata.g.dart index 5bd63591..52001504 100644 --- a/lib/models/metadata/metadata.g.dart +++ b/lib/models/metadata/metadata.g.dart @@ -64,8 +64,8 @@ _$SpotubeSimpleAlbumObjectImpl _$$SpotubeSimpleAlbumObjectImplFromJson( Map.from(e as Map))) .toList() ?? const [], - releaseDate: json['releaseDate'] as String, albumType: $enumDecode(_$SpotubeAlbumTypeEnumMap, json['albumType']), + releaseDate: json['releaseDate'] as String?, ); Map _$$SpotubeSimpleAlbumObjectImplToJson( @@ -76,8 +76,8 @@ Map _$$SpotubeSimpleAlbumObjectImplToJson( 'externalUri': instance.externalUri, 'artists': instance.artists.map((e) => e.toJson()).toList(), 'images': instance.images.map((e) => e.toJson()).toList(), - 'releaseDate': instance.releaseDate, 'albumType': _$SpotubeAlbumTypeEnumMap[instance.albumType]!, + 'releaseDate': instance.releaseDate, }; _$SpotubeFullArtistObjectImpl _$$SpotubeFullArtistObjectImplFromJson( @@ -123,79 +123,29 @@ Map _$$SpotubeSimpleArtistObjectImplToJson( 'externalUri': instance.externalUri, }; -_$SpotubeBrowseAlbumSectionObjectImpl - _$$SpotubeBrowseAlbumSectionObjectImplFromJson(Map json) => - _$SpotubeBrowseAlbumSectionObjectImpl( +_$SpotubeBrowseSectionObjectImpl + _$$SpotubeBrowseSectionObjectImplFromJson( + Map json, + T Function(Object? json) fromJsonT, +) => + _$SpotubeBrowseSectionObjectImpl( id: json['id'] as String, title: json['title'] as String, externalUri: json['externalUri'] as String, - itemType: $enumDecode(_$SectionItemTypeEnumMap, json['itemType']), - items: (json['items'] as List) - .map((e) => SpotubeSimpleAlbumObject.fromJson( - Map.from(e as Map))) - .toList(), + browseMore: json['browseMore'] as bool, + items: (json['items'] as List).map(fromJsonT).toList(), ); -Map _$$SpotubeBrowseAlbumSectionObjectImplToJson( - _$SpotubeBrowseAlbumSectionObjectImpl instance) => +Map _$$SpotubeBrowseSectionObjectImplToJson( + _$SpotubeBrowseSectionObjectImpl instance, + Object? Function(T value) toJsonT, +) => { 'id': instance.id, 'title': instance.title, 'externalUri': instance.externalUri, - 'itemType': _$SectionItemTypeEnumMap[instance.itemType]!, - 'items': instance.items.map((e) => e.toJson()).toList(), - }; - -const _$SectionItemTypeEnumMap = { - SectionItemType.playlist: 'Playlist', - SectionItemType.album: 'Album', - SectionItemType.artist: 'Artist', -}; - -_$SpotubeBrowseArtistSectionObjectImpl - _$$SpotubeBrowseArtistSectionObjectImplFromJson(Map json) => - _$SpotubeBrowseArtistSectionObjectImpl( - id: json['id'] as String, - title: json['title'] as String, - externalUri: json['externalUri'] as String, - itemType: $enumDecode(_$SectionItemTypeEnumMap, json['itemType']), - items: (json['items'] as List) - .map((e) => SpotubeSimpleArtistObject.fromJson( - Map.from(e as Map))) - .toList(), - ); - -Map _$$SpotubeBrowseArtistSectionObjectImplToJson( - _$SpotubeBrowseArtistSectionObjectImpl instance) => - { - 'id': instance.id, - 'title': instance.title, - 'externalUri': instance.externalUri, - 'itemType': _$SectionItemTypeEnumMap[instance.itemType]!, - 'items': instance.items.map((e) => e.toJson()).toList(), - }; - -_$SpotubeBrowsePlaylistSectionObjectImpl - _$$SpotubeBrowsePlaylistSectionObjectImplFromJson(Map json) => - _$SpotubeBrowsePlaylistSectionObjectImpl( - id: json['id'] as String, - title: json['title'] as String, - externalUri: json['externalUri'] as String, - itemType: $enumDecode(_$SectionItemTypeEnumMap, json['itemType']), - items: (json['items'] as List) - .map((e) => SpotubeSimplePlaylistObject.fromJson( - Map.from(e as Map))) - .toList(), - ); - -Map _$$SpotubeBrowsePlaylistSectionObjectImplToJson( - _$SpotubeBrowsePlaylistSectionObjectImpl instance) => - { - 'id': instance.id, - 'title': instance.title, - 'externalUri': instance.externalUri, - 'itemType': _$SectionItemTypeEnumMap[instance.itemType]!, - 'items': instance.items.map((e) => e.toJson()).toList(), + 'browseMore': instance.browseMore, + 'items': instance.items.map(toJsonT).toList(), }; _$SpotubeImageObjectImpl _$$SpotubeImageObjectImplFromJson(Map json) => diff --git a/lib/modules/album/album_card.dart b/lib/modules/album/album_card.dart index 33a7dc01..0ea07f51 100644 --- a/lib/modules/album/album_card.dart +++ b/lib/modules/album/album_card.dart @@ -63,7 +63,7 @@ class AlbumCard extends HookConsumerWidget { ); var isLoading = (isPlaylistPlaying && isFetchingActiveTrack) || updating.value; - var description = "${album.albumType} • ${album.artists.asString()}"; + var description = "${album.albumType.name} • ${album.artists.asString()}"; void onTap() { context.navigateTo(AlbumRoute(id: album.id, album: album)); diff --git a/lib/modules/home/sections/feed.dart b/lib/modules/home/sections/feed.dart deleted file mode 100644 index 216dc491..00000000 --- a/lib/modules/home/sections/feed.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:shadcn_flutter/shadcn_flutter.dart'; -import 'package:spotube/collections/routes.gr.dart'; -import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart'; -import 'package:spotube/extensions/context.dart'; -import 'package:spotube/provider/spotify/views/home.dart'; - -class HomePageFeedSection extends HookConsumerWidget { - const HomePageFeedSection({super.key}); - - @override - Widget build(BuildContext context, ref) { - final homeFeed = ref.watch(homeViewProvider); - final nonShortSections = homeFeed.asData?.value.sections - .where((s) => s.typename == "HomeGenericSectionData") - .toList() ?? - []; - - return SliverList.builder( - itemCount: nonShortSections.length, - itemBuilder: (context, index) { - final section = nonShortSections[index]; - if (section.items.isEmpty) return const SizedBox.shrink(); - - return HorizontalPlaybuttonCardView( - items: [ - for (final item in section.items) - if (item.album != null) - item.album!.asAlbum - else if (item.artist != null) - item.artist!.asArtist - else if (item.playlist != null) - item.playlist!.asPlaylist - ], - title: Text(section.title ?? context.l10n.no_title), - hasNextPage: false, - isLoadingNextPage: false, - onFetchMore: () {}, - titleTrailing: Button.text( - child: Text(context.l10n.browse_all), - onPressed: () { - context.navigateTo(HomeFeedSectionRoute(sectionUri: section.uri)); - }, - ), - ); - }, - ); - } -} diff --git a/lib/modules/home/sections/sections.dart b/lib/modules/home/sections/sections.dart new file mode 100644 index 00000000..cc7a7fad --- /dev/null +++ b/lib/modules/home/sections/sections.dart @@ -0,0 +1,46 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:spotube/collections/routes.gr.dart'; +import 'package:spotube/components/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart'; +import 'package:spotube/extensions/context.dart'; +import 'package:spotube/provider/metadata_plugin/browse/sections.dart'; + +class HomePageBrowseSection extends HookConsumerWidget { + const HomePageBrowseSection({super.key}); + + @override + Widget build(BuildContext context, ref) { + final browseSections = ref.watch(metadataPluginBrowseSectionsProvider); + final sections = browseSections.asData?.value.items; + + return SliverList.builder( + itemCount: sections?.length ?? 0, + itemBuilder: (context, index) { + final section = sections![index]; + if (section.items.isEmpty) return const SizedBox.shrink(); + + return HorizontalPlaybuttonCardView( + items: section.items, + title: Text(section.title), + hasNextPage: false, + isLoadingNextPage: false, + onFetchMore: () {}, + titleTrailing: section.browseMore + ? Button.text( + child: Text(context.l10n.browse_all), + onPressed: () { + context.navigateTo( + HomeBrowseSectionItemsRoute( + sectionId: section.id, + section: section, + ), + ); + }, + ) + : null, + ); + }, + ); + } +} diff --git a/lib/pages/home/feed/feed_section.dart b/lib/pages/home/feed/feed_section.dart deleted file mode 100644 index 5ece363d..00000000 --- a/lib/pages/home/feed/feed_section.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:shadcn_flutter/shadcn_flutter.dart'; -import 'package:skeletonizer/skeletonizer.dart'; -import 'package:spotube/collections/fake.dart'; -import 'package:spotube/components/playbutton_view/playbutton_view.dart'; -import 'package:spotube/modules/album/album_card.dart'; -import 'package:spotube/modules/artist/artist_card.dart'; -import 'package:spotube/modules/playlist/playlist_card.dart'; -import 'package:spotube/components/titlebar/titlebar.dart'; -import 'package:spotube/provider/spotify/views/home_section.dart'; - -@RoutePage() -class HomeFeedSectionPage extends HookConsumerWidget { - static const name = "home_feed_section"; - - final String sectionUri; - const HomeFeedSectionPage({ - super.key, - @PathParam("feedId") required this.sectionUri, - }); - - @override - Widget build(BuildContext context, ref) { - final homeFeedSection = ref.watch(homeSectionViewProvider(sectionUri)); - final section = homeFeedSection.asData?.value ?? FakeData.feedSection; - final controller = useScrollController(); - final isArtist = section.items.every((item) => item.artist != null); - - return SafeArea( - bottom: false, - child: Skeletonizer( - enabled: homeFeedSection.isLoading, - child: Scaffold( - headers: [ - TitleBar( - title: Text(section.title ?? ""), - ) - ], - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: CustomScrollView( - controller: controller, - slivers: [ - // if (isArtist) - // SliverGrid.builder( - // gridDelegate: - // const SliverGridDelegateWithMaxCrossAxisExtent( - // maxCrossAxisExtent: 200, - // mainAxisExtent: 250, - // crossAxisSpacing: 8, - // mainAxisSpacing: 8, - // ), - // itemCount: section.items.length, - // itemBuilder: (context, index) { - // final item = section.items[index]; - // return ArtistCard(item.artist!.asArtist); - // }, - // ) - // else - // PlaybuttonView( - // controller: controller, - // itemCount: section.items.length, - // hasMore: false, - // isLoading: false, - // onRequestMore: () => {}, - // listItemBuilder: (context, index) { - // final item = section.items[index]; - // if (item.album != null) { - // return AlbumCard.tile(item.album!.asAlbum); - // } - // if (item.playlist != null) { - // return PlaylistCard.tile(item.playlist!.asPlaylist); - // } - // return const SizedBox.shrink(); - // }, - // gridItemBuilder: (context, index) { - // final item = section.items[index]; - // if (item.album != null) { - // return AlbumCard(item.album!.asAlbum); - // } - // if (item.playlist != null) { - // return PlaylistCard(item.playlist!.asPlaylist); - // } - // return const SizedBox.shrink(); - // }, - // ), - const SliverToBoxAdapter( - child: SafeArea( - child: SizedBox(), - ), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index 1ef81027..53bb4fb2 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -12,7 +12,7 @@ import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/models/database/database.dart'; import 'package:spotube/modules/connect/connect_device.dart'; import 'package:spotube/modules/home/sections/featured.dart'; -import 'package:spotube/modules/home/sections/feed.dart'; +import 'package:spotube/modules/home/sections/sections.dart'; import 'package:spotube/modules/home/sections/friends.dart'; import 'package:spotube/modules/home/sections/genres/genres.dart'; import 'package:spotube/modules/home/sections/made_for_user.dart'; @@ -76,20 +76,19 @@ class HomePage extends HookConsumerWidget { else if (kIsMacOS) const SliverGap(10), const SliverGap(10), - SliverList.builder( - itemCount: 5, - itemBuilder: (context, index) { - return switch (index) { - 0 => const HomeGenresSection(), - 1 => const HomeRecentlyPlayedSection(), - 2 => const HomeFeaturedSection(), - 3 => const HomePageFriendsSection(), - _ => const HomeNewReleasesSection() - }; - }, - ), - const HomePageFeedSection(), - const SliverSafeArea(sliver: HomeMadeForUserSection()), + // SliverList.builder( + // itemCount: 5, + // itemBuilder: (context, index) { + // return switch (index) { + // 0 => const HomeGenresSection(), + // 1 => const HomeRecentlyPlayedSection(), + // 2 => const HomeFeaturedSection(), + // 3 => const HomePageFriendsSection(), + // _ => const HomeNewReleasesSection() + // }; + // }, + // ), + const HomePageBrowseSection(), ], ), )); diff --git a/lib/pages/home/sections/section_items.dart b/lib/pages/home/sections/section_items.dart new file mode 100644 index 00000000..89666d26 --- /dev/null +++ b/lib/pages/home/sections/section_items.dart @@ -0,0 +1,122 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:shadcn_flutter/shadcn_flutter_extension.dart'; +import 'package:skeletonizer/skeletonizer.dart'; +import 'package:spotube/components/playbutton_view/playbutton_card.dart'; +import 'package:spotube/components/waypoint.dart'; +import 'package:spotube/models/metadata/metadata.dart'; +import 'package:spotube/modules/album/album_card.dart'; +import 'package:spotube/modules/artist/artist_card.dart'; +import 'package:spotube/modules/playlist/playlist_card.dart'; +import 'package:spotube/components/titlebar/titlebar.dart'; +import 'package:spotube/provider/metadata_plugin/browse/section_items.dart'; +import 'package:spotube/provider/metadata_plugin/utils/common.dart'; + +const _dummyPlaybuttonCard = PlaybuttonCard( + imageUrl: 'https://placehold.co/150x150.png', + isLoading: false, + isPlaying: false, + title: "Playbutton", + description: "A really cool playbutton", + isOwner: false, +); + +@RoutePage() +class HomeBrowseSectionItemsPage extends HookConsumerWidget { + static const name = "home_browse_section_items"; + + final String sectionId; + final SpotubeBrowseSectionObject section; + const HomeBrowseSectionItemsPage({ + super.key, + @PathParam("sectionId") required this.sectionId, + required this.section, + }); + + @override + Widget build(BuildContext context, ref) { + final scale = context.theme.scaling; + + final sectionItems = + ref.watch(metadataPluginBrowseSectionItemsProvider(sectionId)); + final sectionItemsNotifier = + ref.watch(metadataPluginBrowseSectionItemsProvider(sectionId).notifier); + final items = sectionItems.asData?.value.items ?? []; + final controller = useScrollController(); + + final isLoading = sectionItems.isLoading || sectionItems.isLoadingNextPage; + final itemCount = items.length; + final hasMore = sectionItems.asData?.value.hasMore ?? false; + + return SafeArea( + bottom: false, + child: Skeletonizer( + enabled: sectionItems.isLoading, + child: Scaffold( + headers: [ + TitleBar( + title: Text(section.title), + ) + ], + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: CustomScrollView( + controller: controller, + slivers: [ + SliverGrid.builder( + itemCount: isLoading ? 6 : itemCount + 1, + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 150 * scale, + mainAxisExtent: 225 * scale, + crossAxisSpacing: 12 * scale, + mainAxisSpacing: 12 * scale, + ), + itemBuilder: (context, index) { + if (isLoading) { + return const Skeletonizer( + enabled: true, + child: _dummyPlaybuttonCard, + ); + } + + if (index == itemCount) { + if (!hasMore) return const SizedBox.shrink(); + return Waypoint( + controller: controller, + isGrid: true, + onTouchEdge: () async { + await sectionItemsNotifier.fetchMore(); + }, + child: const Skeletonizer( + enabled: true, + child: _dummyPlaybuttonCard, + ), + ); + } + + final item = items[index]; + return switch (item) { + SpotubeFullArtistObject() => ArtistCard(item), + SpotubeSimplePlaylistObject() => PlaylistCard(item), + SpotubeSimpleAlbumObject() => AlbumCard(item), + _ => throw Exception( + "Unsupported item type: ${item.runtimeType}", + ), + }; + }, + ), + const SliverToBoxAdapter( + child: SafeArea( + child: SizedBox(), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/provider/metadata_plugin/browse/section_items.dart b/lib/provider/metadata_plugin/browse/section_items.dart new file mode 100644 index 00000000..542db00e --- /dev/null +++ b/lib/provider/metadata_plugin/browse/section_items.dart @@ -0,0 +1,32 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:spotube/models/metadata/metadata.dart'; +import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; +import 'package:spotube/provider/metadata_plugin/utils/family_paginated.dart'; + +class MetadataPluginBrowseSectionItemsNotifier + extends FamilyPaginatedAsyncNotifier { + @override + Future> fetch( + int offset, + int limit, + ) async { + return await (await metadataPlugin).browse.sectionItems( + arg, + limit: limit, + offset: offset, + ); + } + + @override + build(arg) async { + ref.watch(metadataPluginProvider); + return await fetch(0, 20); + } +} + +final metadataPluginBrowseSectionItemsProvider = AsyncNotifierProviderFamily< + MetadataPluginBrowseSectionItemsNotifier, + SpotubePaginationResponseObject, + String>( + () => MetadataPluginBrowseSectionItemsNotifier(), +); diff --git a/lib/provider/metadata_plugin/browse/sections.dart b/lib/provider/metadata_plugin/browse/sections.dart new file mode 100644 index 00000000..957daa86 --- /dev/null +++ b/lib/provider/metadata_plugin/browse/sections.dart @@ -0,0 +1,31 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:spotube/models/metadata/metadata.dart'; +import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; +import 'package:spotube/provider/metadata_plugin/utils/paginated.dart'; + +class MetadataPluginBrowseSectionsNotifier + extends PaginatedAsyncNotifier> { + @override + Future>> + fetch( + int offset, + int limit, + ) async { + return await (await metadataPlugin).browse.sections( + limit: limit, + offset: offset, + ); + } + + @override + build() async { + ref.watch(metadataPluginProvider); + return await fetch(0, 20); + } +} + +final metadataPluginBrowseSectionsProvider = AsyncNotifierProvider< + MetadataPluginBrowseSectionsNotifier, + SpotubePaginationResponseObject>>( + () => MetadataPluginBrowseSectionsNotifier(), +); diff --git a/lib/provider/metadata_plugin/utils/common.dart b/lib/provider/metadata_plugin/utils/common.dart index 2c9877a3..98a1f4e4 100644 --- a/lib/provider/metadata_plugin/utils/common.dart +++ b/lib/provider/metadata_plugin/utils/common.dart @@ -9,6 +9,10 @@ import 'package:spotube/provider/metadata_plugin/metadata_plugin_provider.dart'; import 'package:spotube/services/metadata/endpoints/error.dart'; import 'package:spotube/services/metadata/metadata.dart'; +extension PaginationExtension on AsyncValue { + bool get isLoadingNextPage => this is AsyncData && this is AsyncLoadingNext; +} + mixin MetadataPluginMixin // ignore: invalid_use_of_internal_member on AsyncNotifierBase> { diff --git a/lib/services/metadata/endpoints/browse.dart b/lib/services/metadata/endpoints/browse.dart index e69de29b..c8105ad1 100644 --- a/lib/services/metadata/endpoints/browse.dart +++ b/lib/services/metadata/endpoints/browse.dart @@ -0,0 +1,87 @@ +import 'package:hetu_script/hetu_script.dart'; +import 'package:hetu_script/values.dart'; +import 'package:spotube/models/metadata/metadata.dart'; + +class MetadataPluginBrowseEndpoint { + final Hetu hetu; + MetadataPluginBrowseEndpoint(this.hetu); + + HTInstance get hetuMetadataBrowse => + (hetu.fetch("metadataPlugin") as HTInstance).memberGet("browse") + as HTInstance; + + Future>> + sections({ + int? offset, + int? limit, + }) async { + final raw = await hetuMetadataBrowse.invoke( + "sections", + namedArgs: { + "offset": offset, + "limit": limit, + }..removeWhere((key, value) => value == null), + ) as Map; + + return SpotubePaginationResponseObject< + SpotubeBrowseSectionObject>.fromJson( + raw.cast(), + (Map json) => SpotubeBrowseSectionObject.fromJson( + json.cast(), + (json) { + final isPlaylist = json["owner"] != null; + final isAlbum = json["artists"] != null; + if (isPlaylist) { + return SpotubeSimplePlaylistObject.fromJson( + json.cast(), + ); + } else if (isAlbum) { + return SpotubeSimpleAlbumObject.fromJson( + json.cast(), + ); + } else { + return SpotubeFullArtistObject.fromJson( + json.cast(), + ); + } + }, + ), + ); + } + + Future> sectionItems( + String id, { + int? offset, + int? limit, + }) async { + final raw = await hetuMetadataBrowse.invoke( + "sectionItems", + positionalArgs: [id], + namedArgs: { + "offset": offset, + "limit": limit, + }..removeWhere((key, value) => value == null), + ) as Map; + + return SpotubePaginationResponseObject.fromJson( + raw.cast(), + (json) { + final isPlaylist = json["owner"] != null; + final isAlbum = json["artists"] != null; + if (isPlaylist) { + return SpotubeSimplePlaylistObject.fromJson( + json.cast(), + ); + } else if (isAlbum) { + return SpotubeSimpleAlbumObject.fromJson( + json.cast(), + ); + } else { + return SpotubeFullArtistObject.fromJson( + json.cast(), + ); + } + }, + ); + } +} diff --git a/lib/services/metadata/metadata.dart b/lib/services/metadata/metadata.dart index 38d35d23..034459a9 100644 --- a/lib/services/metadata/metadata.dart +++ b/lib/services/metadata/metadata.dart @@ -14,6 +14,7 @@ import 'package:spotube/services/metadata/apis/localstorage.dart'; import 'package:spotube/services/metadata/endpoints/album.dart'; import 'package:spotube/services/metadata/endpoints/artist.dart'; import 'package:spotube/services/metadata/endpoints/auth.dart'; +import 'package:spotube/services/metadata/endpoints/browse.dart'; import 'package:spotube/services/metadata/endpoints/playlist.dart'; import 'package:spotube/services/metadata/endpoints/user.dart'; @@ -78,6 +79,7 @@ class MetadataPlugin { late final MetadataPluginAlbumEndpoint album; late final MetadataPluginArtistEndpoint artist; + late final MetadataPluginBrowseEndpoint browse; late final MetadataPluginPlaylistEndpoint playlist; late final MetadataPluginUserEndpoint user; @@ -86,6 +88,7 @@ class MetadataPlugin { artist = MetadataPluginArtistEndpoint(hetu); album = MetadataPluginAlbumEndpoint(hetu); + browse = MetadataPluginBrowseEndpoint(hetu); playlist = MetadataPluginPlaylistEndpoint(hetu); user = MetadataPluginUserEndpoint(hetu); }