fix: categories not showing for oauth exception, maximized window size is stored

feat: UserLibrary and UserAlbums using fl_query
This commit is contained in:
Kingkor Roy Tirtho 2022-10-10 22:54:10 +06:00
parent 3e498a4827
commit 4df917e65e
8 changed files with 88 additions and 65 deletions

View File

@ -1,3 +1,4 @@
import 'package:fl_query/fl_query.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
@ -120,7 +121,9 @@ class AlbumView extends HookConsumerWidget {
album.id!, album.id!,
), ),
); );
ref.refresh(currentUserAlbumsQuery); QueryBowl.of(context).refetchQueries(
[currentUserAlbumsQueryJob.queryKey],
);
}); });
}, },
); );

View File

@ -1,34 +1,39 @@
import 'package:fl_query_hooks/fl_query_hooks.dart';
import 'package:flutter/material.dart' hide Image; import 'package:flutter/material.dart' hide Image;
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/components/Album/AlbumCard.dart'; import 'package:spotube/components/Album/AlbumCard.dart';
import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart';
import 'package:spotube/provider/SpotifyDI.dart';
import 'package:spotube/provider/SpotifyRequests.dart'; import 'package:spotube/provider/SpotifyRequests.dart';
import 'package:spotube/utils/type_conversion_utils.dart'; import 'package:spotube/utils/type_conversion_utils.dart';
class UserAlbums extends ConsumerWidget { class UserAlbums extends HookConsumerWidget {
const UserAlbums({Key? key}) : super(key: key); const UserAlbums({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final albums = ref.watch(currentUserAlbumsQuery); final albumsQuery = useQuery(
job: currentUserAlbumsQueryJob,
externalData: ref.watch(spotifyProvider),
);
return albums.when( if (albumsQuery.isLoading || !albumsQuery.hasData) {
data: (data) => SingleChildScrollView( return const Center(child: ShimmerPlaybuttonCard(count: 7));
child: Padding( }
padding: const EdgeInsets.all(8.0),
child: Wrap( return SingleChildScrollView(
spacing: 20, // gap between adjacent chips child: Padding(
runSpacing: 20, // gap between lines padding: const EdgeInsets.all(8.0),
alignment: WrapAlignment.center, child: Wrap(
children: data spacing: 20, // gap between adjacent chips
.map((album) => runSpacing: 20, // gap between lines
AlbumCard(TypeConversionUtils.simpleAlbum_X_Album(album))) alignment: WrapAlignment.center,
.toList(), children: albumsQuery.data!
), .map((album) =>
AlbumCard(TypeConversionUtils.simpleAlbum_X_Album(album)))
.toList(),
), ),
), ),
loading: () => const Center(child: ShimmerPlaybuttonCard(count: 7)),
error: (_, __) => const Text("Failure is the pillar of success"),
); );
} }
} }

View File

