Skip to content

Commit

Permalink
feat: improved caching based on riverpod (#1343)
Browse files Browse the repository at this point in the history
* feat: add riverpod based favorite album provider

* feat: add album is saved, new releases and tracks providers

* feat: add artist related providers

* feat: add all categories providers

* feat: add lyrics provider

* feat: add playlist related providers

* feat: add search provider

* feat: add view and spotify friends provider

* feat: add playlist create and update and favorite handlers

* feat: use providers in home screen

* chore: fix dart lint issues

* feat: use new providers for playlist and albums screen

* feat: use providers in artist page

* feat: use providers on library page

* feat: use provider for playlist and album card and heart button

* feat: use provider in search page

* feat: use providers in generate playlist

* feat: use provider in lyrics screen

* feat: use provider for create playlist

* feat: use provider in add track dialog

* feat: use providers in remaining pages and remove fl_query

* fix: remove direct access to provider.value

* fix: glitching when loading

* fix: user album loading next page indicator

* feat: make many provider autoDispose after 5 minutes of no usage

* fix: ignore episodes in tracks
  • Loading branch information
KRTirtho committed Mar 20, 2024
1 parent 35e9920 commit 6673e5a
Show file tree
Hide file tree
Showing 193 changed files with 3,856 additions and 2,948 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"cmake.configureOnOpen": false,
"cSpell.words": [
"acousticness",
"Buildless",
"danceability",
"instrumentalness",
"Mpris",
Expand Down
170 changes: 170 additions & 0 deletions .vscode/snippets.code-snippets
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
{
"PaginatedState": {
"scope": "dart",
"prefix": "paginatedState",
"description": "Generate a PaginatedState",
"body": [
"class ${1:Model}State extends PaginatedState<${2:Model}> {",
" ${1:Model}State({",
" required super.items,",
" required super.offset,",
" required super.limit,",
" required super.hasMore,",
" });",
" ",
" @override",
" ${1:Model}State copyWith({",
" List<${2:Model}>? items,",
" int? offset,",
" int? limit,",
" bool? hasMore,",
" }) {",
" return ${1:Model}State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" hasMore: hasMore ?? this.hasMore,",
" );",
" }",
"}"
]
},
"PaginatedAsyncNotifier": {
"scope": "dart",
"prefix": "paginatedAsyncNotifier",
"description": "Generate a PaginatedAsyncNotifier",
"body": [
"class ${1:NotifierName}Notifier extends PaginatedAsyncNotifier<${3:Item}, ${2:Model}State> {",
" ${1:NotifierName}Notifier() : super();",
" ",
" @override",
" fetch(int offset, int limit) async {",
" throw UnimplementedError();",
" }",
" ",
" @override",
" build() async {",
" throw UnimplementedError();",
" }",
"}"
]
},
"PaginaitedNotifierWithState": {
"scope": "dart",
"prefix": "paginatedNotifierWithState",
"description": "Generate a PaginatedNotifier with PaginatedState",
"body": [
"class $1State extends PaginatedState<$2> {",
" $1State({",
" required super.items,",
" required super.offset,",
" required super.limit,",
" required super.hasMore,",
" });",
" ",
" @override",
" $1State copyWith({",
" List<$2>? items,",
" int? offset,",
" int? limit,",
" bool? hasMore,",
" }) {",
" return $1State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" hasMore: hasMore ?? this.hasMore,",
" );",
" }",
"}",
" ",
"class $1Notifier",
" extends PaginatedAsyncNotifier<$2, $1State> {",
" $1Notifier() : super();",
" ",
" @override",
" fetch(int offset, int limit) async {",
" throw UnimplementedError();",
" }",
" ",
" @override",
" build() async {",
" throw UnimplementedError();",
" }",
"}",
" ",
"final ${1/(.*)/${1:/camelcase}/}Provider = AsyncNotifierProvider<$1Notifier, $1State>(",
" ()=> $1Notifier(),",
");"
]
},
"FamilyPaginatedAsyncNotifier": {
"scope": "dart",
"prefix": "familyPaginatedAsyncNotifier",
"description": "Generate a FamilyPaginatedAsyncNotifier",
"body": [
"class ${1:NotifierName}Notifier extends FamilyPaginatedAsyncNotifier<${3:Item}, ${2:Model}State, {$4:Arg}> {",
" ${1:NotifierName}Notifier() : super();",
" ",
" @override",
" fetch(arg, offset, limit) async {",
" throw UnimplementedError();",
" }",
" ",
" @override",
" build(arg) async {",
" throw UnimplementedError();",
" }",
"}"
]
},
"FamilyPaginaitedNotifierWithState": {
"scope": "dart",
"prefix": "familyPaginatedNotifierWithState",
"description": "Generate a FamilyPaginatedAsyncNotifier with PaginatedState",
"body": [
"class $1State extends PaginatedState<$2> {",
" $1State({",
" required super.items,",
" required super.offset,",
" required super.limit,",
" required super.hasMore,",
" });",
" ",
" @override",
" $1State copyWith({",
" List<$2>? items,",
" int? offset,",
" int? limit,",
" bool? hasMore,",
" }) {",
" return $1State(",
" items: items ?? this.items,",
" offset: offset ?? this.offset,",
" limit: limit ?? this.limit,",
" hasMore: hasMore ?? this.hasMore,",
" );",
" }",
"}",
" ",
"class $1Notifier",
" extends FamilyPaginatedAsyncNotifier<$2, $1State, $3> {",
" $1Notifier() : super();",
" ",
" @override",
" fetch(arg, offset, limit) async {",
" throw UnimplementedError();",
" }",
" ",
" @override",
" build(arg) async {",
" throw UnimplementedError();",
" }",
"}",
" ",
"final ${1/(.*)/${1:/camelcase}/}Provider = AsyncNotifierProviderFamily<$1Notifier, $1State, $3>(",
" ()=> $1Notifier(),",
");"
]
},
}
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ linter:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
file_names: false
avoid_renaming_method_parameters: false

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
Expand Down
4 changes: 2 additions & 2 deletions lib/collections/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart' hide Search;
import 'package:spotube/models/spotify/recommendation_seeds.dart';
import 'package:spotube/pages/album/album.dart';
import 'package:spotube/pages/getting_started/getting_started.dart';
import 'package:spotube/pages/home/genres/genre_playlists.dart';
Expand Down Expand Up @@ -96,8 +97,7 @@ final routerProvider = Provider((ref) {
path: "result",
pageBuilder: (context, state) => SpotubePage(
child: PlaylistGenerateResultPage(
state:
state.extra as PlaylistGenerateResultRouteState,
state: state.extra as GeneratePlaylistProviderInput,
),
),
),
Expand Down
27 changes: 3 additions & 24 deletions lib/components/album/album_card.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import 'package:fl_query_hooks/fl_query_hooks.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/playbutton_card.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/infinite_query.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/provider/spotify_provider.dart';
import 'package:spotube/provider/spotify/spotify.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/queries/album.dart';
import 'package:spotube/utils/service_utils.dart';
import 'package:spotube/utils/type_conversion_utils.dart';

Expand All @@ -31,15 +28,12 @@ class AlbumCard extends HookConsumerWidget {
useStream(audioPlayer.playingStream).data ?? audioPlayer.isPlaying;
final playlistNotifier = ref.watch(ProxyPlaylistNotifier.notifier);

final queryClient = useQueryClient();

bool isPlaylistPlaying = useMemoized(
() => playlist.containsCollection(album.id!),
[playlist, album.id],
);

final updating = useState(false);
final spotify = ref.watch(spotifyProvider);

final scaffoldMessenger = ScaffoldMessenger.maybeOf(context);

Expand All @@ -50,23 +44,8 @@ class AlbumCard extends HookConsumerWidget {
TypeConversionUtils.simpleTrack_X_Track(track, album))
.toList();
}
final job = AlbumQueries.tracksOfJob(album.id!);

final query = queryClient.createInfiniteQuery(
job.queryKey,
(page) => job.task(page, (spotify: spotify, album: album)),
initialPage: 0,
nextPage: job.nextPage,
);

return await query.fetchAllTracks(
getAllTracks: () async {
final res = await spotify.albums.tracks(album.id!).all();
return res
.map((e) => TypeConversionUtils.simpleTrack_X_Track(e, album))
.toList();
},
);
await ref.read(albumTracksProvider(album).future);
return ref.read(albumTracksProvider(album).notifier).fetchAll();
}

return PlaybuttonCard(
Expand Down
21 changes: 9 additions & 12 deletions lib/components/artist/artist_album_list.dart
Original file line number Diff line number Diff line change
@@ -1,38 +1,35 @@
import 'package:flutter/material.dart' hide Page;
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/services/queries/queries.dart';
import 'package:spotube/provider/spotify/spotify.dart';

class ArtistAlbumList extends HookConsumerWidget {
final String artistId;
ArtistAlbumList(
this.artistId, {
Key? key,
}) : super(key: key);
super.key,
});

final logger = getLogger(ArtistAlbumList);

@override
Widget build(BuildContext context, ref) {
final albumsQuery = useQueries.artist.albumsOf(ref, artistId);
final albumsQuery = ref.watch(artistAlbumsProvider(artistId));
final albumsQueryNotifier =
ref.watch(artistAlbumsProvider(artistId).notifier);

final albums = useMemoized(() {
return albumsQuery.pages
.expand<Album>((page) => page.items ?? const Iterable.empty())
.toList();
}, [albumsQuery.pages]);
final albums = albumsQuery.asData?.value.items ?? [];

final theme = Theme.of(context);

return HorizontalPlaybuttonCardView<Album>(
isLoadingNextPage: albumsQuery.isLoadingNextPage,
hasNextPage: albumsQuery.hasNextPage,
hasNextPage: albumsQuery.asData?.value.hasMore ?? false,
items: albums,
onFetchMore: albumsQuery.fetchNext,
onFetchMore: albumsQueryNotifier.fetchMore,
title: Text(
context.l10n.albums,
style: theme.textTheme.headlineSmall,
Expand Down
2 changes: 1 addition & 1 deletion lib/components/artist/artist_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import 'package:spotube/utils/type_conversion_utils.dart';

class ArtistCard extends HookConsumerWidget {
final Artist artist;
const ArtistCard(this.artist, {Key? key}) : super(key: key);
const ArtistCard(this.artist, {super.key});

@override
Widget build(BuildContext context, ref) {
Expand Down
4 changes: 2 additions & 2 deletions lib/components/desktop_login/login_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import 'package:spotube/provider/authentication_provider.dart';
class TokenLoginForm extends HookConsumerWidget {
final void Function()? onDone;
const TokenLoginForm({
Key? key,
super.key,
this.onDone,
}) : super(key: key);
});

@override
Widget build(BuildContext context, ref) {
Expand Down
27 changes: 10 additions & 17 deletions lib/components/home/sections/featured.dart
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
import 'package:flutter/material.dart' hide Page;
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:spotify/spotify.dart';
import 'package:spotube/components/shared/horizontal_playbutton_card_view/horizontal_playbutton_card_view.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/services/queries/queries.dart';
import 'package:spotube/provider/spotify/spotify.dart';

class HomeFeaturedSection extends HookConsumerWidget {
const HomeFeaturedSection({Key? key}) : super(key: key);
const HomeFeaturedSection({super.key});

@override
Widget build(BuildContext context, ref) {
final featuredPlaylistsQuery = useQueries.playlist.featured(ref);
final playlists = useMemoized(
() => featuredPlaylistsQuery.pages
.whereType<Page<PlaylistSimple>>()
.expand((page) => page.items ?? const <PlaylistSimple>[]),
[featuredPlaylistsQuery.pages],
);
final isLoadingFeaturedPlaylists = !featuredPlaylistsQuery.hasPageData &&
!featuredPlaylistsQuery.isLoadingNextPage;
final featuredPlaylists = ref.watch(featuredPlaylistsProvider);
final featuredPlaylistsNotifier =
ref.watch(featuredPlaylistsProvider.notifier);

return Skeletonizer(
enabled: isLoadingFeaturedPlaylists,
enabled: featuredPlaylists.isLoading,
child: HorizontalPlaybuttonCardView<PlaylistSimple>(
items: playlists.toList(),
items: featuredPlaylists.asData?.value.items ?? [],
title: Text(context.l10n.featured),
isLoadingNextPage: featuredPlaylistsQuery.isLoadingNextPage,
hasNextPage: featuredPlaylistsQuery.hasNextPage,
onFetchMore: featuredPlaylistsQuery.fetchNext,
isLoadingNextPage: featuredPlaylists.isLoadingNextPage,
hasNextPage: featuredPlaylists.asData?.value.hasMore ?? false,
onFetchMore: featuredPlaylistsNotifier.fetchMore,
),
);
}
Expand Down

0 comments on commit 6673e5a

Please sign in to comment.