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

Reader: Fix comments being misplaced after parent comment is moderated #18094

Merged
merged 10 commits into from
Mar 9, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
6 changes: 6 additions & 0 deletions MIGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
This file documents changes in the data model. Please explain any changes to the
data model as well as any custom migrations.

## WordPress 138

@dvdchr 2022-03-07

- `Comment`: added `visibleOnReader` attribute. (required, default `true`, `Boolean`)

## WordPress 137

@dvdchr 2021-11-26
Expand Down
6 changes: 6 additions & 0 deletions WordPress/Classes/Models/Comment+CoreDataProperties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ extension Comment {
@NSManaged public var hierarchy: String
@NSManaged public var replyID: Int32

/// Determines if the comment should be displayed in the Reader comment thread.
///
/// Note that this property is only updated and guaranteed to be correct within the comment thread.
/// The value may be outdated when accessed from other places (e.g. My Sites, Notifications).
@NSManaged public var visibleOnReader: Bool

/*
// Hierarchy is a string representation of a comments ancestors. Each ancestor's
// is denoted by a ten character zero padded representation of its ID
Expand Down
39 changes: 39 additions & 0 deletions WordPress/Classes/Services/CommentService+Replies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,45 @@ extension CommentService {
failure(error)
}
}

/// Update the visibility of the comment's replies on the comment thread.
/// Note that this only applies to comments fetched from the Reader Comments section (i.e. has a reference to the `ReaderPost`).
///
/// - Parameters:
/// - ancestorComment: The ancestor comment that will have its reply comments iterated.
/// - completion: The block executed after the replies are updated.
func updateRepliesVisibility(for ancestorComment: Comment, completion: (() -> Void)? = nil) {
guard let context = ancestorComment.managedObjectContext,
let post = ancestorComment.post as? ReaderPost,
let comments = post.comments as? Set<Comment> else {
completion?()
return
}

let isVisible = (ancestorComment.status == CommentStatusType.approved.description)

// iterate over the ancestor comment's descendants and update their visibility for the comment thread.
//
// the hierarchy property stores ancestral info by storing a string version of its comment ID hierarchy,
// e.g.: "0000000012.0000000025.00000000035". The idea is to check if the ancestor comment's ID exists in the hierarchy.
// as an optimization, skip checking the hierarchy when the comment is the direct child of the ancestor comment.
context.perform {
comments.filter { comment in
comment.parentID == ancestorComment.commentID
|| comment.hierarchy
.split(separator: ".")
.compactMap({ Int32($0) })
.contains(ancestorComment.commentID)
}.forEach { childComment in
childComment.visibleOnReader = isVisible
}

ContextManager.shared.save(context)
DispatchQueue.main.async {
completion?()
}
}
}
}

private extension CommentService {
Expand Down
14 changes: 13 additions & 1 deletion WordPress/Classes/Services/CommentService.m
Original file line number Diff line number Diff line change
Expand Up @@ -1169,6 +1169,7 @@ - (void)mergeHierarchicalComments:(NSArray *)comments forPage:(NSUInteger)page f
return;
}

NSMutableSet<NSNumber *> *visibleCommentIds = [NSMutableSet new];
NSMutableArray *ancestors = [NSMutableArray array];
NSMutableArray *commentsToKeep = [NSMutableArray array];
NSString *entityName = NSStringFromClass([Comment class]);
Expand All @@ -1186,7 +1187,18 @@ - (void)mergeHierarchicalComments:(NSArray *)comments forPage:(NSUInteger)page f
// Calculate hierarchy and depth.
ancestors = [self ancestorsForCommentWithParentID:[NSNumber numberWithInt:comment.parentID] andCurrentAncestors:ancestors];
comment.hierarchy = [self hierarchyFromAncestors:ancestors andCommentID:[NSNumber numberWithInt:comment.commentID]];
comment.depth = [ancestors count];

// Comments are shown on the thread when (1) it is approved, and (2) its ancestors are approved.
// Having the comments sorted hierarchically ascending ensures that each comment's predecessors will be visited first.
// Therefore, we only need to check if the comment and its direct parent are approved.
// Ref: https://github.com/wordpress-mobile/WordPress-iOS/issues/18081
BOOL hasValidParent = comment.parentID > 0 && [visibleCommentIds containsObject:@(comment.parentID)];
if ([comment isApproved] && ([comment isTopLevelComment] || hasValidParent)) {
[visibleCommentIds addObject:@(comment.commentID)];
}
comment.visibleOnReader = [visibleCommentIds containsObject:@(comment.commentID)];

comment.depth = ancestors.count;
comment.post = post;
comment.content = [self sanitizeCommentContent:comment.content isPrivateSite:post.isPrivate];
[commentsToKeep addObject:comment];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,7 @@ - (NSFetchRequest *)fetchRequest
NSString *approvedStatus = [Comment descriptionFor:CommentStatusTypeApproved];

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:NSStringFromClass([Comment class])];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"post = %@ AND status = %@", self.post, approvedStatus];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"post = %@ AND status = %@ AND visibleOnReader = %@", self.post, approvedStatus, @YES];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"hierarchy" ascending:YES];
[fetchRequest setSortDescriptors:@[sortDescriptor]];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,16 +245,23 @@ private extension ReaderCommentsViewController {

func moderateComment(_ comment: Comment, status: CommentStatusType) {
let successBlock: (String) -> Void = { [weak self] noticeText in
self?.commentModified = true
self?.refreshAfterCommentModeration()
guard let self = self else {
return
}

// Dismiss any old notices to avoid stacked Undo notices.
self?.dismissNotice()
// when a comment is unapproved/spammed/trashed, ensure that all of the replies are hidden.
self.commentService.updateRepliesVisibility(for: comment) {
self.commentModified = true
self.refreshAfterCommentModeration()

// If the status is Approved, the user has undone a comment moderation.
// So don't show the Undo option in this case.
(status == .approved) ? self?.displayNotice(title: noticeText) :
self?.showActionableNotice(title: noticeText, comment: comment)
// Dismiss any old notices to avoid stacked Undo notices.
self.dismissNotice()

// If the status is Approved, the user has undone a comment moderation.
// So don't show the Undo option in this case.
(status == .approved) ? self.displayNotice(title: noticeText) :
self.showActionableNotice(title: noticeText, comment: comment)
}
}

switch status {
Expand Down
2 changes: 1 addition & 1 deletion WordPress/Classes/WordPress.xcdatamodeld/.xccurrentversion
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>WordPress 137.xcdatamodel</string>
<string>WordPress 138.xcdatamodel</string>
</dict>
</plist>
Loading