1
0
mirror of https://github.com/krawieck/lemmur/ synced 2024-12-23 14:28:03 +01:00

transition comment section to use mobx

This commit is contained in:
krawieck 2021-10-01 23:03:42 +02:00
parent cca679d6ef
commit 46a2f48e3a
6 changed files with 181 additions and 131 deletions

View File

@ -39,6 +39,15 @@ extension on CommentSortType {
}
}
extension SortCommentTreeList on List<CommentTree> {
void sortBy(CommentSortType sortType) {
sort(sortType.sortFunction);
for (final el in this) {
el._sort(sortType.sortFunction);
}
}
}
class CommentTree {
CommentView comment;
List<CommentTree> children = [];
@ -71,14 +80,4 @@ class CommentTree {
el._sort(compare);
}
}
/// Sorts in-place a list of CommentTrees according to a given sortType
static List<CommentTree> sortList(
CommentSortType sortType, List<CommentTree> comms) {
comms.sort(sortType.sortFunction);
for (final el in comms) {
el._sort(sortType.sortFunction);
}
return comms;
}
}

View File

@ -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<FullPostStore>(
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<AccountsStore>()
.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;

View File

@ -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<CommentView> rawComments;
final List<CommentTree> 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<CommentView> 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<FullPostStore>(
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<AccountsStore>()
.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),
]);
);
},
);
}
}

View File

@ -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<CommentView> newComments = [];
ObservableList<CommentView> newComments = ObservableList<CommentView>();
@observable
CommentSortType sorting = CommentSortType.hot;
@action
// ignore: use_setters_to_change_properties
void updateSorting(CommentSortType sort) {
sorting = sort;
}
@computed
List<CommentTree>? get commentTree {
if (fullPostView == null) return null;
return CommentTree.fromList(fullPostView!.comments);
}
@computed
List<CommentTree>? 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;
}

View File

@ -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<List<CommentTree>?>? _$commentTreeComputed;
@override
List<CommentTree>? get commentTree => (_$commentTreeComputed ??=
Computed<List<CommentTree>?>(() => super.commentTree,
name: '_FullPostStore.commentTree'))
.value;
Computed<List<CommentTree>?>? _$sortedCommentTreeComputed;
@override
List<CommentTree>? get sortedCommentTree => (_$sortedCommentTreeComputed ??=
Computed<List<CommentTree>?>(() => super.sortedCommentTree,
name: '_FullPostStore.sortedCommentTree'))
.value;
Computed<PostView?>? _$postViewComputed;
@override
@ -42,18 +56,33 @@ mixin _$FullPostStore on _FullPostStore, Store {
final _$newCommentsAtom = Atom(name: '_FullPostStore.newComments');
@override
List<CommentView> get newComments {
ObservableList<CommentView> get newComments {
_$newCommentsAtom.reportRead();
return super.newComments;
}
@override
set newComments(List<CommentView> value) {
set newComments(ObservableList<CommentView> 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}
''';

View File

@ -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(),
],
),