@@ -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