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

Notifications > Comment Details: show previous/next comments #17923

Merged
merged 6 commits into from
Feb 14, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import UIKit
import CoreData

@objc protocol CommentDetailsDelegate: AnyObject {

@objc protocol CommentDetailsModerationDelegate: AnyObject {
func nextCommentSelected()
}

protocol CommentDetailsNotificationNavigationDelegate: AnyObject {
func previousNotificationTapped(current: Notification?)
func nextNotificationTapped(current: Notification?)
}

class CommentDetailViewController: UIViewController {

// MARK: Properties
Expand All @@ -18,14 +24,16 @@ class CommentDetailViewController: UIViewController {
private var keyboardManager: KeyboardDismissHelper?
private var dismissKeyboardTapGesture = UITapGestureRecognizer()

@objc weak var delegate: CommentDetailsDelegate?
@objc weak var moderationDelegate: CommentDetailsModerationDelegate?
private var comment: Comment
private var isLastInList = true
private var managedObjectContext: NSManagedObjectContext
private var rows = [RowType]()
private var moderationBar: CommentModerationBar?
private var notification: Notification?

private weak var notificationNavigationDelegate: CommentDetailsNotificationNavigationDelegate?

private var isNotificationComment: Bool {
notification != nil
}
Expand Down Expand Up @@ -175,6 +183,17 @@ class CommentDetailViewController: UIViewController {
return button
}()

var previousButtonEnabled = false {
didSet {
previousButton.isEnabled = previousButtonEnabled
}
}
var nextButtonEnabled = false {
didSet {
nextButton.isEnabled = nextButtonEnabled
}
}

// MARK: Initialization

@objc init(comment: Comment,
Expand All @@ -187,10 +206,12 @@ class CommentDetailViewController: UIViewController {
}

init(comment: Comment,
notification: Notification,
notification: Notification?,
notificationNavigationDelegate: CommentDetailsNotificationNavigationDelegate?,
managedObjectContext: NSManagedObjectContext = ContextManager.sharedInstance().mainContext) {
self.comment = comment
self.notification = notification
self.notificationNavigationDelegate = notificationNavigationDelegate
self.managedObjectContext = managedObjectContext
super.init(nibName: nil, bundle: nil)
}
Expand All @@ -207,6 +228,7 @@ class CommentDetailViewController: UIViewController {
configureReplyView()
setupKeyboardManager()
configureSuggestionsView()
configureNavigationBar()
configureTable()
configureRows()
refreshCommentReplyIfNeeded()
Expand All @@ -224,7 +246,7 @@ class CommentDetailViewController: UIViewController {

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
configureNavigationBar()
configureNavBarButtons()
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
Expand All @@ -238,13 +260,21 @@ class CommentDetailViewController: UIViewController {
tableView.reloadRows(at: [.init(row: contentRowIndex, section: .zero)], with: .fade)
}

@objc func displayComment(_ comment: Comment, isLastInList: Bool) {
// Update the Comment being displayed.
@objc func displayComment(_ comment: Comment, isLastInList: Bool = true) {
self.comment = comment
self.isLastInList = isLastInList
replyTextView?.placeholder = String(format: .replyPlaceholderFormat, comment.authorForDisplay())
refreshData()
refreshCommentReplyIfNeeded()
}

// Update the Notification Comment being displayed.
func refreshView(comment: Comment, notification: Notification?) {
self.notification = notification
displayComment(comment)
}

}

// MARK: - Private Helpers
Expand Down Expand Up @@ -311,8 +341,6 @@ private extension CommentDetailViewController {

navigationController?.navigationBar.isTranslucent = true
title = viewTitle

configureNavBarButtons()
}

/// Updates the navigation bar style based on the `isBlurred` boolean parameter. The intent is to show a visual blur effect when the content is scrolled,
Expand Down Expand Up @@ -432,26 +460,26 @@ private extension CommentDetailViewController {
}

func configureContentCell(_ cell: CommentContentTableViewCell, comment: Comment) {
cell.configure(with: comment) { _ in
self.tableView.performBatchUpdates({})
cell.configure(with: comment) { [weak self] _ in
self?.tableView.performBatchUpdates({})
}

cell.contentLinkTapAction = { url in
cell.contentLinkTapAction = { [weak self] url in
// open all tapped links in web view.
// TODO: Explore reusing URL handling logic from ReaderDetailCoordinator.
self.openWebView(for: url)
self?.openWebView(for: url)
}

cell.accessoryButtonAction = { senderView in
self.shareCommentURL(senderView)
cell.accessoryButtonAction = { [weak self] senderView in
self?.shareCommentURL(senderView)
}

cell.likeButtonAction = {
self.toggleCommentLike()
cell.likeButtonAction = { [weak self] in
self?.toggleCommentLike()
}

cell.replyButtonAction = {
self.showReplyView()
cell.replyButtonAction = { [weak self] in
self?.showReplyView()
}
}

Expand Down Expand Up @@ -614,13 +642,11 @@ private extension CommentDetailViewController {
}

@objc func previousButtonTapped() {
// TODO: handle notification change
DDLogInfo("previousButtonTapped")
notificationNavigationDelegate?.previousNotificationTapped(current: notification)
}

@objc func nextButtonTapped() {
// TODO: handle notification change
DDLogInfo("nextButtonTapped")
notificationNavigationDelegate?.nextNotificationTapped(current: notification)
}

func deleteButtonTapped() {
Expand Down Expand Up @@ -812,7 +838,7 @@ private extension CommentDetailViewController {
}

WPAnalytics.track(.commentSnackbarNext)
delegate?.nextCommentSelected()
moderationDelegate?.nextCommentSelected()
}

struct ModerationMessages {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
static NSString *RestorableBlogIdKey = @"restorableBlogIdKey";
static NSString *RestorableFilterIndexKey = @"restorableFilterIndexKey";

@interface CommentsViewController () <WPTableViewHandlerDelegate, WPContentSyncHelperDelegate, UIViewControllerRestoration, NoResultsViewControllerDelegate, CommentDetailsDelegate>
@interface CommentsViewController () <WPTableViewHandlerDelegate, WPContentSyncHelperDelegate, UIViewControllerRestoration, NoResultsViewControllerDelegate, CommentDetailsModerationDelegate>
@property (nonatomic, strong) WPTableViewHandler *tableViewHandler;
@property (nonatomic, strong) WPContentSyncHelper *syncHelper;
@property (nonatomic, strong) NoResultsViewController *noResultsViewController;
Expand Down Expand Up @@ -233,7 +233,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
self.commentDetailViewController = [[CommentDetailViewController alloc] initWithComment:comment
isLastInList:[self isLastRow:indexPath]
managedObjectContext:[ContextManager sharedInstance].mainContext];
self.commentDetailViewController.delegate = self;
self.commentDetailViewController.moderationDelegate = self;
[self.navigationController pushViewController:self.commentDetailViewController animated:YES];
[CommentAnalytics trackCommentViewedWithComment:comment];
}
Expand Down Expand Up @@ -752,7 +752,7 @@ - (void)actionButtonPressed
[self refreshNoConnectionView];
}

#pragma mark - CommentDetailsDelegate
#pragma mark - CommentDetailsModerationDelegate

- (void)nextCommentSelected
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class NotificationsViewController: UITableViewController, UIViewControllerRestor
///
private var timestampBeforeUpdatesForSecondAlert: String?

private lazy var notificationCommentDetailCoordinator: NotificationCommentDetailCoordinator = {
return NotificationCommentDetailCoordinator(notificationsNavigationDataSource: self)
}()

/// Activity Indicator to be shown when refreshing a Jetpack site status.
///
let activityIndicator: UIActivityIndicatorView = {
Expand Down Expand Up @@ -729,12 +733,14 @@ extension NotificationsViewController {

view.isUserInteractionEnabled = false

DispatchQueue.main.async {
DispatchQueue.main.async { [weak self] in
guard let self = self else {
return
}

if FeatureFlag.notificationCommentDetails.enabled,
note.kind == .comment {
let notificationCommentDetailCoordinator = NotificationCommentDetailCoordinator(notification: note)

notificationCommentDetailCoordinator.createViewController { commentDetailViewController in
self.notificationCommentDetailCoordinator.createViewController(with: note) { commentDetailViewController in
guard let commentDetailViewController = commentDetailViewController else {
// TODO: show error view
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,33 @@ class NotificationCommentDetailCoordinator: NSObject {

// MARK: - Properties

private let notification: Notification
private var notification: Notification?
private var comment: Comment?
private let managedObjectContext = ContextManager.shared.mainContext
private var viewController: CommentDetailViewController?
private var commentID: NSNumber?
private var blog: Blog?

// Arrow navigation data source
private weak var notificationsNavigationDataSource: NotificationsNavigationDataSource?

private lazy var commentService: CommentService = {
return .init(managedObjectContext: managedObjectContext)
}()

// MARK: - Init

init(notification: Notification) {
self.notification = notification
commentID = notification.metaCommentID

if let siteID = notification.metaSiteID {
blog = Blog.lookup(withID: siteID, in: managedObjectContext)
}

init(notificationsNavigationDataSource: NotificationsNavigationDataSource? = nil) {
self.notificationsNavigationDataSource = notificationsNavigationDataSource
super.init()
}

// MARK: - Public Methods

func createViewController(completion: @escaping (CommentDetailViewController?) -> Void) {
func createViewController(with notification: Notification,
completion: @escaping (CommentDetailViewController?) -> Void) {
configure(with: notification)

if let comment = loadCommentFromCache() {
createViewController(comment: comment)
completion(viewController)
Expand All @@ -57,6 +57,15 @@ class NotificationCommentDetailCoordinator: NSObject {

private extension NotificationCommentDetailCoordinator {

func configure(with notification: Notification) {
self.notification = notification
commentID = notification.metaCommentID

if let siteID = notification.metaSiteID {
blog = Blog.lookup(withID: siteID, in: managedObjectContext)
}
}

func loadCommentFromCache() -> Comment? {
guard let commentID = commentID,
let blog = blog else {
Expand Down Expand Up @@ -90,7 +99,90 @@ private extension NotificationCommentDetailCoordinator {
self.comment = comment
viewController = CommentDetailViewController(comment: comment,
notification: notification,
notificationNavigationDelegate: self,
managedObjectContext: managedObjectContext)

updateNavigationButtonStates()
}

func updateViewWith(notification: Notification) {
if notification.kind == .comment {
trackDetailsOpened(for: notification)
configure(with: notification)
refreshViewController()
} else {
// TODO: handle other notification type
}
}

func refreshViewController() {
if let comment = loadCommentFromCache() {
viewController?.refreshView(comment: comment, notification: notification)
updateNavigationButtonStates()
return
}

fetchComment(completion: { comment in
guard let comment = comment else {
// TODO: show error view
return
}

self.viewController?.refreshView(comment: comment, notification: self.notification)
self.updateNavigationButtonStates()
})
}

func updateNavigationButtonStates() {
viewController?.previousButtonEnabled = hasPreviousNotification
viewController?.nextButtonEnabled = hasNextNotification
}

var hasPreviousNotification: Bool {
guard let notification = notification else {
return false
}

return notificationsNavigationDataSource?.notification(preceding: notification) != nil
}

var hasNextNotification: Bool {
guard let notification = notification else {
return false
}
return notificationsNavigationDataSource?.notification(succeeding: notification) != nil
}


func trackDetailsOpened(for notification: Notification) {
let properties = ["notification_type": notification.type ?? "unknown"]
WPAnalytics.track(.openedNotificationDetails, withProperties: properties)
}

}

// MARK: - CommentDetailsNotificationNavigationDelegate

extension NotificationCommentDetailCoordinator: CommentDetailsNotificationNavigationDelegate {

func previousNotificationTapped(current: Notification?) {
guard let current = current,
let previousNotification = notificationsNavigationDataSource?.notification(preceding: current) else {
return
}

WPAnalytics.track(.notificationsPreviousTapped)
updateViewWith(notification: previousNotification)
}

func nextNotificationTapped(current: Notification?) {
guard let current = current,
let nextNotification = notificationsNavigationDataSource?.notification(succeeding: current) else {
return
}

WPAnalytics.track(.notificationsNextTapped)
updateViewWith(notification: nextNotification)
}

}