|  |  |  | // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import UIKit | 
					
						
							|  |  |  | import SessionUIKit | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | final class ReactionContainerView: UIView { | 
					
						
							|  |  |  |     var showingAllReactions = false | 
					
						
							|  |  |  |     private var isOutgoingMessage = false | 
					
						
							|  |  |  |     private var showNumbers = true | 
					
						
							|  |  |  |     private var maxEmojisPerLine = isIPhone6OrSmaller ? 5 : 6 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     var reactions: [ReactionViewModel] = [] | 
					
						
							|  |  |  |     var reactionViews: [ReactionButton] = [] | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - UI | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var mainStackView: UIStackView = { | 
					
						
							|  |  |  |         let result = UIStackView(arrangedSubviews: [ reactionContainerView ]) | 
					
						
							|  |  |  |         result.axis = .vertical | 
					
						
							|  |  |  |         result.spacing = Values.smallSpacing | 
					
						
							|  |  |  |         result.alignment = .center | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var reactionContainerView: UIStackView = { | 
					
						
							|  |  |  |         let result = UIStackView() | 
					
						
							|  |  |  |         result.axis = .vertical | 
					
						
							|  |  |  |         result.spacing = Values.smallSpacing | 
					
						
							|  |  |  |         result.alignment = .leading | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     var expandButton: ExpandingReactionButton? | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     var collapseButton: UIStackView = { | 
					
						
							|  |  |  |         let arrow = UIImageView(image: UIImage(named: "ic_chevron_up")?.resizedImage(to: CGSize(width: 15, height: 13))?.withRenderingMode(.alwaysTemplate)) | 
					
						
							|  |  |  |         arrow.tintColor = Colors.text | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         let textLabel = UILabel() | 
					
						
							|  |  |  |         textLabel.text = "Show less" | 
					
						
							|  |  |  |         textLabel.font = .systemFont(ofSize: Values.verySmallFontSize) | 
					
						
							|  |  |  |         textLabel.textColor = Colors.text | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         let result = UIStackView(arrangedSubviews: [ UIView.hStretchingSpacer(), arrow, textLabel, UIView.hStretchingSpacer() ]) | 
					
						
							|  |  |  |         result.spacing = Values.verySmallSpacing | 
					
						
							|  |  |  |         result.alignment = .center | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - Lifecycle | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     init() { | 
					
						
							|  |  |  |         super.init(frame: CGRect.zero) | 
					
						
							|  |  |  |         setUpViewHierarchy() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     override init(frame: CGRect) { | 
					
						
							|  |  |  |         preconditionFailure("Use init(viewItem:textColor:) instead.") | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     required init?(coder: NSCoder) { | 
					
						
							|  |  |  |         preconditionFailure("Use init(viewItem:textColor:) instead.") | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private func setUpViewHierarchy() { | 
					
						
							|  |  |  |         addSubview(mainStackView) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         mainStackView.pin(to: self) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public func update(_ reactions: [ReactionViewModel], isOutgoingMessage: Bool, showNumbers: Bool) { | 
					
						
							|  |  |  |         self.reactions = reactions | 
					
						
							|  |  |  |         self.isOutgoingMessage = isOutgoingMessage | 
					
						
							|  |  |  |         self.showNumbers = showNumbers | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         prepareForUpdate() | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if showingAllReactions { | 
					
						
							|  |  |  |             updateAllReactions() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             updateCollapsedReactions(reactions) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private func updateCollapsedReactions(_ reactions: [ReactionViewModel]) { | 
					
						
							|  |  |  |         let stackView = UIStackView() | 
					
						
							|  |  |  |         stackView.axis = .horizontal | 
					
						
							|  |  |  |         stackView.spacing = Values.smallSpacing | 
					
						
							|  |  |  |         stackView.alignment = .center | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if isOutgoingMessage { | 
					
						
							|  |  |  |             stackView.semanticContentAttribute = .forceRightToLeft | 
					
						
							|  |  |  |             reactionContainerView.semanticContentAttribute = .forceRightToLeft | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             stackView.semanticContentAttribute = .unspecified | 
					
						
							|  |  |  |             reactionContainerView.semanticContentAttribute = .unspecified | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         var displayedReactions: [ReactionViewModel] | 
					
						
							|  |  |  |         var expandButtonReactions: [EmojiWithSkinTones] | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if reactions.count > maxEmojisPerLine { | 
					
						
							|  |  |  |             displayedReactions = Array(reactions[0...(maxEmojisPerLine - 3)]) | 
					
						
							|  |  |  |             expandButtonReactions = Array(reactions[(maxEmojisPerLine - 2)...maxEmojisPerLine]) | 
					
						
							|  |  |  |                 .map { $0.emoji } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             displayedReactions = reactions | 
					
						
							|  |  |  |             expandButtonReactions = [] | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         for reaction in displayedReactions { | 
					
						
							|  |  |  |             let reactionView = ReactionButton(viewModel: reaction, showNumber: showNumbers) | 
					
						
							|  |  |  |             stackView.addArrangedSubview(reactionView) | 
					
						
							|  |  |  |             reactionViews.append(reactionView) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if expandButtonReactions.count > 0 { | 
					
						
							|  |  |  |             let expandButton: ExpandingReactionButton = ExpandingReactionButton(emojis: expandButtonReactions) | 
					
						
							|  |  |  |             stackView.addArrangedSubview(expandButton) | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             self.expandButton = expandButton | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             expandButton = nil | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         reactionContainerView.addArrangedSubview(stackView) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private func updateAllReactions() { | 
					
						
							|  |  |  |         var reactions = self.reactions | 
					
						
							|  |  |  |         var numberOfLines = 0 | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         while reactions.count > 0 { | 
					
						
							|  |  |  |             var line: [ReactionViewModel] = [] | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             while reactions.count > 0 && line.count < maxEmojisPerLine { | 
					
						
							|  |  |  |                 line.append(reactions.removeFirst()) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |             updateCollapsedReactions(line) | 
					
						
							|  |  |  |             numberOfLines += 1 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if numberOfLines > 1 { | 
					
						
							|  |  |  |             mainStackView.addArrangedSubview(collapseButton) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else { | 
					
						
							|  |  |  |             showingAllReactions = false | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private func prepareForUpdate() { | 
					
						
							|  |  |  |         for subview in reactionContainerView.arrangedSubviews { | 
					
						
							|  |  |  |             reactionContainerView.removeArrangedSubview(subview) | 
					
						
							|  |  |  |             subview.removeFromSuperview() | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         mainStackView.removeArrangedSubview(collapseButton) | 
					
						
							|  |  |  |         collapseButton.removeFromSuperview() | 
					
						
							|  |  |  |         reactionViews = [] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public func showAllEmojis() { | 
					
						
							|  |  |  |         guard !showingAllReactions else { return } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         showingAllReactions = true | 
					
						
							|  |  |  |         update(reactions, isOutgoingMessage: isOutgoingMessage, showNumbers: showNumbers) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     public func showLessEmojis() { | 
					
						
							|  |  |  |         guard showingAllReactions else { return } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         showingAllReactions = false | 
					
						
							|  |  |  |         update(reactions, isOutgoingMessage: isOutgoingMessage, showNumbers: showNumbers) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 |