Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update comment count when adding a comment #349

Merged
merged 24 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d627cfe
feat(comment-count): add post comment count view model
JoachimFavre May 25, 2024
04c6e01
feat(comment-count): make UI use comment count view model
JoachimFavre May 25, 2024
9c4c2a5
feat(comment-count): refresh the comment count when the comment list …
JoachimFavre May 25, 2024
feff9b3
docs(comment-count): add documentation to view-model
JoachimFavre May 25, 2024
4e9beda
refactor(comment-count): remove unnecessary await
JoachimFavre May 25, 2024
3f56292
feat(comment-count): refresh the comment count when a comment is deleted
JoachimFavre May 25, 2024
e5c2f6c
docs(comment-count): improve documentation about auto-dispose
JoachimFavre May 25, 2024
7b901e4
feat(delete-comment-action): remove assumption that user has a single…
JoachimFavre May 25, 2024
ef39f27
test(comment-count): verify comment count update on navigation
JoachimFavre May 25, 2024
810bf09
fix(comment-count-view-model): set loading state while refreshing
JoachimFavre May 26, 2024
70ac3f7
fix(comment-count): use AsyncValue::value instead of valueOrNull
JoachimFavre May 26, 2024
35238cc
refactor(comment-count): remove setCount function
JoachimFavre May 26, 2024
af713c8
fix(home-view-model-override): also override the comment count provider
JoachimFavre May 26, 2024
9067b9c
docs: add documentation to MockPostCommentCountViewModel
JoachimFavre May 26, 2024
071b91a
test(comment-count-vm): Test for refresh after comment list refresh
JoachimFavre May 26, 2024
9afda32
test(comment-count-vm): Test for refresh after comment deletion
JoachimFavre May 26, 2024
82f37a4
Merge branch 'main' into update-comment-count
JoachimFavre May 26, 2024
e91ddb5
fix(comment-count-vm): make it not auto-dispose
JoachimFavre May 26, 2024
9e8d0b2
fix(comment-count-vm): make its mock not auto-dispose too
JoachimFavre May 26, 2024
4e7a21e
fix(comment-count-vm-test): delete the comment using the correct view…
JoachimFavre May 26, 2024
7ff49ad
opt(comment-count-vm): make refresh closer to the point where the new…
JoachimFavre May 26, 2024
55927de
Merge branch 'main' into update-comment-count
JoachimFavre May 27, 2024
ecbc16f
style(comment-count-test): reuse already existing variable
JoachimFavre May 27, 2024
cdcffc8
Merge branch 'main' into update-comment-count
JoachimFavre May 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/viewmodels/comments_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "package:proxima/models/database/post/post_id_firestore.dart";
import "package:proxima/models/ui/comment_details.dart";
import "package:proxima/services/database/comment/comment_repository_service.dart";
import "package:proxima/services/database/user_repository_service.dart";
import "package:proxima/viewmodels/post_comment_count_view_model.dart";

/// This view model is used to fetch the comments of a post.
/// It fetches the comments under the post with the id [arg] and returns
Expand Down Expand Up @@ -45,6 +46,9 @@ class CommentsViewModel extends AutoDisposeFamilyAsyncNotifier<
-commentA.publicationDate.compareTo(commentB.publicationDate),
);

// Update the post comment count
ref.read(postCommentCountProvider(arg).notifier).setCount(comments.length);
gruvw marked this conversation as resolved.
Show resolved Hide resolved

return sortedComments;
}

Expand Down
35 changes: 35 additions & 0 deletions lib/viewmodels/post_comment_count_view_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:proxima/models/database/post/post_id_firestore.dart";
import "package:proxima/services/database/post_repository_service.dart";

/// This view model is used to keep in memory the number of comments of a post.
/// It is refreshed every time the post comment list is refreshed, to stay consistent
/// with it. It is also refreshed when a comment is deleted.
/// The fact that it is auto-dispose allows to keep the memory usage low, keeping only
/// the count of the comments of the posts that are currently displayed on the screen.
/// This may moreover help mitigate some reloading bugs, since it suffices to scroll
/// the post out of the screen to dispose the view model and thus the count of the comments.
class PostCommentCountViewModel
extends AutoDisposeFamilyAsyncNotifier<int, PostIdFirestore> {
@override
Future<int> build(PostIdFirestore arg) async {
final postRepo = ref.watch(postRepositoryServiceProvider);
final post = await postRepo.getPost(arg);
return post.data.commentCount;
}

/// Set the count of comments of the post to [count]. This updates
/// the state without needing to fetch the data from the database.
void setCount(int count) {
state = AsyncValue.data(count);
}

Future<void> refresh() async {
state = await AsyncValue.guard(() => build(arg));
gruvw marked this conversation as resolved.
Show resolved Hide resolved
}
}

