Skip to content

Commit 2fbc7d4

Browse files
committed
Reader: Fix an issue with navigateToCommentID failing to find comments
1 parent 0f0a55b commit 2fbc7d4

4 files changed

Lines changed: 114 additions & 7 deletions

File tree

WordPress/Classes/Services/CommentService.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@import WordPressShared;
99

10-
NSUInteger const WPTopLevelHierarchicalCommentsPerPage = 20;
10+
NSUInteger const WPTopLevelHierarchicalCommentsPerPage = 40;
1111
NSInteger const WPNumberOfCommentsToSync = 100;
1212
static NSTimeInterval const CommentsRefreshTimeoutInSeconds = 60 * 5; // 5 minutes
1313

WordPress/Classes/ViewRelated/Comments/Views/Detail/CommentContentTableViewCell.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,24 @@ final class CommentContentTableViewCell: UITableViewCell, NibReusable {
205205
containerStackTrailingConstraint.constant = 0
206206
}
207207

208+
/// Emphasizes the cell and draws the user's attention with a flash that settles at the target highlight level.
209+
func flashHighlight() {
210+
isEmphasized = true
211+
212+
guard let highlightedBackgroundView else {
213+
return wpAssertionFailure("background view not configured")
214+
}
215+
216+
let originalBackgroundColor = highlightedBackgroundView.backgroundColor
217+
UIView.animate(withDuration: 0.33, delay: 0.5, options: .curveEaseOut) {
218+
highlightedBackgroundView.backgroundColor = UIAppColor.blue.withAlphaComponent(0.09)
219+
} completion: { _ in
220+
UIView.animate(withDuration: 0.33, delay: 1.5, options: .curveEaseOut) {
221+
highlightedBackgroundView.backgroundColor = originalBackgroundColor
222+
}
223+
}
224+
}
225+
208226
func hideActions() {
209227
replyButton.isHidden = true
210228
likeButton.isHidden = true

WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsTableViewController.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ final class ReaderCommentsTableViewController: UIViewController, UITableViewData
9696
}
9797

