import 'dart:async'; import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotify/spotify.dart'; import 'package:spotube/provider/history/state.dart'; import 'package:spotube/provider/spotify_provider.dart'; import 'package:spotube/utils/persisted_state_notifier.dart'; class PlaybackHistoryState { final List items; const PlaybackHistoryState({this.items = const []}); factory PlaybackHistoryState.fromJson(Map json) { return PlaybackHistoryState( items: json["items"] ?.map( (json) => PlaybackHistoryItem.fromJson(json), ) .toList() .cast() ?? [], ); } Map toJson() { return { "items": items.map((s) => s.toJson()).toList(), }; } PlaybackHistoryState copyWith({ List? items, }) { return PlaybackHistoryState(items: items ?? this.items); } } class PlaybackHistoryNotifier extends PersistedStateNotifier { final Ref ref; PlaybackHistoryNotifier(this.ref) : super(const PlaybackHistoryState(), "playback_history"); SpotifyApi get spotify => ref.read(spotifyProvider); @override FutureOr fromJson(Map json) => PlaybackHistoryState.fromJson(json); @override Map toJson() { return state.toJson(); } void addPlaylists(List playlists) { state = state.copyWith( items: [ ...state.items, for (final playlist in playlists) PlaybackHistoryItem.playlist( date: DateTime.now(), playlist: playlist), ], ); } void addAlbums(List albums) { state = state.copyWith( items: [ ...state.items, for (final album in albums) PlaybackHistoryItem.album(date: DateTime.now(), album: album), ], ); } void addTrack(Track track) async { // For some reason Track's artists images are `null` // so we need to fetch them from the API final artists = await spotify.artists.list(track.artists!.map((e) => e.id!).toList()); track.artists = artists.toList(); state = state.copyWith( items: [ ...state.items, PlaybackHistoryItem.track(date: DateTime.now(), track: track), ], ); } void clear() { state = state.copyWith(items: []); } } final playbackHistoryProvider = StateNotifierProvider( (ref) => PlaybackHistoryNotifier(ref), ); typedef PlaybackHistoryGrouped = ({ List tracks, List albums, List playlists, }); final playbackHistoryGroupedProvider = Provider((ref) { final history = ref.watch(playbackHistoryProvider); final tracks = history.items .whereType() .sorted((a, b) => b.date.compareTo(a.date)) .toList(); final albums = history.items .whereType() .sorted((a, b) => b.date.compareTo(a.date)) .toList(); final playlists = history.items .whereType() .sorted((a, b) => b.date.compareTo(a.date)) .toList(); return ( tracks: tracks, albums: albums, playlists: playlists, ); });