final postCommentCountProvider = AsyncNotifierProvider.autoDispose
.family<PostCommentCountViewModel, int, PostIdFirestore>(
() => PostCommentCountViewModel(),
);
3 changes: 3 additions & 0 deletions lib/viewmodels/user_comments_view_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "package:proxima/models/database/post/post_id_firestore.dart";
import "package:proxima/models/ui/user_comment_details.dart";
import "package:proxima/services/database/comment/comment_repository_service.dart";
import "package:proxima/viewmodels/login_view_model.dart";
import "package:proxima/viewmodels/post_comment_count_view_model.dart";
import "package:proxima/viewmodels/posts_feed_view_model.dart";

typedef UserCommentsState = List<UserCommentDetails>;
Expand Down Expand Up @@ -53,6 +54,8 @@ class UserCommentViewModel extends AutoDisposeAsyncNotifier<UserCommentsState> {
// Refresh the home feed after comment deletion, that way the comment
// count will be updated
ref.read(postsFeedViewModelProvider.notifier).refresh();
// Also update the comment count for the post
ref.read(postCommentCountProvider(postId).notifier).refresh();
gruvw marked this conversation as resolved.
Show resolved Hide resolved
}

/// Refresh the list of posts
Expand Down
14 changes: 10 additions & 4 deletions lib/views/pages/home/content/feed/components/comment_count.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import "package:flutter/material.dart";
import "package:hooks_riverpod/hooks_riverpod.dart";
import "package:proxima/models/database/post/post_id_firestore.dart";
import "package:proxima/viewmodels/post_comment_count_view_model.dart";

/// This widget is used to display the comment number in the post card.
/// It contains the comment icon and the number of comments.
class CommentCount extends StatelessWidget {
final int count;
class CommentCount extends ConsumerWidget {
final PostIdFirestore postId;

const CommentCount({
super.key,
required this.count,
required this.postId,
});

@override
Widget build(BuildContext context) {
Widget build(BuildContext context, WidgetRef ref) {
final asyncCount = ref.watch(postCommentCountProvider(postId));
final count = asyncCount.valueOrNull ?? 0;

const icon = Icon(Icons.comment, size: 20);
final countText = Text(count.toString());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class PostCard extends ConsumerWidget {
onTap: () => _onPostSelect(context, postDetails, ref),
child: CommentCount(
key: postCardCommentsNumberKey,
count: postDetails.commentNumber,
postId: postDetails.postId,
),
),
],
Expand Down
18 changes: 14 additions & 4 deletions test/end2end/app_actions.dart
gruvw marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,21 @@ class AppActions {
await tester.pumpAndSettle();

// Check that the post content is displayed
expect(find.text(comment), findsOneWidget);
final commentText = find.text(comment);
expect(commentText, findsOneWidget);

// Find the delete button on card
final deleteButton = find.byKey(ProfileInfoCard.deleteButtonCardKey);
expect(deleteButton, findsOneWidget);
// Find and tap the delete button on the card
final commentCard = find.ancestor(
of: find.text(comment),
JoachimFavre marked this conversation as resolved.
Show resolved Hide resolved
matching: find.byKey(ProfileInfoCard.infoCardKey),
);
expect(commentCard, findsOne);

final deleteButton = find.descendant(
of: commentCard,
matching: find.byKey(ProfileInfoCard.deleteButtonCardKey),
);
expect(deleteButton, findsOne);

await tester.tap(deleteButton);
await tester.pumpAndSettle(delayNeededForAsyncFunctionExecution);
Expand Down
25 changes: 13 additions & 12 deletions test/end2end/app_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import "package:proxima/services/database/firestore_service.dart";
import "package:proxima/services/sensors/geolocation_service.dart";
import "package:proxima/views/navigation/bottom_navigation_bar/navigation_bar_routes.dart";
import "package:proxima/views/pages/home/content/challenge/challenge_list.dart";
import "package:proxima/views/pages/home/content/feed/post_feed.dart";
import "package:proxima/views/pages/profile/components/profile_app_bar.dart";
import "package:proxima/views/proxima_app.dart";

Expand Down Expand Up @@ -161,17 +160,19 @@ void main() {
await AppActions.loginToCreateAccount(tester);
await AppActions.createAccountToHome(tester);
await AppActions.createPost(tester, testPostTitle, testPostDescription);
await AppActions.openPost(tester, testPostTitle);
const comment = "I like turtles too!";
await AppActions.addComment(tester, comment);

// back to feed
await AppActions.navigateBack(tester);
await AppActions.flingRefresh(tester, find.byType(PostFeed));
await AppActions.expectCommentCount(tester, testPostTitle, 0);

// Add two comments
const comments = ["I like turtles too!", "I prefer elephants"];
for (final (i, comment) in comments.indexed) {
await AppActions.openPost(tester, testPostTitle);
await AppActions.addComment(tester, comment);
await AppActions.navigateBack(tester);
await AppActions.expectCommentCount(tester, testPostTitle, i + 1);
}

// expect comment count to be correct
AppActions.expectCommentCount(tester, testPostTitle, 1);
await AppActions.deleteComment(tester, comment);
AppActions.expectCommentCount(tester, testPostTitle, 0);
// Deleting a comment should reduce the comment count
await AppActions.deleteComment(tester, comments.first);
await AppActions.expectCommentCount(tester, testPostTitle, 1);
});
}