9898
@objc func scrollToComment(withID commentID: NSNumber) -> Bool {
99+
scrollToComment(withID: commentID, animated: true)
100+
}
101+
102+
func scrollToComment(withID commentID: NSNumber, animated: Bool) -> Bool {
99103
let comments = fetchResultsController.fetchedObjects ?? []
100104
guard let comment = comments.first(where: { $0.commentID == commentID.int32Value }) else {
101105
return false

WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.swift

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ final class ReaderCommentsViewController: UIViewController, WPContentSyncHelperD
4343
private var commentModified = false
4444
private var highlightedIndexPath: IndexPath?
4545

46+
private var navigationOverlayView: UIView?
47+
private var navigationPagesLoaded = 0
48+
private var navigationCommentID: Int32?
49+
private var onNavigationCommentRendered: (() -> Void)?
50+
4651
private var syncHelper: WPContentSyncHelper?
4752
private var followCommentsService: FollowCommentsService?
4853
private var readerCommentsFollowPresenter: ReaderCommentsFollowPresenter?
@@ -101,6 +106,12 @@ final class ReaderCommentsViewController: UIViewController, WPContentSyncHelperD
101106
if commentModified {
102107
NotificationCenter.default.post(name: .ReaderCommentModifiedNotification, object: nil)
103108
}
109+
110+
navigationCommentID = nil
111+
onNavigationCommentRendered = nil
112+
navigationOverlayView?.removeFromSuperview()
113+
navigationOverlayView = nil
114+
navigationPagesLoaded = 0
104115
}
105116

106117
override func viewDidLayoutSubviews() {
@@ -362,7 +373,8 @@ final class ReaderCommentsViewController: UIViewController, WPContentSyncHelperD
362373
let isModerationEnabled = comment.allowsModeration()
363374
cell.accessoryButton.showsMenuAsPrimaryAction = isModerationEnabled
364375
cell.accessoryButton.menu = isModerationEnabled ? menu(for: comment, indexPath: indexPath, tableView: tableView, sourceView: cell.accessoryButton) : nil
365-
cell.configure(viewModel: viewModel, helper: helper) { [weak tableView] _ in
376+
let commentID = comment.commentID
377+
cell.configure(viewModel: viewModel, helper: helper) { [weak self, weak tableView] _ in
366378
guard let tableView else { return }
367379

368380
if tableView.alpha == 0 {
@@ -373,6 +385,7 @@ final class ReaderCommentsViewController: UIViewController, WPContentSyncHelperD
373385
UIView.setAnimationsEnabled(false)
374386
tableView.performBatchUpdates({})
375387
UIView.setAnimationsEnabled(true)
388+
self?.commentRenderedIfNeeded(commentID: commentID)
376389
}
377390

378391
cell.isEmphasized = indexPath == highlightedIndexPath
@@ -427,15 +440,87 @@ final class ReaderCommentsViewController: UIViewController, WPContentSyncHelperD
427440
self.highlightedIndexPath = indexPath
428441
}
429442

443+
// Shows an overlay while searching for the target comment across pages (up to 5).
444+
// Once found, waits for the cell's WebKit content to finish rendering, then
445+
// fades the overlay out and flashes the cell to draw the user's attention.
430446
private func navigateToCommentIDIfNeeded() {
431-
guard let navigateToCommentID, let tableVC else {
432-
return
447+
guard let navigateToCommentID, let tableVC else { return }
448+
449+
showNavigationOverlay()
450+
451+
if tableVC.scrollToComment(withID: navigateToCommentID, animated: false) {
452+
let commentID = navigateToCommentID.int32Value
453+
self.navigateToCommentID = nil
454+
self.navigationPagesLoaded = 0
455+
setupNavigationReveal(commentID: commentID, in: tableVC)
456+
} else if navigationPagesLoaded < 5, let syncHelper, syncHelper.hasMoreContent {
457+
navigationPagesLoaded += 1
458+
syncHelper.syncMoreContent()
459+
} else {
460+
self.navigateToCommentID = nil
461+
self.navigationPagesLoaded = 0
462+
hideNavigationOverlay(completion: nil)
433463
}
434-
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
435-
if tableVC.scrollToComment(withID: navigateToCommentID) {
436-
self.navigateToCommentID = nil
464+
}
465+
466+
private func setupNavigationReveal(commentID: Int32, in tableVC: ReaderCommentsTableViewController) {
467+
navigationCommentID = commentID
468+
469+
let reveal: () -> Void = { [weak self, weak tableVC] in
470+
guard let self, let tableVC, self.navigationCommentID == commentID else { return }
471+
self.navigationCommentID = nil
472+
self.onNavigationCommentRendered = nil
473+
474+
guard let indexPath = self.highlightedIndexPath else { return }
475+
tableVC.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
476+
477+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.33) { [weak self, weak tableVC] in
478+
guard let self, let tableVC else { return }
479+
self.hideNavigationOverlay {
480+
(tableVC.tableView.cellForRow(at: indexPath) as? CommentContentTableViewCell)?.flashHighlight()
481+
}
437482
}
438483
}
484+
485+
onNavigationCommentRendered = reveal
486+
487+
// Safety timeout in case the render callback never fires
488+
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
489+
guard self?.navigationCommentID == commentID else { return }
490+
reveal()
491+
}
492+
}
493+
494+
func commentRenderedIfNeeded(commentID: Int32) {
495+
guard commentID == navigationCommentID else { return }
496+
onNavigationCommentRendered?()
497+
}
498+
499+
private func showNavigationOverlay() {
500+
guard navigationOverlayView == nil else { return }
501+
let overlay = UIView()
502+
overlay.backgroundColor = .systemBackground
503+
let spinner = UIActivityIndicatorView(style: .medium)
504+
spinner.startAnimating()
505+
overlay.addSubview(spinner)
506+
spinner.pinCenter()
507+
view.addSubview(overlay)
508+
overlay.pinEdges()
509+
navigationOverlayView = overlay
510+
}
511+
512+
private func hideNavigationOverlay(completion: (() -> Void)?) {
513+
guard let overlay = navigationOverlayView else {
514+
completion?()
515+
return
516+
}
517+
navigationOverlayView = nil
518+
UIView.animate(withDuration: 0.33) {
519+
overlay.alpha = 0
520+
} completion: { _ in
521+
overlay.removeFromSuperview()
522+
completion?()
523+
}
439524
}
440525

441526
// MARK: - Tracking

0 commit comments

Comments
 (0)