diff --git a/lib/collections/fake.dart b/lib/collections/fake.dart index 849b4d17..09bb8087 100644 --- a/lib/collections/fake.dart +++ b/lib/collections/fake.dart @@ -105,6 +105,16 @@ abstract class FakeData { ..explicit = false ..linkedFrom = trackLink; + static final simpleTrack = SpotubeSimpleTrackObject( + id: "1", + name: "A Track Name", + artists: [], + album: albumSimple, + externalUri: "https://example.com", + durationMs: 50000, + explicit: false, + ); + static final TrackLink trackLink = TrackLink() ..id = "1" ..type = "type" diff --git a/lib/components/heart_button/use_track_toggle_like.dart b/lib/components/heart_button/use_track_toggle_like.dart index ba5cbee1..349a4ab2 100644 --- a/lib/components/heart_button/use_track_toggle_like.dart +++ b/lib/components/heart_button/use_track_toggle_like.dart @@ -1,36 +1,31 @@ -import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:spotify/spotify.dart'; -import 'package:spotube/provider/scrobbler/scrobbler.dart'; -import 'package:spotube/provider/spotify/spotify.dart'; +import 'package:spotube/models/metadata/metadata.dart'; +import 'package:spotube/provider/metadata_plugin/library/tracks.dart'; typedef UseTrackToggleLike = ({ bool isLiked, - Future Function(Track track) toggleTrackLike, + Future Function(SpotubeTrackObject track) toggleTrackLike, }); -UseTrackToggleLike useTrackToggleLike(Track track, WidgetRef ref) { - final savedTracks = ref.watch(likedTracksProvider); - final savedTracksNotifier = ref.watch(likedTracksProvider.notifier); +UseTrackToggleLike useTrackToggleLike(SpotubeTrackObject track, WidgetRef ref) { + final savedTracksNotifier = + ref.watch(metadataPluginSavedTracksProvider.notifier); - final isLiked = useMemoized( - () => - savedTracks.asData?.value.any((element) => element.id == track.id) ?? - false, - [savedTracks.asData?.value, track.id], + final isSavedTrack = ref.watch( + metadataPluginIsSavedTrackProvider(track.id), ); - final scrobblerNotifier = ref.read(scrobblerProvider.notifier); - return ( - isLiked: isLiked, + isLiked: isSavedTrack.asData?.value ?? false, toggleTrackLike: (track) async { - await savedTracksNotifier.toggleFavorite(track); + final isLikedTrack = await ref.read( + metadataPluginIsSavedTrackProvider(track.id).future, + ); - if (!isLiked) { - await scrobblerNotifier.love(track); + if (isLikedTrack) { + await savedTracksNotifier.removeFavorite([track]); } else { - await scrobblerNotifier.unlove(track); + await savedTracksNotifier.addFavorite([track]); } }, ); diff --git a/lib/models/metadata/metadata.freezed.dart b/lib/models/metadata/metadata.freezed.dart index d01743a4..d1fa4f45 100644 --- a/lib/models/metadata/metadata.freezed.dart +++ b/lib/models/metadata/metadata.freezed.dart @@ -2820,38 +2820,134 @@ abstract class _SpotubeSearchResponseObject get copyWith => throw _privateConstructorUsedError; } -SpotubeFullTrackObject _$SpotubeFullTrackObjectFromJson( - Map json) { - return _SpotubeFullTrackObject.fromJson(json); +SpotubeTrackObject _$SpotubeTrackObjectFromJson(Map json) { + switch (json['runtimeType']) { + case 'full': + return SpotubeFullTrackObject.fromJson(json); + case 'simple': + return SpotubeSimpleTrackObject.fromJson(json); + + default: + throw CheckedFromJsonException(json, 'runtimeType', 'SpotubeTrackObject', + 'Invalid union type "${json['runtimeType']}"!'); + } } /// @nodoc -mixin _$SpotubeFullTrackObject { +mixin _$SpotubeTrackObject { String get id => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError; String get externalUri => throw _privateConstructorUsedError; List get artists => throw _privateConstructorUsedError; - SpotubeSimpleAlbumObject get album => throw _privateConstructorUsedError; + SpotubeSimpleAlbumObject? get album => throw _privateConstructorUsedError; int get durationMs => throw _privateConstructorUsedError; - String get isrc => throw _privateConstructorUsedError; bool get explicit => throw _privateConstructorUsedError; + @optionalTypeArgs + TResult when({ + required TResult Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit) + full, + required TResult Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album) + simple, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit)? + full, + TResult? Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album)? + simple, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeWhen({ + TResult Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit)? + full, + TResult Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album)? + simple, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult map({ + required TResult Function(SpotubeFullTrackObject value) full, + required TResult Function(SpotubeSimpleTrackObject value) simple, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(SpotubeFullTrackObject value)? full, + TResult? Function(SpotubeSimpleTrackObject value)? simple, + }) => + throw _privateConstructorUsedError; + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SpotubeFullTrackObject value)? full, + TResult Function(SpotubeSimpleTrackObject value)? simple, + required TResult orElse(), + }) => + throw _privateConstructorUsedError; - /// Serializes this SpotubeFullTrackObject to a JSON map. + /// Serializes this SpotubeTrackObject to a JSON map. Map toJson() => throw _privateConstructorUsedError; - /// Create a copy of SpotubeFullTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) - $SpotubeFullTrackObjectCopyWith get copyWith => + $SpotubeTrackObjectCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $SpotubeFullTrackObjectCopyWith<$Res> { - factory $SpotubeFullTrackObjectCopyWith(SpotubeFullTrackObject value, - $Res Function(SpotubeFullTrackObject) then) = - _$SpotubeFullTrackObjectCopyWithImpl<$Res, SpotubeFullTrackObject>; +abstract class $SpotubeTrackObjectCopyWith<$Res> { + factory $SpotubeTrackObjectCopyWith( + SpotubeTrackObject value, $Res Function(SpotubeTrackObject) then) = + _$SpotubeTrackObjectCopyWithImpl<$Res, SpotubeTrackObject>; @useResult $Res call( {String id, @@ -2860,24 +2956,22 @@ abstract class $SpotubeFullTrackObjectCopyWith<$Res> { List artists, SpotubeSimpleAlbumObject album, int durationMs, - String isrc, bool explicit}); - $SpotubeSimpleAlbumObjectCopyWith<$Res> get album; + $SpotubeSimpleAlbumObjectCopyWith<$Res>? get album; } /// @nodoc -class _$SpotubeFullTrackObjectCopyWithImpl<$Res, - $Val extends SpotubeFullTrackObject> - implements $SpotubeFullTrackObjectCopyWith<$Res> { - _$SpotubeFullTrackObjectCopyWithImpl(this._value, this._then); +class _$SpotubeTrackObjectCopyWithImpl<$Res, $Val extends SpotubeTrackObject> + implements $SpotubeTrackObjectCopyWith<$Res> { + _$SpotubeTrackObjectCopyWithImpl(this._value, this._then); // ignore: unused_field final $Val _value; // ignore: unused_field final $Res Function($Val) _then; - /// Create a copy of SpotubeFullTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override @@ -2888,7 +2982,6 @@ class _$SpotubeFullTrackObjectCopyWithImpl<$Res, Object? artists = null, Object? album = null, Object? durationMs = null, - Object? isrc = null, Object? explicit = null, }) { return _then(_value.copyWith( @@ -2909,17 +3002,13 @@ class _$SpotubeFullTrackObjectCopyWithImpl<$Res, : artists // ignore: cast_nullable_to_non_nullable as List, album: null == album - ? _value.album + ? _value.album! : album // ignore: cast_nullable_to_non_nullable as SpotubeSimpleAlbumObject, durationMs: null == durationMs ? _value.durationMs : durationMs // ignore: cast_nullable_to_non_nullable as int, - isrc: null == isrc - ? _value.isrc - : isrc // ignore: cast_nullable_to_non_nullable - as String, explicit: null == explicit ? _value.explicit : explicit // ignore: cast_nullable_to_non_nullable @@ -2927,12 +3016,16 @@ class _$SpotubeFullTrackObjectCopyWithImpl<$Res, ) as $Val); } - /// Create a copy of SpotubeFullTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') - $SpotubeSimpleAlbumObjectCopyWith<$Res> get album { - return $SpotubeSimpleAlbumObjectCopyWith<$Res>(_value.album, (value) { + $SpotubeSimpleAlbumObjectCopyWith<$Res>? get album { + if (_value.album == null) { + return null; + } + + return $SpotubeSimpleAlbumObjectCopyWith<$Res>(_value.album!, (value) { return _then(_value.copyWith(album: value) as $Val); }); } @@ -2940,7 +3033,7 @@ class _$SpotubeFullTrackObjectCopyWithImpl<$Res, /// @nodoc abstract class _$$SpotubeFullTrackObjectImplCopyWith<$Res> - implements $SpotubeFullTrackObjectCopyWith<$Res> { + implements $SpotubeTrackObjectCopyWith<$Res> { factory _$$SpotubeFullTrackObjectImplCopyWith( _$SpotubeFullTrackObjectImpl value, $Res Function(_$SpotubeFullTrackObjectImpl) then) = @@ -2963,15 +3056,14 @@ abstract class _$$SpotubeFullTrackObjectImplCopyWith<$Res> /// @nodoc class __$$SpotubeFullTrackObjectImplCopyWithImpl<$Res> - extends _$SpotubeFullTrackObjectCopyWithImpl<$Res, - _$SpotubeFullTrackObjectImpl> + extends _$SpotubeTrackObjectCopyWithImpl<$Res, _$SpotubeFullTrackObjectImpl> implements _$$SpotubeFullTrackObjectImplCopyWith<$Res> { __$$SpotubeFullTrackObjectImplCopyWithImpl( _$SpotubeFullTrackObjectImpl _value, $Res Function(_$SpotubeFullTrackObjectImpl) _then) : super(_value, _then); - /// Create a copy of SpotubeFullTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override @@ -3020,11 +3112,21 @@ class __$$SpotubeFullTrackObjectImplCopyWithImpl<$Res> as bool, )); } + + /// Create a copy of SpotubeTrackObject + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $SpotubeSimpleAlbumObjectCopyWith<$Res> get album { + return $SpotubeSimpleAlbumObjectCopyWith<$Res>(_value.album, (value) { + return _then(_value.copyWith(album: value)); + }); + } } /// @nodoc @JsonSerializable() -class _$SpotubeFullTrackObjectImpl implements _SpotubeFullTrackObject { +class _$SpotubeFullTrackObjectImpl implements SpotubeFullTrackObject { _$SpotubeFullTrackObjectImpl( {required this.id, required this.name, @@ -3033,8 +3135,10 @@ class _$SpotubeFullTrackObjectImpl implements _SpotubeFullTrackObject { required this.album, required this.durationMs, required this.isrc, - required this.explicit}) - : _artists = artists; + required this.explicit, + final String? $type}) + : _artists = artists, + $type = $type ?? 'full'; factory _$SpotubeFullTrackObjectImpl.fromJson(Map json) => _$$SpotubeFullTrackObjectImplFromJson(json); @@ -3063,9 +3167,12 @@ class _$SpotubeFullTrackObjectImpl implements _SpotubeFullTrackObject { @override final bool explicit; + @JsonKey(name: 'runtimeType') + final String $type; + @override String toString() { - return 'SpotubeFullTrackObject(id: $id, name: $name, externalUri: $externalUri, artists: $artists, album: $album, durationMs: $durationMs, isrc: $isrc, explicit: $explicit)'; + return 'SpotubeTrackObject.full(id: $id, name: $name, externalUri: $externalUri, artists: $artists, album: $album, durationMs: $durationMs, isrc: $isrc, explicit: $explicit)'; } @override @@ -3099,7 +3206,7 @@ class _$SpotubeFullTrackObjectImpl implements _SpotubeFullTrackObject { isrc, explicit); - /// Create a copy of SpotubeFullTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @@ -3108,6 +3215,122 @@ class _$SpotubeFullTrackObjectImpl implements _SpotubeFullTrackObject { get copyWith => __$$SpotubeFullTrackObjectImplCopyWithImpl< _$SpotubeFullTrackObjectImpl>(this, _$identity); + @override + @optionalTypeArgs + TResult when({ + required TResult Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit) + full, + required TResult Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album) + simple, + }) { + return full( + id, name, externalUri, artists, album, durationMs, isrc, explicit); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit)? + full, + TResult? Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album)? + simple, + }) { + return full?.call( + id, name, externalUri, artists, album, durationMs, isrc, explicit); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit)? + full, + TResult Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album)? + simple, + required TResult orElse(), + }) { + if (full != null) { + return full( + id, name, externalUri, artists, album, durationMs, isrc, explicit); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(SpotubeFullTrackObject value) full, + required TResult Function(SpotubeSimpleTrackObject value) simple, + }) { + return full(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(SpotubeFullTrackObject value)? full, + TResult? Function(SpotubeSimpleTrackObject value)? simple, + }) { + return full?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SpotubeFullTrackObject value)? full, + TResult Function(SpotubeSimpleTrackObject value)? simple, + required TResult orElse(), + }) { + if (full != null) { + return full(this); + } + return orElse(); + } + @override Map toJson() { return _$$SpotubeFullTrackObjectImplToJson( @@ -3116,8 +3339,8 @@ class _$SpotubeFullTrackObjectImpl implements _SpotubeFullTrackObject { } } -abstract class _SpotubeFullTrackObject implements SpotubeFullTrackObject { - factory _SpotubeFullTrackObject( +abstract class SpotubeFullTrackObject implements SpotubeTrackObject { + factory SpotubeFullTrackObject( {required final String id, required final String name, required final String externalUri, @@ -3127,7 +3350,7 @@ abstract class _SpotubeFullTrackObject implements SpotubeFullTrackObject { required final String isrc, required final bool explicit}) = _$SpotubeFullTrackObjectImpl; - factory _SpotubeFullTrackObject.fromJson(Map json) = + factory SpotubeFullTrackObject.fromJson(Map json) = _$SpotubeFullTrackObjectImpl.fromJson; @override @@ -3142,12 +3365,11 @@ abstract class _SpotubeFullTrackObject implements SpotubeFullTrackObject { SpotubeSimpleAlbumObject get album; @override int get durationMs; - @override String get isrc; @override bool get explicit; - /// Create a copy of SpotubeFullTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) @@ -3155,124 +3377,9 @@ abstract class _SpotubeFullTrackObject implements SpotubeFullTrackObject { get copyWith => throw _privateConstructorUsedError; } -SpotubeSimpleTrackObject _$SpotubeSimpleTrackObjectFromJson( - Map json) { - return _SpotubeSimpleTrackObject.fromJson(json); -} - -/// @nodoc -mixin _$SpotubeSimpleTrackObject { - String get id => throw _privateConstructorUsedError; - String get name => throw _privateConstructorUsedError; - String get externalUri => throw _privateConstructorUsedError; - int get durationMs => throw _privateConstructorUsedError; - bool get explicit => throw _privateConstructorUsedError; - List get artists => - throw _privateConstructorUsedError; - SpotubeSimpleAlbumObject? get album => throw _privateConstructorUsedError; - - /// Serializes this SpotubeSimpleTrackObject to a JSON map. - Map toJson() => throw _privateConstructorUsedError; - - /// Create a copy of SpotubeSimpleTrackObject - /// with the given fields replaced by the non-null parameter values. - @JsonKey(includeFromJson: false, includeToJson: false) - $SpotubeSimpleTrackObjectCopyWith get copyWith => - throw _privateConstructorUsedError; -} - -/// @nodoc -abstract class $SpotubeSimpleTrackObjectCopyWith<$Res> { - factory $SpotubeSimpleTrackObjectCopyWith(SpotubeSimpleTrackObject value, - $Res Function(SpotubeSimpleTrackObject) then) = - _$SpotubeSimpleTrackObjectCopyWithImpl<$Res, SpotubeSimpleTrackObject>; - @useResult - $Res call( - {String id, - String name, - String externalUri, - int durationMs, - bool explicit, - List artists, - SpotubeSimpleAlbumObject? album}); - - $SpotubeSimpleAlbumObjectCopyWith<$Res>? get album; -} - -/// @nodoc -class _$SpotubeSimpleTrackObjectCopyWithImpl<$Res, - $Val extends SpotubeSimpleTrackObject> - implements $SpotubeSimpleTrackObjectCopyWith<$Res> { - _$SpotubeSimpleTrackObjectCopyWithImpl(this._value, this._then); - - // ignore: unused_field - final $Val _value; - // ignore: unused_field - final $Res Function($Val) _then; - - /// Create a copy of SpotubeSimpleTrackObject - /// with the given fields replaced by the non-null parameter values. - @pragma('vm:prefer-inline') - @override - $Res call({ - Object? id = null, - Object? name = null, - Object? externalUri = null, - Object? durationMs = null, - Object? explicit = null, - Object? artists = null, - Object? album = freezed, - }) { - return _then(_value.copyWith( - id: null == id - ? _value.id - : id // ignore: cast_nullable_to_non_nullable - as String, - name: null == name - ? _value.name - : name // ignore: cast_nullable_to_non_nullable - as String, - externalUri: null == externalUri - ? _value.externalUri - : externalUri // ignore: cast_nullable_to_non_nullable - as String, - durationMs: null == durationMs - ? _value.durationMs - : durationMs // ignore: cast_nullable_to_non_nullable - as int, - explicit: null == explicit - ? _value.explicit - : explicit // ignore: cast_nullable_to_non_nullable - as bool, - artists: null == artists - ? _value.artists - : artists // ignore: cast_nullable_to_non_nullable - as List, - album: freezed == album - ? _value.album - : album // ignore: cast_nullable_to_non_nullable - as SpotubeSimpleAlbumObject?, - ) as $Val); - } - - /// Create a copy of SpotubeSimpleTrackObject - /// with the given fields replaced by the non-null parameter values. - @override - @pragma('vm:prefer-inline') - $SpotubeSimpleAlbumObjectCopyWith<$Res>? get album { - if (_value.album == null) { - return null; - } - - return $SpotubeSimpleAlbumObjectCopyWith<$Res>(_value.album!, (value) { - return _then(_value.copyWith(album: value) as $Val); - }); - } -} - /// @nodoc abstract class _$$SpotubeSimpleTrackObjectImplCopyWith<$Res> - implements $SpotubeSimpleTrackObjectCopyWith<$Res> { + implements $SpotubeTrackObjectCopyWith<$Res> { factory _$$SpotubeSimpleTrackObjectImplCopyWith( _$SpotubeSimpleTrackObjectImpl value, $Res Function(_$SpotubeSimpleTrackObjectImpl) then) = @@ -3294,7 +3401,7 @@ abstract class _$$SpotubeSimpleTrackObjectImplCopyWith<$Res> /// @nodoc class __$$SpotubeSimpleTrackObjectImplCopyWithImpl<$Res> - extends _$SpotubeSimpleTrackObjectCopyWithImpl<$Res, + extends _$SpotubeTrackObjectCopyWithImpl<$Res, _$SpotubeSimpleTrackObjectImpl> implements _$$SpotubeSimpleTrackObjectImplCopyWith<$Res> { __$$SpotubeSimpleTrackObjectImplCopyWithImpl( @@ -3302,7 +3409,7 @@ class __$$SpotubeSimpleTrackObjectImplCopyWithImpl<$Res> $Res Function(_$SpotubeSimpleTrackObjectImpl) _then) : super(_value, _then); - /// Create a copy of SpotubeSimpleTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override @@ -3350,7 +3457,7 @@ class __$$SpotubeSimpleTrackObjectImplCopyWithImpl<$Res> /// @nodoc @JsonSerializable() -class _$SpotubeSimpleTrackObjectImpl implements _SpotubeSimpleTrackObject { +class _$SpotubeSimpleTrackObjectImpl implements SpotubeSimpleTrackObject { _$SpotubeSimpleTrackObjectImpl( {required this.id, required this.name, @@ -3358,8 +3465,10 @@ class _$SpotubeSimpleTrackObjectImpl implements _SpotubeSimpleTrackObject { required this.durationMs, required this.explicit, final List artists = const [], - this.album}) - : _artists = artists; + this.album, + final String? $type}) + : _artists = artists, + $type = $type ?? 'simple'; factory _$SpotubeSimpleTrackObjectImpl.fromJson(Map json) => _$$SpotubeSimpleTrackObjectImplFromJson(json); @@ -3386,9 +3495,12 @@ class _$SpotubeSimpleTrackObjectImpl implements _SpotubeSimpleTrackObject { @override final SpotubeSimpleAlbumObject? album; + @JsonKey(name: 'runtimeType') + final String $type; + @override String toString() { - return 'SpotubeSimpleTrackObject(id: $id, name: $name, externalUri: $externalUri, durationMs: $durationMs, explicit: $explicit, artists: $artists, album: $album)'; + return 'SpotubeTrackObject.simple(id: $id, name: $name, externalUri: $externalUri, durationMs: $durationMs, explicit: $explicit, artists: $artists, album: $album)'; } @override @@ -3420,7 +3532,7 @@ class _$SpotubeSimpleTrackObjectImpl implements _SpotubeSimpleTrackObject { const DeepCollectionEquality().hash(_artists), album); - /// Create a copy of SpotubeSimpleTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @override @@ -3429,6 +3541,121 @@ class _$SpotubeSimpleTrackObjectImpl implements _SpotubeSimpleTrackObject { get copyWith => __$$SpotubeSimpleTrackObjectImplCopyWithImpl< _$SpotubeSimpleTrackObjectImpl>(this, _$identity); + @override + @optionalTypeArgs + TResult when({ + required TResult Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit) + full, + required TResult Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album) + simple, + }) { + return simple(id, name, externalUri, durationMs, explicit, artists, album); + } + + @override + @optionalTypeArgs + TResult? whenOrNull({ + TResult? Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit)? + full, + TResult? Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album)? + simple, + }) { + return simple?.call( + id, name, externalUri, durationMs, explicit, artists, album); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function( + String id, + String name, + String externalUri, + List artists, + SpotubeSimpleAlbumObject album, + int durationMs, + String isrc, + bool explicit)? + full, + TResult Function( + String id, + String name, + String externalUri, + int durationMs, + bool explicit, + List artists, + SpotubeSimpleAlbumObject? album)? + simple, + required TResult orElse(), + }) { + if (simple != null) { + return simple( + id, name, externalUri, durationMs, explicit, artists, album); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(SpotubeFullTrackObject value) full, + required TResult Function(SpotubeSimpleTrackObject value) simple, + }) { + return simple(this); + } + + @override + @optionalTypeArgs + TResult? mapOrNull({ + TResult? Function(SpotubeFullTrackObject value)? full, + TResult? Function(SpotubeSimpleTrackObject value)? simple, + }) { + return simple?.call(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(SpotubeFullTrackObject value)? full, + TResult Function(SpotubeSimpleTrackObject value)? simple, + required TResult orElse(), + }) { + if (simple != null) { + return simple(this); + } + return orElse(); + } + @override Map toJson() { return _$$SpotubeSimpleTrackObjectImplToJson( @@ -3437,8 +3664,8 @@ class _$SpotubeSimpleTrackObjectImpl implements _SpotubeSimpleTrackObject { } } -abstract class _SpotubeSimpleTrackObject implements SpotubeSimpleTrackObject { - factory _SpotubeSimpleTrackObject( +abstract class SpotubeSimpleTrackObject implements SpotubeTrackObject { + factory SpotubeSimpleTrackObject( {required final String id, required final String name, required final String externalUri, @@ -3447,7 +3674,7 @@ abstract class _SpotubeSimpleTrackObject implements SpotubeSimpleTrackObject { final List artists, final SpotubeSimpleAlbumObject? album}) = _$SpotubeSimpleTrackObjectImpl; - factory _SpotubeSimpleTrackObject.fromJson(Map json) = + factory SpotubeSimpleTrackObject.fromJson(Map json) = _$SpotubeSimpleTrackObjectImpl.fromJson; @override @@ -3465,7 +3692,7 @@ abstract class _SpotubeSimpleTrackObject implements SpotubeSimpleTrackObject { @override SpotubeSimpleAlbumObject? get album; - /// Create a copy of SpotubeSimpleTrackObject + /// Create a copy of SpotubeTrackObject /// with the given fields replaced by the non-null parameter values. @override @JsonKey(includeFromJson: false, includeToJson: false) diff --git a/lib/models/metadata/metadata.g.dart b/lib/models/metadata/metadata.g.dart index ff263819..887df6d2 100644 --- a/lib/models/metadata/metadata.g.dart +++ b/lib/models/metadata/metadata.g.dart @@ -302,6 +302,7 @@ _$SpotubeFullTrackObjectImpl _$$SpotubeFullTrackObjectImplFromJson(Map json) => durationMs: (json['durationMs'] as num).toInt(), isrc: json['isrc'] as String, explicit: json['explicit'] as bool, + $type: json['runtimeType'] as String?, ); Map _$$SpotubeFullTrackObjectImplToJson( @@ -315,6 +316,7 @@ Map _$$SpotubeFullTrackObjectImplToJson( 'durationMs': instance.durationMs, 'isrc': instance.isrc, 'explicit': instance.explicit, + 'runtimeType': instance.$type, }; _$SpotubeSimpleTrackObjectImpl _$$SpotubeSimpleTrackObjectImplFromJson( @@ -334,6 +336,7 @@ _$SpotubeSimpleTrackObjectImpl _$$SpotubeSimpleTrackObjectImplFromJson( ? null : SpotubeSimpleAlbumObject.fromJson( Map.from(json['album'] as Map)), + $type: json['runtimeType'] as String?, ); Map _$$SpotubeSimpleTrackObjectImplToJson( @@ -346,6 +349,7 @@ Map _$$SpotubeSimpleTrackObjectImplToJson( 'explicit': instance.explicit, 'artists': instance.artists.map((e) => e.toJson()).toList(), 'album': instance.album?.toJson(), + 'runtimeType': instance.$type, }; _$SpotubeUserObjectImpl _$$SpotubeUserObjectImplFromJson(Map json) => diff --git a/lib/models/metadata/track.dart b/lib/models/metadata/track.dart index f56344f3..eb82f2d8 100644 --- a/lib/models/metadata/track.dart +++ b/lib/models/metadata/track.dart @@ -1,8 +1,8 @@ part of 'metadata.dart'; @freezed -class SpotubeFullTrackObject with _$SpotubeFullTrackObject { - factory SpotubeFullTrackObject({ +class SpotubeTrackObject with _$SpotubeTrackObject { + factory SpotubeTrackObject.full({ required String id, required String name, required String externalUri, @@ -11,15 +11,9 @@ class SpotubeFullTrackObject with _$SpotubeFullTrackObject { required int durationMs, required String isrc, required bool explicit, - }) = _SpotubeFullTrackObject; + }) = SpotubeFullTrackObject; - factory SpotubeFullTrackObject.fromJson(Map json) => - _$SpotubeFullTrackObjectFromJson(json); -} - -@freezed -class SpotubeSimpleTrackObject with _$SpotubeSimpleTrackObject { - factory SpotubeSimpleTrackObject({ + factory SpotubeTrackObject.simple({ required String id, required String name, required String externalUri, @@ -27,8 +21,12 @@ class SpotubeSimpleTrackObject with _$SpotubeSimpleTrackObject { required bool explicit, @Default([]) List artists, SpotubeSimpleAlbumObject? album, - }) = _SpotubeSimpleTrackObject; + }) = SpotubeSimpleTrackObject; - factory SpotubeSimpleTrackObject.fromJson(Map json) => - _$SpotubeSimpleTrackObjectFromJson(json); + factory SpotubeTrackObject.fromJson(Map json) => + _$SpotubeTrackObjectFromJson( + json.containsKey("isrc") + ? {...json, "runtimeType": "full"} + : {...json, "runtimeType": "simple"}, + ); } diff --git a/lib/provider/metadata_plugin/library/tracks.dart b/lib/provider/metadata_plugin/library/tracks.dart new file mode 100644 index 00000000..9645deda --- /dev/null +++ b/lib/provider/metadata_plugin/library/tracks.dart @@ -0,0 +1,71 @@ +import 'package:hooks_riverpod/hooks_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/user.dart'; +import 'package:spotube/provider/metadata_plugin/utils/common.dart'; +import 'package:spotube/provider/metadata_plugin/utils/paginated.dart'; + +class MetadataPluginSavedTracksNotifier + extends AutoDisposePaginatedAsyncNotifier { + MetadataPluginSavedTracksNotifier() : super(); + + @override + fetch(offset, limit) async { + final user = await ref.read(metadataPluginUserProvider.future); + + if (user == null) { + throw Exception( + 'User not found \n' + 'You need to be logged in to access saved tracks.', + ); + } + + final tracks = await (await metadataPlugin).album.tracks( + user.id, + offset: offset, + limit: limit, + ); + + return tracks; + } + + @override + build() async { + ref.cacheFor(); + + ref.watch(metadataPluginProvider); + return await fetch(0, 20); + } + + Future addFavorite(List tracks) async { + await (await metadataPlugin).track.save(tracks.map((e) => e.id).toList()); + + for (final track in tracks) { + ref.invalidate(metadataPluginIsSavedTrackProvider(track.id)); + } + } + + Future removeFavorite(List tracks) async { + await (await metadataPlugin).track.unsave(tracks.map((e) => e.id).toList()); + + for (final track in tracks) { + ref.invalidate(metadataPluginIsSavedTrackProvider(track.id)); + } + } +} + +final metadataPluginSavedTracksProvider = AutoDisposeAsyncNotifierProvider< + MetadataPluginSavedTracksNotifier, + SpotubePaginationResponseObject>( + () => MetadataPluginSavedTracksNotifier(), +); + +final metadataPluginIsSavedTrackProvider = + FutureProvider.autoDispose.family( + (ref, trackId) async { + final metadataPlugin = await ref.watch(metadataPluginProvider.future); + + return metadataPlugin!.user + .isSavedTracks([trackId]).then((value) => value.first); + }, +); diff --git a/lib/services/metadata/endpoints/track.dart b/lib/services/metadata/endpoints/track.dart index e69de29b..ce509a82 100644 --- a/lib/services/metadata/endpoints/track.dart +++ b/lib/services/metadata/endpoints/track.dart @@ -0,0 +1,29 @@ +import 'package:hetu_script/hetu_script.dart'; +import 'package:hetu_script/values.dart'; +import 'package:spotube/models/metadata/metadata.dart'; + +class MetadataPluginTrackEndpoint { + final Hetu hetu; + MetadataPluginTrackEndpoint(this.hetu); + + HTInstance get hetuMetadataTrack => + (hetu.fetch("metadataPlugin") as HTInstance).memberGet("track") + as HTInstance; + + Future getTrack(String id) async { + final raw = + await hetuMetadataTrack.invoke("getTrack", positionalArgs: [id]) as Map; + + return SpotubeFullTrackObject.fromJson( + raw.cast(), + ); + } + + Future save(List ids) async { + await hetuMetadataTrack.invoke("save", positionalArgs: [ids]); + } + + Future unsave(List ids) async { + await hetuMetadataTrack.invoke("unsave", positionalArgs: [ids]); + } +} diff --git a/lib/services/metadata/metadata.dart b/lib/services/metadata/metadata.dart index 1d1eaf7d..98dad35a 100644 --- a/lib/services/metadata/metadata.dart +++ b/lib/services/metadata/metadata.dart @@ -17,6 +17,7 @@ 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/search.dart'; +import 'package:spotube/services/metadata/endpoints/track.dart'; import 'package:spotube/services/metadata/endpoints/user.dart'; const defaultMetadataLimit = "20"; @@ -83,6 +84,7 @@ class MetadataPlugin { late final MetadataPluginBrowseEndpoint browse; late final MetadataPluginSearchEndpoint search; late final MetadataPluginPlaylistEndpoint playlist; + late final MetadataPluginTrackEndpoint track; late final MetadataPluginUserEndpoint user; MetadataPlugin._(this.hetu) { @@ -93,6 +95,7 @@ class MetadataPlugin { browse = MetadataPluginBrowseEndpoint(hetu); search = MetadataPluginSearchEndpoint(hetu); playlist = MetadataPluginPlaylistEndpoint(hetu); + track = MetadataPluginTrackEndpoint(hetu); user = MetadataPluginUserEndpoint(hetu); } }