Skip to content

Commit

Permalink
Merge pull request #725 from ajsosa/insert-comment
Browse files Browse the repository at this point in the history
#498 Insert new comments without refrshing
  • Loading branch information
hjiangsu authored Sep 12, 2023
2 parents a39ef6a + 5160223 commit 8e01ebd
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 29 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- Show sort type icon - contribution from @micahmo
- Downvote actions will be disabled when instances have downvotes disabled
- Automatically save drafts for posts and comments - contribution from @micahmo
- Newly created comments get inserted into comment list correctly without losing your scroll position. If comment is top level, the list scrolls to your comment. The comment also gets highlighted - contribution from @ajsosa

### Changed

Expand Down Expand Up @@ -49,6 +50,7 @@
- Fix issues entering URLs with some keyboards when logging in - contribution from @micahmo
- Fix issue with accessibility in sort picker - contribution from @micahmo
- Fix issue where deleted replies could not be marked read - contribution from @micahmo
- Fix issue where creating new comment refreshes the whole post page - contribution from @ajsosa

## 0.2.3+16 - 2023-08-15

Expand Down
45 changes: 24 additions & 21 deletions lib/post/bloc/post_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ class PostBloc extends Bloc<PostEvent, PostState> {

while (attemptCount < 2) {
try {
emit(state.copyWith(status: PostStatus.loading, selectedCommentPath: event.selectedCommentPath, selectedCommentId: event.selectedCommentId));
emit(state.copyWith(
status: PostStatus.loading, selectedCommentPath: event.selectedCommentPath, selectedCommentId: event.selectedCommentId, newlyCreatedCommentId: event.newlyCreatedCommentId));

LemmyApiV3 lemmy = LemmyClient.instance.lemmyApiV3;

Expand Down Expand Up @@ -126,18 +127,18 @@ class PostBloc extends Bloc<PostEvent, PostState> {
}
}

emit(
state.copyWith(
status: PostStatus.success,
postId: postView?.postView.post.id,
postView: postView,
communityId: postView?.postView.post.communityId,
moderators: moderators,
selectedCommentPath: event.selectedCommentPath,
selectedCommentId: event.selectedCommentId),
);
emit(state.copyWith(
status: PostStatus.success,
postId: postView?.postView.post.id,
postView: postView,
communityId: postView?.postView.post.communityId,
moderators: moderators,
selectedCommentPath: event.selectedCommentPath,
selectedCommentId: event.selectedCommentId,
newlyCreatedCommentId: event.newlyCreatedCommentId));

emit(state.copyWith(status: PostStatus.refreshing, selectedCommentPath: event.selectedCommentPath, selectedCommentId: event.selectedCommentId));
emit(state.copyWith(
status: PostStatus.refreshing, selectedCommentPath: event.selectedCommentPath, selectedCommentId: event.selectedCommentId, newlyCreatedCommentId: event.newlyCreatedCommentId));

CommentSortType sortType = event.sortType ?? (state.sortType ?? defaultSortType);

Expand Down Expand Up @@ -183,7 +184,8 @@ class PostBloc extends Bloc<PostEvent, PostState> {
communityId: postView?.postView.post.communityId,
sortType: sortType,
selectedCommentId: event.selectedCommentId,
selectedCommentPath: event.selectedCommentPath),
selectedCommentPath: event.selectedCommentPath,
newlyCreatedCommentId: event.newlyCreatedCommentId),
);
} catch (e) {
exception = e;
Expand Down Expand Up @@ -469,17 +471,18 @@ class PostBloc extends Bloc<PostEvent, PostState> {
int? selectedCommentId = event.selectedCommentId;
String? selectedCommentPath = event.selectedCommentPath;

// for now, refresh the post and refetch the comments
// @todo: insert the new comment in place without requiring a refetch
// @todo: alternatively, insert and scroll to new comment on refetch
if (event.parentCommentId != null) {
add(GetPostEvent(postView: state.postView!, selectedCommentId: selectedCommentId, selectedCommentPath: selectedCommentPath));
} else {
List<CommentViewTree> updatedComments = insertNewComment(state.comments, createComment.commentView);

if (event.parentCommentId == null) {
selectedCommentId = null;
selectedCommentPath = null;
add(GetPostEvent(postView: state.postView!, selectedCommentId: selectedCommentId, selectedCommentPath: selectedCommentPath));
}
return emit(state.copyWith(status: PostStatus.success, selectedCommentId: selectedCommentId, selectedCommentPath: selectedCommentPath));
return emit(state.copyWith(
status: PostStatus.success,
comments: updatedComments,
selectedCommentId: selectedCommentId,
selectedCommentPath: selectedCommentPath,
newlyCreatedCommentId: createComment.commentView.comment.id));
} catch (e) {
return emit(state.copyWith(status: PostStatus.failure, errorMessage: e.toString()));
}
Expand Down
3 changes: 2 additions & 1 deletion lib/post/bloc/post_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ class GetPostEvent extends PostEvent {
final CommentSortType? sortType;
final String? selectedCommentPath;
final int? selectedCommentId;
final int? newlyCreatedCommentId;

const GetPostEvent({this.sortType, this.postView, this.postId, this.selectedCommentPath, this.selectedCommentId});
const GetPostEvent({this.sortType, this.postView, this.postId, this.selectedCommentPath, this.selectedCommentId, this.newlyCreatedCommentId});
}

class GetPostCommentsEvent extends PostEvent {
Expand Down
7 changes: 7 additions & 0 deletions lib/post/bloc/post_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class PostState extends Equatable {
this.sortTypeIcon,
this.selectedCommentId,
this.selectedCommentPath,
this.newlyCreatedCommentId,
this.moddingCommentId = -1,
this.viewAllCommentsRefresh = false,
this.navigateCommentIndex = 0,
Expand All @@ -44,14 +45,17 @@ class PostState extends Equatable {
final int commentCount;
final bool hasReachedCommentEnd;
final int? selectedCommentId;
final int? newlyCreatedCommentId;
final String? selectedCommentPath;

// This is to track what comment is being restored or deleted so we can
// show a spinner indicator that thunder is working on it
final int moddingCommentId;

final String? errorMessage;

final int navigateCommentIndex;

// This exists purely for forcing the bloc to refire
// even if the comment index doesn't change
final int navigateCommentId;
Expand All @@ -72,6 +76,7 @@ class PostState extends Equatable {
IconData? sortTypeIcon,
int? selectedCommentId,
String? selectedCommentPath,
int? newlyCreatedCommentId,
int? moddingCommentId,
bool? viewAllCommentsRefresh = false,
int? navigateCommentIndex,
Expand All @@ -93,6 +98,7 @@ class PostState extends Equatable {
sortTypeIcon: sortTypeIcon ?? this.sortTypeIcon,
selectedCommentId: selectedCommentId,
selectedCommentPath: selectedCommentPath,
newlyCreatedCommentId: newlyCreatedCommentId,
moddingCommentId: moddingCommentId ?? this.moddingCommentId,
viewAllCommentsRefresh: viewAllCommentsRefresh ?? false,
navigateCommentIndex: navigateCommentIndex ?? 0,
Expand All @@ -116,6 +122,7 @@ class PostState extends Equatable {
sortTypeIcon,
selectedCommentId,
selectedCommentPath,
newlyCreatedCommentId,
viewAllCommentsRefresh,
moddingCommentId,
navigateCommentIndex,
Expand Down
1 change: 1 addition & 0 deletions lib/post/pages/post_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ class _PostPageState extends State<PostPage> {
comments: state.comments,
selectedCommentId: state.selectedCommentId,
selectedCommentPath: state.selectedCommentPath,
newlyCreatedCommentId: state.newlyCreatedCommentId,
moddingCommentId: state.moddingCommentId,
viewFullCommentsRefreshing: state.viewAllCommentsRefresh,
itemScrollController: _itemScrollController,
Expand Down
3 changes: 3 additions & 0 deletions lib/post/pages/post_page_success.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class PostPageSuccess extends StatefulWidget {
final List<CommentViewTree> comments;
final int? selectedCommentId;
final String? selectedCommentPath;
final int? newlyCreatedCommentId;
final int? moddingCommentId;

final ItemScrollController itemScrollController;
Expand All @@ -47,6 +48,7 @@ class PostPageSuccess extends StatefulWidget {
this.hasReachedCommentEnd = false,
this.selectedCommentId,
this.selectedCommentPath,
this.newlyCreatedCommentId,
this.moddingCommentId,
this.viewFullCommentsRefreshing = false,
required this.moderators,
Expand Down Expand Up @@ -85,6 +87,7 @@ class _PostPageSuccessState extends State<PostPageSuccess> {
moddingCommentId: widget.moddingCommentId,
selectedCommentId: widget.selectedCommentId,
selectedCommentPath: widget.selectedCommentPath,
newlyCreatedCommentId: widget.newlyCreatedCommentId,
now: DateTime.now().toUtc(),
itemScrollController: widget.itemScrollController,
itemPositionsListener: widget.itemPositionsListener,
Expand Down
17 changes: 10 additions & 7 deletions lib/post/widgets/comment_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ import 'package:thunder/core/enums/nested_comment_indicator.dart';
import 'package:thunder/core/enums/swipe_action.dart';
import 'package:thunder/post/bloc/post_bloc.dart';
import 'package:thunder/post/utils/comment_actions.dart';
import 'package:thunder/shared/comment_card_actions.dart';
import 'package:thunder/shared/comment_header.dart';
import 'package:thunder/shared/common_markdown_body.dart';
import 'package:thunder/core/auth/bloc/auth_bloc.dart';
import 'package:thunder/core/models/comment_view_tree.dart';
import 'package:thunder/thunder/bloc/thunder_bloc.dart';
Expand All @@ -29,6 +26,7 @@ class CommentCard extends StatefulWidget {
final Set collapsedCommentSet;
final int? selectCommentId;
final String? selectedCommentPath;
final int? newlyCreatedCommentId;
final int? moddingCommentId;

final DateTime now;
Expand All @@ -48,6 +46,7 @@ class CommentCard extends StatefulWidget {
this.collapsedCommentSet = const {},
this.selectCommentId,
this.selectedCommentPath,
this.newlyCreatedCommentId,
this.moddingCommentId,
required this.onDeleteAction,
required this.moderators,
Expand Down Expand Up @@ -140,12 +139,15 @@ class _CommentCardState extends State<CommentCard> with SingleTickerProviderStat

// Checks for the same creator id to user id
final bool isOwnComment = widget.commentViewTree.commentView?.creator.id == context.read<AuthBloc>().state.account?.userId;

final bool isUserLoggedIn = context.read<AuthBloc>().state.isLoggedIn;

final ThunderState state = context.read<ThunderBloc>().state;

bool collapseParentCommentOnGesture = state.collapseParentCommentOnGesture;
final int? commentId = widget.commentViewTree.commentView?.comment.id;
bool highlightComment = false;
if (widget.selectCommentId == commentId && widget.newlyCreatedCommentId == null || widget.newlyCreatedCommentId == commentId) {
highlightComment = true;
}

NestedCommentIndicatorStyle nestedCommentIndicatorStyle = state.nestedCommentIndicatorStyle;
NestedCommentIndicatorColor nestedCommentIndicatorColor = state.nestedCommentIndicatorColor;

Expand Down Expand Up @@ -335,7 +337,7 @@ class _CommentCardState extends State<CommentCard> with SingleTickerProviderStat
),
),
child: Material(
color: widget.selectCommentId == widget.commentViewTree.commentView!.comment.id ? theme.highlightColor : theme.colorScheme.background,
color: highlightComment ? theme.highlightColor : theme.colorScheme.background,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
Expand Down Expand Up @@ -442,6 +444,7 @@ class _CommentCardState extends State<CommentCard> with SingleTickerProviderStat
moddingCommentId: widget.moddingCommentId,
selectedCommentPath: widget.selectedCommentPath,
selectCommentId: widget.selectCommentId,
newlyCreatedCommentId: widget.newlyCreatedCommentId,
now: widget.now,
commentViewTree: widget.commentViewTree.replies[index],
collapsedCommentSet: widget.collapsedCommentSet,
Expand Down
10 changes: 10 additions & 0 deletions lib/post/widgets/comment_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class CommentSubview extends StatefulWidget {
final PostViewMedia? postViewMedia;
final int? selectedCommentId;
final String? selectedCommentPath;
final int? newlyCreatedCommentId;
final int? moddingCommentId;
final ItemScrollController itemScrollController;
final ItemPositionsListener itemPositionsListener;
Expand All @@ -46,6 +47,7 @@ class CommentSubview extends StatefulWidget {
this.postViewMedia,
this.selectedCommentId,
this.selectedCommentPath,
this.newlyCreatedCommentId,
this.moddingCommentId,
required this.itemScrollController,
required this.itemPositionsListener,
Expand Down Expand Up @@ -107,6 +109,13 @@ class _CommentSubviewState extends State<CommentSubview> with SingleTickerProvid
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
);
} else if (state.newlyCreatedCommentId != null && state.comments.first.commentView?.comment.id == state.newlyCreatedCommentId) {
// Only scroll for top level comments since you can comment from anywhere in the comment section.
widget.itemScrollController.scrollTo(
index: 1,
duration: const Duration(milliseconds: 250),
curve: Curves.easeInOut,
);
}
},
child: ScrollablePositionedList.builder(
Expand Down Expand Up @@ -172,6 +181,7 @@ class _CommentSubviewState extends State<CommentSubview> with SingleTickerProvid
now: widget.now,
selectCommentId: widget.selectedCommentId,
selectedCommentPath: widget.selectedCommentPath,
newlyCreatedCommentId: widget.newlyCreatedCommentId,
moddingCommentId: widget.moddingCommentId,
commentViewTree: widget.comments[index - 1],
collapsedCommentSet: collapsedCommentSet,
Expand Down
43 changes: 43 additions & 0 deletions lib/utils/comment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,49 @@ List<CommentViewTree> buildCommentViewTree(List<CommentView> comments, {bool fla
return commentMap.values.where((commentView) => commentView.commentView!.comment.path.isEmpty || commentView.commentView!.comment.path == '0.${commentView.commentView!.comment.id}').toList();
}

List<CommentViewTree> insertNewComment(List<CommentViewTree> comments, CommentView commentView) {
List<String> parentIds = commentView.comment.path.split('.');
String commentTime = commentView.comment.published.toIso8601String();

CommentViewTree newCommentTree = CommentViewTree(
datePostedOrEdited: formatTimeToString(dateTime: commentTime),
commentView: commentView,
replies: [],
level: commentView.comment.path.split('.').length - 2,
);

if (parentIds[1] == commentView.comment.id.toString()) {
comments.insert(0, newCommentTree);
return comments;
}

String parentId = parentIds[parentIds.length - 2];
CommentViewTree? parentComment = findParentComment(1, parentIds, parentId.toString(), comments);

// TODO: surface some sort of error maybe if for some reason we fail to find parent comment
if (parentComment != null) {
parentComment.replies.insert(0, newCommentTree);
}

return comments;
}

CommentViewTree? findParentComment(int index, List<String> parentIds, String targetId, List<CommentViewTree> comments) {
for (CommentViewTree existing in comments) {
if (existing.commentView?.comment.id.toString() != parentIds[index]) {
continue;
}

if (targetId == existing.commentView?.comment.id.toString()) {
return existing;
}

return findParentComment(index + 1, parentIds, targetId, existing.replies);
}

return null;
}

List<int> findCommentIndexesFromCommentViewTree(List<CommentViewTree> commentTrees, int commentId, [List<int>? indexes]) {
indexes ??= [];

Expand Down

0 comments on commit 8e01ebd

Please sign in to comment.