mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			86 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			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 TypingIndicatorCell : 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
 | 
						|
    }
 | 
						|
}
 |