From c1c56780562f160e22d4291b7a967f8f21dfa4ab Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Thu, 20 Jan 2022 10:48:01 +1100 Subject: [PATCH] implement animation for selected search result message flickering --- Session/Conversations/ConversationVC.swift | 32 ++++++++++++++----- .../Message Cells/VisibleMessageCell.swift | 31 ++++++++++++++++-- Session/Home/GlobalSearch.swift | 2 +- Session/Shared/BaseVC.swift | 2 +- SignalUtilitiesKit/Utilities/UIView+OWS.swift | 10 +++--- 5 files changed, 59 insertions(+), 18 deletions(-) diff --git a/Session/Conversations/ConversationVC.swift b/Session/Conversations/ConversationVC.swift index ea9aaef47..7e654f99a 100644 --- a/Session/Conversations/ConversationVC.swift +++ b/Session/Conversations/ConversationVC.swift @@ -7,7 +7,8 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversationSettingsViewDelegate, ConversationSearchControllerDelegate, UITableViewDataSource, UITableViewDelegate { let isUnsendRequestsEnabled = true // Set to true once unsend requests are done on all platforms let thread: TSThread - let focusedMessageID: String? // This isn't actually used ATM + let focusedMessageID: String? // This is used for global search + var focusedMessageIndexPath: IndexPath? var unreadViewItems: [ConversationViewItem] = [] var scrollButtonConstraint: NSLayoutConstraint? // Search @@ -236,13 +237,17 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat // unreadIndicatorIndex is calculated during loading of the viewItems, so it's // supposed to be accurate. DispatchQueue.main.async { - let firstUnreadMessageIndex = self.viewModel.viewState.unreadIndicatorIndex?.intValue - ?? (self.viewItems.count - self.unreadViewItems.count) - if unreadCount > 0, let viewItem = self.viewItems[ifValid: firstUnreadMessageIndex], let interactionID = viewItem.interaction.uniqueId { - self.scrollToInteraction(with: interactionID, position: .top, isAnimated: false) - self.unreadCountView.alpha = self.scrollButton.alpha + if let focusedMessageID = self.focusedMessageID { + self.scrollToInteraction(with: focusedMessageID, isAnimated: false, highlighted: true) } else { - self.scrollToBottom(isAnimated: false) + let firstUnreadMessageIndex = self.viewModel.viewState.unreadIndicatorIndex?.intValue + ?? (self.viewItems.count - self.unreadViewItems.count) + if unreadCount > 0, let viewItem = self.viewItems[ifValid: firstUnreadMessageIndex], let interactionID = viewItem.interaction.uniqueId { + self.scrollToInteraction(with: interactionID, position: .top, isAnimated: false) + self.unreadCountView.alpha = self.scrollButton.alpha + } else { + self.scrollToBottom(isAnimated: false) + } } self.scrollButton.alpha = self.getScrollButtonOpacity() } @@ -251,6 +256,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + highlightFocusedMessageIfNeeded() didFinishInitialLayout = true markAllAsRead() } @@ -313,6 +319,13 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat } } + private func highlightFocusedMessageIfNeeded() { + if let indexPath = focusedMessageIndexPath, let cell = messagesTableView.cellForRow(at: indexPath) as? VisibleMessageCell { + cell.highlithed() + focusedMessageIndexPath = nil + } + } + @objc func handleKeyboardWillChangeFrameNotification(_ notification: Notification) { guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return } if (newHeight > 0 && baselineKeyboardHeight == 0) { @@ -617,8 +630,11 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat scrollToInteraction(with: interactionID) } - func scrollToInteraction(with interactionID: String, position: UITableView.ScrollPosition = .middle, isAnimated: Bool = true) { + func scrollToInteraction(with interactionID: String, position: UITableView.ScrollPosition = .middle, isAnimated: Bool = true, highlighted: Bool = false) { guard let indexPath = viewModel.ensureLoadWindowContainsInteractionId(interactionID) else { return } messagesTableView.scrollToRow(at: indexPath, at: position, animated: isAnimated) + if highlighted, let _ = messagesTableView.cellForRow(at: indexPath) as? VisibleMessageCell { + focusedMessageIndexPath = indexPath + } } } diff --git a/Session/Conversations/Message Cells/VisibleMessageCell.swift b/Session/Conversations/Message Cells/VisibleMessageCell.swift index c564a598e..7195b981b 100644 --- a/Session/Conversations/Message Cells/VisibleMessageCell.swift +++ b/Session/Conversations/Message Cells/VisibleMessageCell.swift @@ -65,7 +65,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { lazy var bubbleView: UIView = { let result = UIView() - result.layer.cornerRadius = VisibleMessageCell.smallCornerRadius + result.layer.cornerRadius = VisibleMessageCell.largeCornerRadius return result }() @@ -431,10 +431,12 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { } private func updateBubbleViewCorners() { - let maskPath = UIBezierPath(roundedRect: bubbleView.bounds, byRoundingCorners: getCornersToRound(), + let cornersToRound = getCornersToRound() + let maskPath = UIBezierPath(roundedRect: bubbleView.bounds, byRoundingCorners: cornersToRound, cornerRadii: CGSize(width: VisibleMessageCell.largeCornerRadius, height: VisibleMessageCell.largeCornerRadius)) bubbleViewMaskLayer.path = maskPath.cgPath - bubbleView.layer.mask = bubbleViewMaskLayer + bubbleView.layer.cornerRadius = VisibleMessageCell.largeCornerRadius + bubbleView.layer.maskedCorners = getCornerMask(from: cornersToRound) } override func prepareForReuse() { @@ -470,6 +472,16 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { return abs(v.x) > abs(v.y) // It has to be more horizontal than vertical } else { return true + + } + } + + func highlithed() { + bubbleView.setShadow(radius: 6, opacity: 1, offset: .zero, color: Colors.accent.cgColor) + DispatchQueue.main.async { + UIView.animate(withDuration: 2) { + self.bubbleView.setShadow(radius: 0, opacity: 0, offset: .zero, color: UIColor.clear.cgColor) + } } } @@ -571,6 +583,19 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate { return result } + private func getCornerMask(from rectCorner: UIRectCorner) -> CACornerMask { + var cornerMask = CACornerMask() + if rectCorner.contains(.allCorners) { + cornerMask = [ .layerMaxXMinYCorner, .layerMinXMinYCorner, .layerMaxXMaxYCorner, .layerMinXMaxYCorner] + } else { + if rectCorner.contains(.topRight) { cornerMask.insert(.layerMaxXMinYCorner) } + if rectCorner.contains(.topLeft) { cornerMask.insert(.layerMinXMinYCorner) } + if rectCorner.contains(.bottomRight) { cornerMask.insert(.layerMaxXMaxYCorner) } + if rectCorner.contains(.bottomLeft) { cornerMask.insert(.layerMinXMaxYCorner) } + } + return cornerMask + } + private static func getFontSize(for viewItem: ConversationViewItem) -> CGFloat { let baselineFontSize = Values.mediumFontSize switch viewItem.displayableBodyText?.jumbomojiCount { diff --git a/Session/Home/GlobalSearch.swift b/Session/Home/GlobalSearch.swift index b3f244c33..1d478b3ed 100644 --- a/Session/Home/GlobalSearch.swift +++ b/Session/Home/GlobalSearch.swift @@ -156,7 +156,7 @@ extension GlobalSearchViewController { case .messages: let sectionResults = searchResultSet.messages guard let searchResult = sectionResults[safe: indexPath.row], let threadId = searchResult.thread.threadRecord.uniqueId, let thread = TSThread.fetch(uniqueId: threadId) else { return } - show(thread, highlightedMessageID: nil, animated: true) + show(thread, highlightedMessageID: searchResult.messageId, animated: true) } tableView.deselectRow(at: indexPath, animated: true) } diff --git a/Session/Shared/BaseVC.swift b/Session/Shared/BaseVC.swift index 99df46e4b..ee1d1aaaa 100644 --- a/Session/Shared/BaseVC.swift +++ b/Session/Shared/BaseVC.swift @@ -82,7 +82,7 @@ class BaseVC : UIViewController { headingImageView.image = UIImage(named: "SessionHeading")?.withRenderingMode(.alwaysTemplate) headingImageView.contentMode = .scaleAspectFit headingImageView.set(.width, to: 150) - headingImageView.set(.height, to: Values.largeFontSize) + headingImageView.set(.height, to: 18) navigationItem.titleView = headingImageView } diff --git a/SignalUtilitiesKit/Utilities/UIView+OWS.swift b/SignalUtilitiesKit/Utilities/UIView+OWS.swift index 12ff3354b..2e02932ef 100644 --- a/SignalUtilitiesKit/Utilities/UIView+OWS.swift +++ b/SignalUtilitiesKit/Utilities/UIView+OWS.swift @@ -120,11 +120,11 @@ public extension UIView { return constraints } - func setShadow(radius: CGFloat = 2.0, opacity: CGFloat = 0.66, offset: CGPoint = .zero, color: CGColor = UIColor.black.cgColor) { - layer.shadowColor = UIColor.black.cgColor - layer.shadowRadius = 2.0 - layer.shadowOpacity = 0.66 - layer.shadowOffset = .zero + func setShadow(radius: CGFloat = 2.0, opacity: Float = 0.66, offset: CGSize = .zero, color: CGColor = UIColor.black.cgColor) { + layer.shadowColor = color + layer.shadowRadius = radius + layer.shadowOpacity = opacity + layer.shadowOffset = offset } }