diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 0be968bb772f..d099e643848f 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -11,6 +11,7 @@ * [*] Fix layout issues in Privacy Settings section of App Settings [#23936] * [*] Fix incorrect chevron icons direction in RTL languages [#23940] * [*] Fix an issue with clear navigation bar background in revision browser [#23941] +* [*] Fix an issue with comments being lost on request failure [#23942] 25.6 ----- diff --git a/WordPress/Classes/ViewRelated/Comments/Controllers/CommentDetailViewController.swift b/WordPress/Classes/ViewRelated/Comments/Controllers/CommentDetailViewController.swift index 7b0a4c918050..679cfb7551e6 100644 --- a/WordPress/Classes/ViewRelated/Comments/Controllers/CommentDetailViewController.swift +++ b/WordPress/Classes/ViewRelated/Comments/Controllers/CommentDetailViewController.swift @@ -1068,13 +1068,15 @@ private extension CommentDetailViewController { return } + replyTextView?.setShowingLoadingIndicator(true) + commentService.createReply(for: comment, content: content) { reply in self.commentService.uploadComment(reply, success: { [weak self] in - self?.displayReplyNotice(success: true) + self?.didSendReply(success: true) self?.refreshCommentReplyIfNeeded() }, failure: { [weak self] error in DDLogError("Failed uploading comment reply: \(String(describing: error))") - self?.displayReplyNotice(success: false) + self?.didSendReply(success: false, error: error) }) } } @@ -1084,21 +1086,30 @@ private extension CommentDetailViewController { return } + replyTextView?.setShowingLoadingIndicator(true) + commentService.replyToHierarchicalComment(withID: NSNumber(value: comment.commentID), post: post, content: content, success: { [weak self] in - self?.displayReplyNotice(success: true) + self?.didSendReply(success: true) self?.refreshCommentReplyIfNeeded() }, failure: { [weak self] error in DDLogError("Failed creating post comment reply: \(String(describing: error))") - self?.displayReplyNotice(success: false) + self?.didSendReply(success: false, error: error) }) } - func displayReplyNotice(success: Bool) { + func didSendReply(success: Bool, error: Error? = nil) { + replyTextView?.setShowingLoadingIndicator(false) + if success { + replyTextView?.text = "" + } else { + replyTextView?.becomeFirstResponder() + } + let message = success ? ReplyMessages.successMessage : ReplyMessages.failureMessage - displayNotice(title: message) + displayNotice(title: message, message: error?.localizedDescription) } func configureSuggestionsView() { diff --git a/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift b/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift index 27a99751b05b..006525b144ff 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationDetailsViewController.swift @@ -417,12 +417,15 @@ extension NotificationDetailsViewController { replyTextView.accessibilityIdentifier = .replyTextViewAccessibilityId replyTextView.accessibilityLabel = NSLocalizedString("Reply Text", comment: "Notifications Reply Accessibility Identifier") replyTextView.delegate = self - replyTextView.onReply = { [weak self] content in - let group = self?.note.contentGroup(ofKind: .comment) + replyTextView.onReply = { [weak self, weak replyTextView] content in + guard let self, let replyTextView else { + return + } + let group = self.note.contentGroup(ofKind: .comment) guard let block: FormattableCommentContent = group?.blockOfKind(.comment) else { return } - self?.replyCommentWithBlock(block, content: content) + self.replyCommentWithBlock(block, content: content, textView: replyTextView) } replyTextView.setContentCompressionResistancePriority(.required, for: .vertical) @@ -1085,26 +1088,30 @@ private extension NotificationDetailsViewController { _ = navigationController?.popToRootViewController(animated: true) } - func replyCommentWithBlock(_ block: FormattableCommentContent, content: String) { + func replyCommentWithBlock(_ block: FormattableCommentContent, content: String, textView: ReplyTextView) { guard let replyAction = block.action(id: ReplyToCommentAction.actionIdentifier()) else { return } let generator = UINotificationFeedbackGenerator() generator.prepare() - generator.notificationOccurred(.success) let actionContext = ActionContext(block: block, content: content) { [weak self] (request, success) in + textView.setShowingLoadingIndicator(false) if success { + generator.notificationOccurred(.success) WPAppAnalytics.track(.notificationsCommentRepliedTo) + textView.text = "" let message = NSLocalizedString("Reply Sent!", comment: "The app successfully sent a comment") self?.displayNotice(title: message) } else { generator.notificationOccurred(.error) + textView.becomeFirstResponder() self?.displayReplyError(with: block, content: content) } } + textView.setShowingLoadingIndicator(true) replyAction.execute(context: actionContext) } diff --git a/WordPress/Classes/ViewRelated/Notifications/ReplyTextView/ReplyTextView.swift b/WordPress/Classes/ViewRelated/Notifications/ReplyTextView/ReplyTextView.swift index 003e65a7ff59..924ddcb1c32a 100644 --- a/WordPress/Classes/ViewRelated/Notifications/ReplyTextView/ReplyTextView.swift +++ b/WordPress/Classes/ViewRelated/Notifications/ReplyTextView/ReplyTextView.swift @@ -143,30 +143,14 @@ import Gridicons // MARK: - IBActions @IBAction fileprivate func btnReplyPressed() { - guard let handler = onReply else { - return - } - - // We can't reply without an internet connection - let appDelegate = WordPressAppDelegate.shared - guard appDelegate!.connectionAvailable else { - let title = NSLocalizedString("No Connection", comment: "Title of error prompt when no internet connection is available.") - let message = NSLocalizedString("The Internet connection appears to be offline.", - comment: "Message of error prompt shown when a user tries to perform an action without an internet connection.") - WPError.showAlert(withTitle: title, message: message) - textView.resignFirstResponder() - return - } + guard let onReply else { return } // Load the new text let newText = textView.text textView.resignFirstResponder() - // Cleanup + Shrink - text = String() - // Hit the handler - handler(newText!) + onReply(newText ?? "") } @IBAction fileprivate func btnEnterFullscreenPressed(_ sender: Any) { @@ -286,8 +270,12 @@ import Gridicons comment: "Accessibility Label for the enter full screen button on the comment reply text view") // Reply button - replyButton.setTitleColor(UIAppColor.brand, for: .normal) - replyButton.titleLabel?.text = NSLocalizedString("Reply", comment: "Reply to a comment.") + replyButton.configuration = { + var configuration = UIButton.Configuration.plain() + configuration.baseForegroundColor = UIAppColor.brand + configuration.title = NSLocalizedString("Reply", comment: "Reply to a comment.") + return configuration + }() replyButton.accessibilityIdentifier = "reply-button" replyButton.accessibilityLabel = NSLocalizedString("Reply", comment: "Accessibility label for the reply button") refreshReplyButton() @@ -308,6 +296,16 @@ import Gridicons frame.size.height = minimumHeight } + @objc func setShowingLoadingIndicator(_ isLoading: Bool) { + isUserInteractionEnabled = !isLoading + + textView.alpha = isLoading ? 0.33 : 1.0 + + replyButton.isEnabled = !isLoading + replyButton.configuration?.title = isLoading ? nil : NSLocalizedString("Reply", comment: "Reply to a comment.") + replyButton.configuration?.showsActivityIndicator = isLoading + } + // MARK: - Refresh Helpers fileprivate func refreshInterface() { refreshPlaceholder() diff --git a/WordPress/Classes/ViewRelated/Notifications/ReplyTextView/ReplyTextView.xib b/WordPress/Classes/ViewRelated/Notifications/ReplyTextView/ReplyTextView.xib index bff11aee058d..873787813b75 100644 --- a/WordPress/Classes/ViewRelated/Notifications/ReplyTextView/ReplyTextView.xib +++ b/WordPress/Classes/ViewRelated/Notifications/ReplyTextView/ReplyTextView.xib @@ -1,9 +1,9 @@ - + - + @@ -102,7 +102,6 @@ - diff --git a/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m b/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m index 2caa6da68a6d..e0610eaedf52 100644 --- a/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m +++ b/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.m @@ -916,6 +916,9 @@ - (void)sendReplyWithNewContent:(NSString *)content NSString *successMessage = NSLocalizedString(@"Reply Sent!", @"The app successfully sent a comment"); [weakSelf displayNoticeWithTitle:successMessage message:nil]; + [weakSelf.replyTextView setShowingLoadingIndicator:NO]; + weakSelf.replyTextView.text = @""; + [weakSelf trackReplyTo:replyToComment]; [weakSelf.tableView deselectSelectedRowWithAnimation:YES]; [weakSelf refreshReplyTextViewPlaceholder]; @@ -931,11 +934,16 @@ - (void)sendReplyWithNewContent:(NSString *)content DDLogError(@"Error sending reply: %@", error); [generator notificationOccurred:UINotificationFeedbackTypeError]; NSString *message = NSLocalizedString(@"There has been an unexpected error while sending your reply", "Reply Failure Message"); - [weakSelf displayNoticeWithTitle:message message:nil]; + [weakSelf.replyTextView setShowingLoadingIndicator:NO]; + [weakSelf displayNoticeWithTitle:message message:[error localizedDescription]]; + + [weakSelf.replyTextView becomeFirstResponder]; [weakSelf refreshTableViewAndNoResultsView:NO]; }; + [self.replyTextView setShowingLoadingIndicator:YES]; + CommentService *service = [[CommentService alloc] initWithCoreDataStack:[ContextManager sharedInstance]]; if (replyToComment) {