session-ios/Session/Conversations V2/Message Cells/TypingIndicatorCellV2.swift

86 lines
2.9 KiB
Swift

// Assumptions
// We'll never encounter an outgoing typing indicator.
// Typing indicators are only sent in contact threads.
final class TypingIndicatorCellV2 : MessageCell {
private var positionInCluster: Position? {
guard let viewItem = viewItem else { return nil }
if viewItem.isFirstInCluster { return .top }
if viewItem.isLastInCluster { return .bottom }
return .middle
}
private var isOnlyMessageInCluster: Bool { viewItem?.isFirstInCluster == true && viewItem?.isLastInCluster == true }
// MARK: UI Components
private lazy var bubbleView: UIView = {
let result = UIView()
result.layer.cornerRadius = VisibleMessageCell.smallCornerRadius
result.backgroundColor = Colors.receivedMessageBackground
return result
}()
private let bubbleViewMaskLayer = CAShapeLayer()
private lazy var typingIndicatorView = TypingIndicatorView()
// MARK: Settings
override class var identifier: String { "TypingIndicatorCell" }
// MARK: Direction & Position
enum Position { case top, middle, bottom }
// MARK: Lifecycle
override func setUpViewHierarchy() {
super.setUpViewHierarchy()
// Bubble view
addSubview(bubbleView)
bubbleView.pin(.left, to: .left, of: self, withInset: VisibleMessageCell.contactThreadHSpacing)
bubbleView.pin(.top, to: .top, of: self, withInset: 1)
// Typing indicator view
bubbleView.addSubview(typingIndicatorView)
typingIndicatorView.pin(to: bubbleView, withInset: 12)
}
// MARK: Updating
override func update() {
guard let viewItem = viewItem, viewItem.interaction is TypingIndicatorInteraction else { return }
// Bubble view
updateBubbleViewCorners()
// Typing indicator view
typingIndicatorView.startAnimation()
}
override func layoutSubviews() {
super.layoutSubviews()
updateBubbleViewCorners()
}
private func updateBubbleViewCorners() {
let maskPath = UIBezierPath(roundedRect: bubbleView.bounds, byRoundingCorners: getCornersToRound(),
cornerRadii: CGSize(width: VisibleMessageCell.largeCornerRadius, height: VisibleMessageCell.largeCornerRadius))
bubbleViewMaskLayer.path = maskPath.cgPath
bubbleView.layer.mask = bubbleViewMaskLayer
}
override func prepareForReuse() {
super.prepareForReuse()
typingIndicatorView.stopAnimation()
}
// MARK: Convenience
private func getCornersToRound() -> UIRectCorner {
guard !isOnlyMessageInCluster else { return .allCorners }
let result: UIRectCorner
switch positionInCluster {
case .top: result = [ .topLeft, .topRight, .bottomRight ]
case .middle: result = [ .topRight, .bottomRight ]
case .bottom: result = [ .topRight, .bottomRight, .bottomLeft ]
case nil: result = .allCorners
}
return result
}
}