1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-10-23 15:03:22 +00:00
comunicmobile/lib/ui/widgets/posts_list_widget.dart

300 lines
7.9 KiB
Dart
Raw Normal View History

2020-05-10 16:48:26 +00:00
import 'dart:math';
2020-04-18 14:35:53 +00:00
import 'package:comunic/helpers/events_helper.dart';
2019-06-10 07:47:02 +00:00
import 'package:comunic/helpers/groups_helper.dart';
2020-04-18 14:07:56 +00:00
import 'package:comunic/helpers/posts_helper.dart';
2019-05-11 13:35:07 +00:00
import 'package:comunic/helpers/users_helper.dart';
2019-06-10 07:47:02 +00:00
import 'package:comunic/lists/groups_list.dart';
2019-05-11 13:35:07 +00:00
import 'package:comunic/lists/posts_list.dart';
import 'package:comunic/lists/users_list.dart';
2020-04-18 14:35:53 +00:00
import 'package:comunic/models/comment.dart';
2019-05-19 12:54:09 +00:00
import 'package:comunic/models/post.dart';
2019-05-11 13:35:07 +00:00
import 'package:comunic/ui/screens/conversation_screen.dart';
import 'package:comunic/ui/tiles/post_tile.dart';
2020-04-18 14:07:56 +00:00
import 'package:comunic/ui/widgets/safe_state.dart';
import 'package:comunic/ui/widgets/scroll_watcher.dart';
2019-05-11 13:35:07 +00:00
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart';
/// Posts list widget
///
/// Displays a list of posts
///
/// @author Pierre HUBERT
class PostsListWidget extends StatefulWidget {
final List<Widget>? topWidgets;
final Future<PostsList?> Function() getPostsList;
final Future<PostsList?> Function(int from)? getOlder;
2019-05-23 16:37:56 +00:00
final bool showPostsTarget;
2019-06-10 12:47:27 +00:00
final bool buildListView;
final bool userNamesClickable;
2020-05-16 09:04:58 +00:00
final bool disablePullToRefresh;
2019-05-11 13:35:07 +00:00
const PostsListWidget({
Key? key,
required this.getPostsList,
required this.showPostsTarget,
this.userNamesClickable = true,
2019-06-10 12:47:27 +00:00
this.buildListView = true,
this.getOlder,
2020-05-08 07:23:02 +00:00
this.topWidgets,
2020-05-16 09:04:58 +00:00
this.disablePullToRefresh = false,
2019-05-11 13:35:07 +00:00
}) : assert(getPostsList != null),
2019-05-23 16:37:56 +00:00
assert(showPostsTarget != null),
2019-06-10 12:47:27 +00:00
assert(buildListView != null),
assert(userNamesClickable != null),
2020-05-16 09:04:58 +00:00
assert(disablePullToRefresh != null),
2019-05-11 13:35:07 +00:00
super(key: key);
@override
2020-04-16 07:07:23 +00:00
State<StatefulWidget> createState() => PostsListWidgetState();
2019-05-11 13:35:07 +00:00
}
2020-04-18 14:07:56 +00:00
class PostsListWidgetState extends SafeState<PostsListWidget> {
2019-05-11 13:35:07 +00:00
// Helpers
final UsersHelper _usersHelper = UsersHelper();
2019-06-10 07:47:02 +00:00
final GroupsHelper _groupsHelper = GroupsHelper();
2019-05-11 13:35:07 +00:00
// Class members
PostsList? _list;
late UsersList _users;
late GroupsList _groups;
ScrollWatcher? _scrollController;
ErrorLevel _error = ErrorLevel.NONE;
2019-06-15 14:29:07 +00:00
bool _loading = false;
2019-05-11 13:35:07 +00:00
2020-04-18 14:07:56 +00:00
final _registeredPosts = Set<int>();
2019-05-11 13:35:07 +00:00
set error(ErrorLevel err) => setState(() => _error = err);
2020-05-08 07:23:02 +00:00
int get _numberTopWidgets =>
widget.topWidgets == null ? 0 : widget.topWidgets!.length;
2020-05-08 07:23:02 +00:00
@override
void initState() {
super.initState();
2020-04-16 07:07:23 +00:00
_scrollController = ScrollWatcher(onReachBottom: reachedPostsBottom);
2020-04-18 14:35:53 +00:00
// Register to events
this.listen<NewCommentEvent>((ev) => this._addComment(ev.comment));
2020-04-18 14:46:55 +00:00
this.listenChangeState<UpdatedCommentEvent>(
(ev) => this._updateComment(ev.comment));
2020-04-18 14:57:00 +00:00
this.listenChangeState<DeletedCommentEvent>(
(ev) => this._removeComment(ev.commentID));
}
2019-05-11 13:35:07 +00:00
2020-04-18 14:07:56 +00:00
@override
void dispose() {
super.dispose();
_unregisterAllPosts();
}
@override
void setState(fn) {
if (!mounted) return;
super.setState(fn);
// Register for posts update
_registerRequiredPosts();
}
2019-05-11 13:35:07 +00:00
@override
void didChangeDependencies() {
super.didChangeDependencies();
2020-04-17 08:37:02 +00:00
loadPostsList();
2019-05-11 13:35:07 +00:00
}
2019-06-15 14:29:07 +00:00
void _loadError() {
error = _list == null ? ErrorLevel.MAJOR : ErrorLevel.MINOR;
_loading = false;
}
2019-05-11 13:35:07 +00:00
/// Load the list of posts
2020-04-17 08:37:02 +00:00
Future<void> loadPostsList({bool getOlder = false}) async {
2019-11-02 11:46:17 +00:00
if (_loading) return;
2019-06-15 14:29:07 +00:00
_loading = true;
2021-03-10 16:54:41 +00:00
try {
final list = !getOlder
? await widget.getPostsList()
: await widget.getOlder!(_list!.oldestID);
2021-03-10 16:54:41 +00:00
if (list == null) return _loadError();
final users = await _usersHelper.getList(list.usersID);
final groups = await _groupsHelper.getList(list.groupsID);
if (groups == null) return _loadError();
if (!mounted) return;
setState(() {
if (!getOlder) {
_list = list;
_users = users;
_groups = groups;
} else {
_list!.addAll(list);
2021-03-10 16:54:41 +00:00
_users.addAll(users);
_groups.addAll(groups);
}
});
} catch (e, s) {
print("Failed to load post information ! $e => $s");
_loadError();
}
2019-06-15 14:29:07 +00:00
_loading = false;
2019-05-11 13:35:07 +00:00
}
2020-04-18 14:07:56 +00:00
/// Register for potential new posts
void _registerRequiredPosts() async {
if (_list == null) return;
final missing = _list!
2020-04-18 14:07:56 +00:00
.where((f) => !_registeredPosts.contains(f.id))
.map((f) => f.id)
.toSet();
_registeredPosts.addAll(missing);
for (final postID in missing) {
await PostsHelper().registerPostEvents(postID);
}
}
/// Unregister from all posts
///
/// This method should be called only once
void _unregisterAllPosts() async {
for (final postID in _registeredPosts) {
// We put the try - catch here because we must absolutely unregister ALL
// POSTS event if one post fails to remove
try {
await PostsHelper().unregisterPostEvents(postID);
} catch (e, stack) {
print("Could not unregister post! $e $stack");
}
}
}
2019-05-11 13:35:07 +00:00
Widget _buildErrorCard() {
return buildErrorCard(tr("Could not get the list of posts !"));
}
Widget _buildNoPostNotice() {
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(tr("There is no post to display here yet.")!),
),
);
}
2020-05-16 09:04:58 +00:00
Widget _buildListViewWithRefreshIndicator() => RefreshIndicator(
child: _buildListView(),
onRefresh: () => loadPostsList(),
);
2019-05-11 13:35:07 +00:00
Widget _buildListView() {
2020-05-16 09:04:58 +00:00
return ListView.builder(
// We use max function here to display to post notice in case there are not posts to display but there are custom widgets...
itemCount: max(_list!.length, 1) + _numberTopWidgets,
2020-05-10 16:48:26 +00:00
2020-05-16 09:04:58 +00:00
itemBuilder: _buildItem,
controller: _scrollController,
2019-06-10 12:47:27 +00:00
);
}
Widget _buildColumn() {
return Column(
children: List.generate(
_list!.length,
2019-06-10 12:47:27 +00:00
(i) => _buildItem(null, i),
),
);
}
Widget _buildItem(BuildContext? context, int index) {
if (index < _numberTopWidgets) return widget.topWidgets![index];
2020-05-08 07:23:02 +00:00
2020-05-10 16:48:26 +00:00
// Show no posts notice if required
if (_list!.length == 0) return _buildNoPostNotice();
2020-05-10 16:48:26 +00:00
2019-06-10 12:47:27 +00:00
return PostTile(
post: _list![index - _numberTopWidgets],
2019-06-10 12:47:27 +00:00
usersInfo: _users,
groupsInfo: _groups,
onDeletedPost: _removePost,
showPostTarget: widget.showPostsTarget,
userNamesClickable: widget.userNamesClickable,
2019-05-11 13:35:07 +00:00
);
}
@override
Widget build(BuildContext context) {
if (_error == ErrorLevel.MAJOR) return _buildErrorCard();
if (_list == null) return buildCenteredProgressBar();
if (_list!.length == 0 && _numberTopWidgets == 0)
2020-05-10 16:48:26 +00:00
return _buildNoPostNotice();
2020-05-16 09:04:58 +00:00
if (!widget.buildListView) return _buildColumn();
if (widget.disablePullToRefresh) return _buildListView();
return _buildListViewWithRefreshIndicator();
2019-05-11 13:35:07 +00:00
}
2019-05-19 12:54:09 +00:00
void _removePost(Post post) => setState(() => _list!.remove(post));
2020-04-16 07:07:23 +00:00
void reachedPostsBottom() {
2020-04-17 08:37:02 +00:00
if (widget.getOlder != null) loadPostsList(getOlder: true);
}
2020-04-18 14:35:53 +00:00
/// Add new comment
void _addComment(Comment c) async {
if (_list == null) return;
try {
// This command will throw if post could not be found
final Post p = _list!.singleWhere((p) => p.id == c.postID);
2020-04-18 14:35:53 +00:00
if (!_users.hasUser(c.userID))
_users.add(await UsersHelper().getSingleWithThrow(c.userID));
setState(() {
p.comments!.add(c);
2020-04-18 14:35:53 +00:00
});
} catch (e, stack) {
print("$e\n$stack");
}
}
2020-04-18 14:46:55 +00:00
/// Update a comment
void _updateComment(Comment c) {
if (_list == null) return;
try {
final Post p = _list!.singleWhere((p) => p.id == c.postID);
2020-04-18 14:46:55 +00:00
final index = p.comments!.indexWhere((d) => d.id == c.id);
2020-04-18 14:46:55 +00:00
if (index > -1) p.comments![index] = c;
2020-04-18 14:46:55 +00:00
} catch (e, stack) {
print("$e\n$stack");
}
}
2020-04-18 14:57:00 +00:00
/// Remove a comment from the list
void _removeComment(int? commentID) {
2020-04-18 14:57:00 +00:00
if (_list == null) return;
_list!.forEach((p) => p.comments!.removeWhere((c) => c.id == commentID));
2020-04-18 14:57:00 +00:00
}
2019-05-11 13:35:07 +00:00
}