FloatChatDisplayView.swift 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. //
  2. // FloatChatDisplayView.swift
  3. // TUIRoomKit
  4. //
  5. // Created by CY zhao on 2024/5/9.
  6. // Copyright © 2024 Tencent. All rights reserved.
  7. //
  8. import UIKit
  9. #if USE_OPENCOMBINE
  10. import OpenCombine
  11. import OpenCombineDispatch
  12. #else
  13. import Combine
  14. #endif
  15. import Factory
  16. protocol FloatChatDisplayViewDelegate: AnyObject {
  17. func getTheLatestUserName(userId: String) -> String
  18. }
  19. class FloatChatDisplayView: UIView {
  20. @Injected(\.floatChatService) private var store: FloatChatStoreProvider
  21. private lazy var messagePublisher = self.store.select(FloatChatSelectors.getLatestMessage)
  22. private var messages: [FloatChatMessageView] = []
  23. var cancellableSet = Set<AnyCancellable>()
  24. private let messageSpacing: CGFloat = 8
  25. weak var delegate: FloatChatDisplayViewDelegate?
  26. private lazy var blurLayer: CALayer = {
  27. let layer = CAGradientLayer()
  28. layer.colors = [
  29. UIColor.black.withAlphaComponent(0).cgColor,
  30. UIColor.black.withAlphaComponent(1).cgColor
  31. ]
  32. layer.locations = [0, 0.2]
  33. layer.startPoint = CGPoint(x: 0.5, y: 0)
  34. layer.endPoint = CGPoint(x: 0.5, y: 1)
  35. return layer
  36. }()
  37. override func layoutSubviews() {
  38. super.layoutSubviews()
  39. blurLayer.frame = self.bounds
  40. }
  41. private var isViewReady = false
  42. override func didMoveToWindow() {
  43. super.didMoveToWindow()
  44. guard !isViewReady else { return }
  45. constructViewHierarchy()
  46. bindInteraction()
  47. reportViewShow()
  48. isViewReady = true
  49. }
  50. private func constructViewHierarchy() {
  51. self.layer.mask = blurLayer
  52. }
  53. func bindInteraction() {
  54. messagePublisher
  55. .filter{ !($0.content.isEmpty && $0.type == .text) }
  56. .receive(on: DispatchQueue.mainQueue)
  57. .sink { [weak self] floatMessage in
  58. guard let self = self else { return }
  59. self.addMessage(floatMessage)
  60. }
  61. .store(in: &cancellableSet)
  62. }
  63. private func reportViewShow() {
  64. store.dispatch(action: FloatChatActions.reportData(payload: .metricsBarragePanelShow))
  65. }
  66. private func addMessage(_ message: FloatChatMessage) {
  67. var message = message
  68. if let userName = delegate?.getTheLatestUserName(userId: message.user.userId), !userName.isEmpty {
  69. message.user.userName = userName
  70. }
  71. let messageView = FloatChatMessageView(floatMessage: message)
  72. if currentMessageHeight() + messageView.height + messageSpacing > bounds.height {
  73. removeOldestMessage()
  74. }
  75. addSubview(messageView)
  76. messageView.snp.makeConstraints { make in
  77. make.leading.equalToSuperview()
  78. make.width.lessThanOrEqualToSuperview()
  79. make.height.lessThanOrEqualToSuperview()
  80. if let lastMessage = messages.last {
  81. make.top.equalTo(lastMessage.snp.bottom).offset(messageSpacing).priority(.high)
  82. }
  83. make.bottom.lessThanOrEqualToSuperview()
  84. }
  85. messages.append(messageView)
  86. DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
  87. self.removeMessageWithAnimation(message: messageView)
  88. }
  89. }
  90. private func currentMessageHeight() -> CGFloat {
  91. return messages.reduce(0) { $0 + $1.height + messageSpacing}
  92. }
  93. private func removeOldestMessage() {
  94. guard let oldest = messages.first else { return }
  95. removeMessage(message: oldest)
  96. }
  97. private func removeMessageWithAnimation(message: FloatChatMessageView) {
  98. UIView.animate(withDuration: 0.3) {
  99. message.alpha = 0
  100. } completion: { _ in
  101. self.removeMessage(message: message)
  102. }
  103. }
  104. private func removeMessage(message: FloatChatMessageView) {
  105. if let index = messages.firstIndex(of: message) {
  106. message.removeFromSuperview()
  107. messages.remove(at: index)
  108. }
  109. }
  110. }
  111. extension FloatChatDisplayView {
  112. override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
  113. return nil
  114. }
  115. }