session-ios/Session/Conversations/Message Cells/Content Views/ReactionContainerView.swift

183 lines
5.7 KiB
Swift

// 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)
}
}