Swiftgram/submodules/ChatTitleActivityNode/Sources/ChatTitleActivityNode.swift
2021-08-18 00:47:36 +04:00

133 lines
4.9 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
public enum ChatTitleActivityAnimationStyle {
case none
case crossfade
case slide
}
public enum ChatTitleActivityInfoType {
case online
case lastSeenTime
case generic
}
public enum ChatTitleActivityNodeState: Equatable {
case none
case info(NSAttributedString, ChatTitleActivityInfoType)
case typingText(NSAttributedString, UIColor)
case uploading(NSAttributedString, UIColor)
case recordingVoice(NSAttributedString, UIColor)
case recordingVideo(NSAttributedString, UIColor)
case playingGame(NSAttributedString, UIColor)
case choosingSticker(NSAttributedString, UIColor)
func contentNode() -> ChatTitleActivityContentNode? {
switch self {
case .none:
return nil
case let .info(text, _):
return ChatTitleActivityContentNode(text: text)
case let .typingText(text, color):
return ChatTypingActivityContentNode(text: text, color: color)
case let .uploading(text, color):
return ChatUploadingActivityContentNode(text: text, color: color)
case let .recordingVoice(text, color):
return ChatRecordingVoiceActivityContentNode(text: text, color: color)
case let .recordingVideo(text, color):
return ChatRecordingVideoActivityContentNode(text: text, color: color)
case let .playingGame(text, color):
return ChatPlayingActivityContentNode(text: text, color: color)
case let .choosingSticker(text, color):
return ChatChoosingStickerActivityContentNode(text: text, color: color)
}
}
public var string: String? {
if case let .info(text, _) = self {
return text.string
}
return nil
}
}
public class ChatTitleActivityNode: ASDisplayNode {
public private(set) var state: ChatTitleActivityNodeState = .none
private var contentNode: ChatTitleActivityContentNode?
private var nextContentNode: ChatTitleActivityContentNode?
override public init() {
super.init()
}
public func makeCopy() -> ASDisplayNode {
let node = ASDisplayNode()
if let contentNode = self.contentNode {
node.addSubnode(contentNode.makeCopy())
}
node.frame = self.frame
return node
}
public func transitionToState(_ state: ChatTitleActivityNodeState, animation: ChatTitleActivityAnimationStyle = .crossfade, completion: @escaping () -> Void = {}) -> Bool {
if self.state != state {
let currentState = self.state
self.state = state
let contentNode = state.contentNode()
if contentNode !== self.contentNode {
self.transitionToContentNode(contentNode, state: state, fromState: currentState, animation: animation, completion: completion)
}
return true
} else {
completion()
return false
}
}
private func transitionToContentNode(_ node: ChatTitleActivityContentNode?, state: ChatTitleActivityNodeState, fromState: ChatTitleActivityNodeState, animation: ChatTitleActivityAnimationStyle = .crossfade, completion: @escaping () -> Void) {
if let previousContentNode = self.contentNode {
if case .none = animation {
previousContentNode.removeFromSupernode()
self.contentNode = node
if let contentNode = self.contentNode {
self.addSubnode(contentNode)
}
} else {
var animation = animation
if case let .info(_, fromType) = fromState, case let .info(_, toType) = state, fromType == toType {
animation = .none
}
if case .typingText = fromState, case .typingText = state {
animation = .none
}
self.contentNode = node
if let contentNode = self.contentNode {
self.addSubnode(contentNode)
if self.isNodeLoaded {
contentNode.animateIn(from: fromState, style: animation)
}
}
previousContentNode.animateOut(to: state, style: animation) {
previousContentNode.removeFromSupernode()
}
}
} else {
self.contentNode = node
if let contentNode = self.contentNode {
self.addSubnode(contentNode)
}
}
}
public func updateLayout(_ constrainedSize: CGSize, offset: CGFloat = 0.0, alignment: NSTextAlignment) -> CGSize {
return CGSize(width: 0.0, height: self.contentNode?.updateLayout(constrainedSize, offset: offset, alignment: alignment).height ?? 0.0)
}
}