From 0d434fafccb13f873146c31c195c79189e276c02 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 9 Jan 2021 16:34:24 +0000 Subject: [PATCH] Add new useRefreshable hook --- lib/hooks/refreshable.dart | 37 ++++++++++++++++++++++++++++++++ lib/pages/communities_tab.dart | 39 +++++++++++++++++----------------- lib/pages/full_post.dart | 35 +++++++++++++----------------- 3 files changed, 71 insertions(+), 40 deletions(-) create mode 100644 lib/hooks/refreshable.dart diff --git a/lib/hooks/refreshable.dart b/lib/hooks/refreshable.dart new file mode 100644 index 0000000..196da2f --- /dev/null +++ b/lib/hooks/refreshable.dart @@ -0,0 +1,37 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +import 'memo_future.dart'; + +class Refreshable { + const Refreshable({@required this.snapshot, @required this.refresh}) + : assert(snapshot != null), + assert(refresh != null); + + final AsyncSnapshot snapshot; + final AsyncCallback refresh; +} + +Refreshable useRefreshable(AsyncValueGetter fetcher, + [List keys = const []]) { + final newData = useState(null); + final snapshot = useMemoFuture(() async { + newData.value = null; + return fetcher(); + }, keys); + + final outSnapshot = () { + if (newData.value != null) { + return AsyncSnapshot.withData(ConnectionState.done, newData.value); + } + return snapshot; + }(); + + return Refreshable( + snapshot: outSnapshot, + refresh: () async { + newData.value = await fetcher(); + }, + ); +} diff --git a/lib/pages/communities_tab.dart b/lib/pages/communities_tab.dart index 39f98eb..7915fbf 100644 --- a/lib/pages/communities_tab.dart +++ b/lib/pages/communities_tab.dart @@ -8,7 +8,7 @@ import 'package:fuzzy/fuzzy.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; import '../hooks/delayed_loading.dart'; -import '../hooks/memo_future.dart'; +import '../hooks/refreshable.dart'; import '../hooks/stores.dart'; import '../util/extensions/api.dart'; import '../util/extensions/iterators.dart'; @@ -60,14 +60,11 @@ class CommunitiesTab extends HookWidget { } // TODO: rebuild when instances/accounts change - final instancesSnap = useMemoFuture(getInstances); - final communitiesSnap = useMemoFuture(getCommunities); + final instancesRefreshable = useRefreshable(getInstances); + final communitiesRefreshable = useRefreshable(getCommunities); - final updatedCommunities = - useState>>(null); - final updatedInstances = useState>(null); - - if (communitiesSnap.hasError || instancesSnap.hasError) { + if (communitiesRefreshable.snapshot.hasError || + instancesRefreshable.snapshot.hasError) { return Scaffold( appBar: AppBar(), body: Center( @@ -77,15 +74,16 @@ class CommunitiesTab extends HookWidget { Padding( padding: const EdgeInsets.all(8), child: Text( - communitiesSnap.error?.toString() ?? - instancesSnap.error?.toString(), + communitiesRefreshable.snapshot.error?.toString() ?? + instancesRefreshable.snapshot.error?.toString(), ), ) ], ), ), ); - } else if (!communitiesSnap.hasData || !instancesSnap.hasData) { + } else if (!communitiesRefreshable.snapshot.hasData || + !instancesRefreshable.snapshot.hasData) { return Scaffold( appBar: AppBar(), body: const Center( @@ -97,11 +95,10 @@ class CommunitiesTab extends HookWidget { refresh() async { await HapticFeedback.mediumImpact(); try { - final i = getInstances(); - final c = getCommunities(); - await Future.wait([i, c]); - updatedInstances.value = await i; - updatedCommunities.value = await c; + await Future.wait([ + instancesRefreshable.refresh(), + communitiesRefreshable.refresh(), + ]); // ignore: avoid_catches_without_on_clauses } catch (e) { Scaffold.of(context) @@ -109,8 +106,8 @@ class CommunitiesTab extends HookWidget { } } - final instances = updatedInstances.value ?? instancesSnap.data; - final communities = updatedCommunities.value ?? communitiesSnap.data + final instances = instancesRefreshable.snapshot.data; + final communities = communitiesRefreshable.snapshot.data ..forEach( (e) => e.sort((a, b) => a.communityName.compareTo(b.communityName))); @@ -239,6 +236,7 @@ class CommunitiesTab extends HookWidget { ], ), trailing: _CommunitySubscribeToggle( + key: ValueKey(comm.communityId), instanceHost: comm.instanceHost, communityId: comm.communityId, ), @@ -258,9 +256,10 @@ class _CommunitySubscribeToggle extends HookWidget { final String instanceHost; const _CommunitySubscribeToggle( - {@required this.instanceHost, @required this.communityId}) + {@required this.instanceHost, @required this.communityId, Key key}) : assert(instanceHost != null), - assert(communityId != null); + assert(communityId != null), + super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/pages/full_post.dart b/lib/pages/full_post.dart index 8e93902..4478421 100644 --- a/lib/pages/full_post.dart +++ b/lib/pages/full_post.dart @@ -6,7 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:lemmy_api_client/lemmy_api_client.dart'; import '../hooks/logged_in_action.dart'; -import '../hooks/memo_future.dart'; +import '../hooks/refreshable.dart'; import '../hooks/stores.dart'; import '../util/more_icon.dart'; import '../widgets/comment_section.dart'; @@ -19,10 +19,8 @@ class FullPostPage extends HookWidget { final int id; final String instanceHost; final PostView post; - final GlobalKey _refreshIndicatorKey = - GlobalKey(); - FullPostPage({@required this.id, @required this.instanceHost}) + const FullPostPage({@required this.id, @required this.instanceHost}) : assert(id != null), assert(instanceHost != null), post = null; @@ -33,23 +31,22 @@ class FullPostPage extends HookWidget { @override Widget build(BuildContext context) { final accStore = useAccountsStore(); - final fullPostSnap = useMemoFuture(() => LemmyApi(instanceHost) + final fullPostRefreshable = useRefreshable(() => LemmyApi(instanceHost) .v1 .getPost(id: id, auth: accStore.defaultTokenFor(instanceHost)?.raw)); final loggedInAction = useLoggedInAction(instanceHost); final newComments = useState(const []); - final updatedPost = useState(null); - // FALLBACK VIEW - if (!fullPostSnap.hasData && this.post == null) { + // FALLBACK VIEW + if (!fullPostRefreshable.snapshot.hasData && this.post == null) { return Scaffold( appBar: AppBar(), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (fullPostSnap.hasError) - Text(fullPostSnap.error.toString()) + if (fullPostRefreshable.snapshot.hasError) + Text(fullPostRefreshable.snapshot.error.toString()) else const CircularProgressIndicator(), ], @@ -60,10 +57,11 @@ class FullPostPage extends HookWidget { // VARIABLES - final post = updatedPost.value?.post ?? - (fullPostSnap.hasData ? fullPostSnap.data.post : this.post); + final post = fullPostRefreshable.snapshot.hasData + ? fullPostRefreshable.snapshot.data.post + : this.post; - final fullPost = updatedPost.value ?? fullPostSnap.data; + final fullPost = fullPostRefreshable.snapshot.data; // FUNCTIONS @@ -71,9 +69,7 @@ class FullPostPage extends HookWidget { await HapticFeedback.mediumImpact(); try { - return await LemmyApi(instanceHost) - .v1 - .getPost(id: id, auth: accStore.defaultTokenFor(instanceHost)?.raw); + await fullPostRefreshable.refresh(); // ignore: avoid_catches_without_on_clauses } catch (e) { Scaffold.of(context).showSnackBar(SnackBar( @@ -110,23 +106,22 @@ class FullPostPage extends HookWidget { child: const Icon(Icons.comment)), body: RefreshIndicator( onRefresh: refresh, - key: _refreshIndicatorKey, child: ListView( physics: const AlwaysScrollableScrollPhysics(), children: [ Post(post, fullPost: true), - if (fullPostSnap.hasData) + if (fullPostRefreshable.snapshot.hasData) CommentSection( newComments.value.followedBy(fullPost.comments).toList(), postCreatorId: fullPost.post.creatorId) - else if (fullPostSnap.hasError) + else if (fullPostRefreshable.snapshot.hasError) Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 30), child: Column( children: [ const Icon(Icons.error), - Text('Error: ${fullPostSnap.error}') + Text('Error: ${fullPostRefreshable.snapshot.error}') ], ), )