From 46a2f48e3a5e88b782ae886ad69e1e3e3c98acf9 Mon Sep 17 00:00:00 2001 From: krawieck Date: Fri, 1 Oct 2021 23:03:42 +0200 Subject: [PATCH] transition comment section to use mobx --- lib/comment_tree.dart | 19 ++- lib/pages/full_post/full_post.dart | 36 +---- lib/widgets/comment_section.dart | 179 +++++++++++++----------- lib/widgets/post/full_post_store.dart | 29 +++- lib/widgets/post/full_post_store.g.dart | 47 ++++++- lib/widgets/post/post.dart | 2 + 6 files changed, 181 insertions(+), 131 deletions(-) diff --git a/lib/comment_tree.dart b/lib/comment_tree.dart index fe0e49c..6f6942c 100644 --- a/lib/comment_tree.dart +++ b/lib/comment_tree.dart @@ -39,6 +39,15 @@ extension on CommentSortType { } } +extension SortCommentTreeList on List { + void sortBy(CommentSortType sortType) { + sort(sortType.sortFunction); + for (final el in this) { + el._sort(sortType.sortFunction); + } + } +} + class CommentTree { CommentView comment; List children = []; @@ -71,14 +80,4 @@ class CommentTree { el._sort(compare); } } - - /// Sorts in-place a list of CommentTrees according to a given sortType - static List sortList( - CommentSortType sortType, List comms) { - comms.sort(sortType.sortFunction); - for (final el in comms) { - el._sort(sortType.sortFunction); - } - return comms; - } } diff --git a/lib/pages/full_post/full_post.dart b/lib/pages/full_post/full_post.dart index 36a7c0e..573b156 100644 --- a/lib/pages/full_post/full_post.dart +++ b/lib/pages/full_post/full_post.dart @@ -170,7 +170,7 @@ class _FullPostPage extends HookWidget { children: [ const SizedBox(height: 15), PostTile.fromPostStore(store.postStore!), - const _Comments(), + const CommentSection(), ], ), )); @@ -179,40 +179,6 @@ class _FullPostPage extends HookWidget { } } -class _Comments extends StatelessWidget { - const _Comments(); - - // TODO: comments rebuild every refresh, even when they don't change. FIX THAT! - - @override - Widget build(BuildContext context) { - return ObserverBuilder( - builder: (context, store) { - final fullPost = store.fullPostView; - if (fullPost != null) { - return CommentSection(store.comments!, - postCreatorId: fullPost.postView.creator.id); - } else if (store.fullPostState.errorTerm != null) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 30), - child: FailedToLoad( - message: 'Comments failed to load', - refresh: () => store.refresh(context - .read() - .defaultUserDataFor(store.instanceHost) - ?.jwt)), - ); - } else { - return const Padding( - padding: EdgeInsets.only(top: 40), - child: Center(child: CircularProgressIndicator()), - ); - } - }, - ); - } -} - class FailedToLoad extends StatelessWidget { final String message; final VoidCallback refresh; diff --git a/lib/widgets/comment_section.dart b/lib/widgets/comment_section.dart index 1e87ff3..47ef70b 100644 --- a/lib/widgets/comment_section.dart +++ b/lib/widgets/comment_section.dart @@ -1,20 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:lemmy_api_client/v3.dart'; +import 'package:provider/provider.dart'; import '../comment_tree.dart'; import '../l10n/l10n.dart'; +import '../pages/full_post/full_post.dart'; +import '../stores/accounts_store.dart'; +import '../util/observer_consumers.dart'; import 'bottom_modal.dart'; import 'bottom_safe.dart'; import 'comment/comment.dart'; +import 'post/full_post_store.dart'; /// Manages comments section, sorts them -class CommentSection extends HookWidget { - final List rawComments; - final List comments; - final int postCreatorId; - final CommentSortType sortType; - +class CommentSection extends StatelessWidget { static const sortPairs = { CommentSortType.hot: [Icons.whatshot, L10nStrings.hot], CommentSortType.new_: [Icons.new_releases, L10nStrings.new_], @@ -23,89 +21,108 @@ class CommentSection extends HookWidget { CommentSortType.chat: [Icons.chat, L10nStrings.chat], }; - CommentSection( - List rawComments, { - required this.postCreatorId, - this.sortType = CommentSortType.hot, - }) : comments = - CommentTree.sortList(sortType, CommentTree.fromList(rawComments)), - rawComments = rawComments - ..sort((b, a) => a.comment.published.compareTo(b.comment.published)); + const CommentSection({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - final sorting = useState(sortType); + return ObserverBuilder( + builder: (context, store) { + // error & spinner handling + if (store.fullPostView == null) { + if (store.fullPostState.errorTerm != null) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 30), + child: FailedToLoad( + message: 'Comments failed to load', + refresh: () => store.refresh(context + .read() + .defaultUserDataFor(store.instanceHost) + ?.jwt)), + ); + } else { + return const Padding( + padding: EdgeInsets.only(top: 40), + child: Center(child: CircularProgressIndicator()), + ); + } + } - void sortComments(CommentSortType sort) { - if (sort != sorting.value && sort != CommentSortType.chat) { - CommentTree.sortList(sort, comments); - } - - sorting.value = sort; - } - - return Column(children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), - child: Row( + return Column( children: [ - OutlinedButton( - onPressed: () { - showBottomModal( - title: 'sort by', - context: context, - builder: (context) => Column( - children: [ - for (final e in sortPairs.entries) - ListTile( - leading: Icon(e.value[0] as IconData), - title: Text((e.value[1] as String).tr(context)), - trailing: sorting.value == e.key - ? const Icon(Icons.check) - : null, - onTap: () { - Navigator.of(context).pop(); - sortComments(e.key); - }, - ) - ], - ), - ); - }, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), child: Row( children: [ - Text((sortPairs[sorting.value]![1] as String).tr(context)), - const Icon(Icons.arrow_drop_down), + OutlinedButton( + onPressed: () { + showBottomModal( + title: 'sort by', + context: context, + builder: (context) => Column( + children: [ + for (final e in sortPairs.entries) + ListTile( + leading: Icon(e.value[0] as IconData), + title: Text((e.value[1] as String).tr(context)), + trailing: store.sorting == e.key + ? const Icon(Icons.check) + : null, + onTap: () { + Navigator.of(context).pop(); + store.updateSorting(e.key); + }, + ) + ], + ), + ); + }, + child: Row( + children: [ + Text((sortPairs[store.sorting]![1] as String) + .tr(context)), + const Icon(Icons.arrow_drop_down), + ], + ), + ), + const Spacer(), ], ), ), - const Spacer(), + // sorting menu goes here + if (store.fullPostView!.comments.isEmpty) + const Padding( + padding: EdgeInsets.symmetric(vertical: 50), + child: Text( + 'no comments yet', + style: TextStyle(fontStyle: FontStyle.italic), + ), + ) + else ...[ + for (final com in store.newComments) + CommentWidget.fromCommentView( + com, + detached: false, + key: ValueKey(com), + ), + if (store.sorting == CommentSortType.chat) + for (final com in store.fullPostView!.comments) + CommentWidget.fromCommentView( + com, + detached: false, + key: ValueKey(com), + ) + else + for (final com in store.sortedCommentTree!) + CommentWidget( + com, + key: ValueKey(com), + ), + const BottomSafe( + kMinInteractiveDimension + kFloatingActionButtonMargin), + ] ], - ), - ), - // sorting menu goes here - if (comments.isEmpty) - const Padding( - padding: EdgeInsets.symmetric(vertical: 50), - child: Text( - 'no comments yet', - style: TextStyle(fontStyle: FontStyle.italic), - ), - ) - else if (sorting.value == CommentSortType.chat) - for (final com in rawComments) - CommentWidget.fromCommentView( - com, - detached: false, - key: ValueKey(com), - ) - else - for (final com in comments) - CommentWidget( - com, - key: ValueKey(com), - ), - const BottomSafe(kMinInteractiveDimension + kFloatingActionButtonMargin), - ]); + ); + }, + ); } } diff --git a/lib/widgets/post/full_post_store.dart b/lib/widgets/post/full_post_store.dart index 089cf23..c1ef3d1 100644 --- a/lib/widgets/post/full_post_store.dart +++ b/lib/widgets/post/full_post_store.dart @@ -1,6 +1,7 @@ import 'package:lemmy_api_client/v3.dart'; import 'package:mobx/mobx.dart'; +import '../../comment_tree.dart'; import '../../util/async_store.dart'; import 'post_store.dart'; @@ -16,7 +17,27 @@ abstract class _FullPostStore with Store { FullPostView? fullPostView; @observable - List newComments = []; + ObservableList newComments = ObservableList(); + + @observable + CommentSortType sorting = CommentSortType.hot; + + @action + // ignore: use_setters_to_change_properties + void updateSorting(CommentSortType sort) { + sorting = sort; + } + + @computed + List? get commentTree { + if (fullPostView == null) return null; + return CommentTree.fromList(fullPostView!.comments); + } + + @computed + List? get sortedCommentTree { + return commentTree?..sortBy(sorting); + } @observable PostStore? postStore; @@ -68,12 +89,14 @@ abstract class _FullPostStore with Store { required this.instanceHost, }); + // ignore: unused_element _FullPostStore.fromPostView(PostView postView) : postId = postView.post.id, instanceHost = postView.instanceHost, postStore = PostStore(postView); - _FullPostStore.fromPostStore(this.postStore) - : postId = postStore!.postView.post.id, + // ignore: unused_element + _FullPostStore.fromPostStore(PostStore this.postStore) + : postId = postStore.postView.post.id, instanceHost = postStore.postView.instanceHost; } diff --git a/lib/widgets/post/full_post_store.g.dart b/lib/widgets/post/full_post_store.g.dart index 933c499..9435738 100644 --- a/lib/widgets/post/full_post_store.g.dart +++ b/lib/widgets/post/full_post_store.g.dart @@ -9,6 +9,20 @@ part of 'full_post_store.dart'; // ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic mixin _$FullPostStore on _FullPostStore, Store { + Computed?>? _$commentTreeComputed; + + @override + List? get commentTree => (_$commentTreeComputed ??= + Computed?>(() => super.commentTree, + name: '_FullPostStore.commentTree')) + .value; + Computed?>? _$sortedCommentTreeComputed; + + @override + List? get sortedCommentTree => (_$sortedCommentTreeComputed ??= + Computed?>(() => super.sortedCommentTree, + name: '_FullPostStore.sortedCommentTree')) + .value; Computed? _$postViewComputed; @override @@ -42,18 +56,33 @@ mixin _$FullPostStore on _FullPostStore, Store { final _$newCommentsAtom = Atom(name: '_FullPostStore.newComments'); @override - List get newComments { + ObservableList get newComments { _$newCommentsAtom.reportRead(); return super.newComments; } @override - set newComments(List value) { + set newComments(ObservableList value) { _$newCommentsAtom.reportWrite(value, super.newComments, () { super.newComments = value; }); } + final _$sortingAtom = Atom(name: '_FullPostStore.sorting'); + + @override + CommentSortType get sorting { + _$sortingAtom.reportRead(); + return super.sorting; + } + + @override + set sorting(CommentSortType value) { + _$sortingAtom.reportWrite(value, super.sorting, () { + super.sorting = value; + }); + } + final _$postStoreAtom = Atom(name: '_FullPostStore.postStore'); @override @@ -87,6 +116,17 @@ mixin _$FullPostStore on _FullPostStore, Store { final _$_FullPostStoreActionController = ActionController(name: '_FullPostStore'); + @override + void updateSorting(CommentSortType sort) { + final _$actionInfo = _$_FullPostStoreActionController.startAction( + name: '_FullPostStore.updateSorting'); + try { + return super.updateSorting(sort); + } finally { + _$_FullPostStoreActionController.endAction(_$actionInfo); + } + } + @override void addComment(CommentView commentView) { final _$actionInfo = _$_FullPostStoreActionController.startAction( @@ -103,7 +143,10 @@ mixin _$FullPostStore on _FullPostStore, Store { return ''' fullPostView: ${fullPostView}, newComments: ${newComments}, +sorting: ${sorting}, postStore: ${postStore}, +commentTree: ${commentTree}, +sortedCommentTree: ${sortedCommentTree}, postView: ${postView}, comments: ${comments} '''; diff --git a/lib/widgets/post/post.dart b/lib/widgets/post/post.dart index 7e40dc1..3769d3e 100644 --- a/lib/widgets/post/post.dart +++ b/lib/widgets/post/post.dart @@ -9,6 +9,7 @@ import '../../util/async_store_listener.dart'; import '../../util/extensions/api.dart'; import '../../util/goto.dart'; import 'post_actions.dart'; +import 'post_body.dart'; import 'post_info_section.dart'; import 'post_link_preview.dart'; import 'post_media.dart'; @@ -84,6 +85,7 @@ class _Post extends HookWidget { PostTitle(), PostMedia(), PostLinkPreview(), + PostBody(), PostActions(), ], ),