@ -1,49 +1,54 @@
import 'package:fl_query_hooks/fl_query_hooks.dart';
import 'package:flutter/material.dart' hide Image; import 'package:flutter/material.dart' hide Image;
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart'; import 'package:spotube/components/LoaderShimmers/ShimmerPlaybuttonCard.dart';
import 'package:spotube/components/Playlist/PlaylistCard.dart'; import 'package:spotube/components/Playlist/PlaylistCard.dart';
import 'package:spotube/components/Playlist/PlaylistCreateDialog.dart'; import 'package:spotube/components/Playlist/PlaylistCreateDialog.dart';
import 'package:spotube/provider/SpotifyDI.dart';
import 'package:spotube/provider/SpotifyRequests.dart'; import 'package:spotube/provider/SpotifyRequests.dart';
class UserPlaylists extends ConsumerWidget { class UserPlaylists extends HookConsumerWidget {
const UserPlaylists({Key? key}) : super(key: key); const UserPlaylists({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final playlists = ref.watch(currentUserPlaylistsQuery); final playlistsQuery = useQuery(
job: currentUserPlaylistsQueryJob,
externalData: ref.watch(spotifyProvider),
);
Image image = Image();
image.height = 300;
image.width = 300;
PlaylistSimple likedTracksPlaylist = PlaylistSimple();
likedTracksPlaylist.name = "Liked Tracks";
likedTracksPlaylist.type = "playlist";
likedTracksPlaylist.collaborative = false;
likedTracksPlaylist.public = false;
likedTracksPlaylist.id = "user-liked-tracks";
image.url = "https://t.scdn.co/images/3099b3803ad9496896c43f22fe9be8c4.png";
likedTracksPlaylist.images = [image];
return playlists.when( if (playlistsQuery.isLoading || !playlistsQuery.hasData) {
loading: () => const Center(child: ShimmerPlaybuttonCard(count: 7)), return const Center(child: ShimmerPlaybuttonCard(count: 7));
data: (data) { }
Image image = Image();
image.height = 300; return SingleChildScrollView(
image.width = 300; child: Padding(
PlaylistSimple likedTracksPlaylist = PlaylistSimple(); padding: const EdgeInsets.all(8.0),
likedTracksPlaylist.name = "Liked Tracks"; child: Wrap(
likedTracksPlaylist.type = "playlist"; spacing: 20, // gap between adjacent chips
likedTracksPlaylist.collaborative = false; runSpacing: 20, // gap between lines
likedTracksPlaylist.public = false; alignment: WrapAlignment.center,
likedTracksPlaylist.id = "user-liked-tracks"; children: [
image.url = const PlaylistCreateDialog(),
"https://t.scdn.co/images/3099b3803ad9496896c43f22fe9be8c4.png"; PlaylistCard(likedTracksPlaylist),
likedTracksPlaylist.images = [image]; ...playlistsQuery.data!
return SingleChildScrollView( .map((playlist) => PlaylistCard(playlist))
child: Padding( .toList(),
padding: const EdgeInsets.all(8.0), ],
child: Wrap( ),
spacing: 20, // gap between adjacent chips ),
runSpacing: 20, // gap between lines );
alignment: WrapAlignment.center,
children: [
const PlaylistCreateDialog(),
PlaylistCard(likedTracksPlaylist),
...data.map((playlist) => PlaylistCard(playlist)).toList(),
],
),
),
);
},
error: (_, __) => const Text("Failure is the pillar of success"));
} }
} }

View File

@ -1,3 +1,4 @@
import 'package:fl_query/fl_query.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -51,7 +52,9 @@ class PlaylistCreateDialog extends HookConsumerWidget {
description: description.text, description: description.text,
) )
.then((_) { .then((_) {
ref.refresh(currentUserPlaylistsQuery); QueryBowl.of(context).refetchQueries([
currentUserPlaylistsQueryJob.queryKey,
]);
Navigator.pop(context); Navigator.pop(context);
}); });
}, },

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:fl_query/fl_query.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -146,7 +147,9 @@ class PlaylistView extends HookConsumerWidget {
logger.e("FollowButton.onPressed", e, stack); logger.e("FollowButton.onPressed", e, stack);
} finally { } finally {
ref.refresh(query); ref.refresh(query);
ref.refresh(currentUserPlaylistsQuery); QueryBowl.of(context).refetchQueries([
currentUserPlaylistsQueryJob.queryKey,
]);
} }
}, },
); );

View File

@ -170,6 +170,7 @@ class _SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
prevSize?.height == appWindow.size.height; prevSize?.height == appWindow.size.height;
if (localStorage == null || windowSameDimension || kIsMobile) return; if (localStorage == null || windowSameDimension || kIsMobile) return;
if (appWindow.isMaximized) return;
localStorage!.setString( localStorage!.setString(
LocalStorageKeys.windowSizeInfo, LocalStorageKeys.windowSizeInfo,
jsonEncode({ jsonEncode({

View File

@ -95,16 +95,18 @@ class Auth extends PersistedChangeNotifier {
} }
@override @override
FutureOr<void> loadFromLocal(Map<String, dynamic> map) { FutureOr<void> loadFromLocal(Map<String, dynamic> map) async {
_accessToken = map["accessToken"]; _accessToken = map["accessToken"];
_expiration = map["expiration"] != null _expiration = map["expiration"] != null
? DateTime.tryParse(map["expiration"]) ? DateTime.tryParse(map["expiration"])
: _expiration; : _expiration;
_authCookie = map["authCookie"]; _authCookie = map["authCookie"];
_restartRefresher();
if (isExpired) { if (isExpired) {
refresh(); final data = await ServiceUtils.getAccessToken(authCookie!);
_accessToken = data.accessToken;
_expiration = data.expiration;
} }
_restartRefresher();
} }
@override @override

View File

@ -45,16 +45,17 @@ final categoryPlaylistsQueryJob =
}, },
); );
final currentUserPlaylistsQuery = FutureProvider<Iterable<PlaylistSimple>>( final currentUserPlaylistsQueryJob =
(ref) { QueryJob<Iterable<PlaylistSimple>, SpotifyApi>(
final spotify = ref.watch(spotifyProvider); queryKey: "current-user-query",
task: (_, spotify) {
return spotify.playlists.me.all(); return spotify.playlists.me.all();
}, },
); );
final currentUserAlbumsQuery = FutureProvider<Iterable<AlbumSimple>>( final currentUserAlbumsQueryJob = QueryJob<Iterable<AlbumSimple>, SpotifyApi>(
(ref) { queryKey: "current-user-albums",
final spotify = ref.watch(spotifyProvider); task: (_, spotify) {
return spotify.me.savedAlbums().all(); return spotify.me.savedAlbums().all();
}, },
); );