TopViewModel.swift 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. //
  2. // TopViewModel.swift
  3. // TUIRoomKit
  4. //
  5. // Created by janejntang on 2022/12/30.
  6. // Copyright © 2022 Tencent. All rights reserved.
  7. //
  8. import Foundation
  9. import RTCRoomEngine
  10. protocol TopViewModelResponder: AnyObject {
  11. func updateTimerLabel(text: String)
  12. func updateStackView(item: ButtonItemData)
  13. func updateMeetingNameLabel(_ text: String)
  14. func addStackSubview(item: ButtonItemData)
  15. #if RTCube_APPSTORE
  16. func showReportView()
  17. #endif
  18. }
  19. class TopViewModel: NSObject {
  20. private var topMenuTimer: DispatchSourceTimer?
  21. private(set) var viewItems: [ButtonItemData] = []
  22. var engineManager: EngineManager {
  23. return EngineManager.shared
  24. }
  25. var store: RoomStore {
  26. return engineManager.store
  27. }
  28. weak var viewResponder: TopViewModelResponder?
  29. var roomInfo: TUIRoomInfo {
  30. engineManager.store.roomInfo
  31. }
  32. var currentUser: UserEntity {
  33. engineManager.store.currentUser
  34. }
  35. override init() {
  36. super.init()
  37. createBottomData()
  38. initialStatus()
  39. subscribeUIEvent()
  40. updateTimerLabelText()
  41. }
  42. private func createBottomData() {
  43. let micItem = ButtonItemData()
  44. micItem.normalIcon = "room_earpiece"
  45. micItem.selectedIcon = "room_speakerphone"
  46. micItem.backgroundColor = UIColor(0xA3AEC7)
  47. micItem.resourceBundle = tuiRoomKitBundle()
  48. micItem.isSelect = engineManager.store.audioSetting.isSoundOnSpeaker
  49. micItem.action = { [weak self] sender in
  50. guard let self = self, let button = sender as? UIButton else { return }
  51. self.switchMicItemAction(sender: button)
  52. }
  53. viewItems.append(micItem)
  54. let cameraItem = ButtonItemData()
  55. cameraItem.normalIcon = "room_switch_camera"
  56. cameraItem.backgroundColor = UIColor(0xA3AEC7)
  57. cameraItem.resourceBundle = tuiRoomKitBundle()
  58. cameraItem.buttonType = .switchCamaraItemType
  59. cameraItem.isHidden = !currentUser.hasVideoStream
  60. cameraItem.action = { [weak self] sender in
  61. guard let self = self, let button = sender as? UIButton else { return }
  62. self.switchCameraItemAction(sender: button)
  63. }
  64. viewItems.append(cameraItem)
  65. #if RTCube_APPSTORE
  66. injectReport()
  67. #endif
  68. }
  69. private func initialStatus() {
  70. if engineManager.store.audioSetting.isSoundOnSpeaker {
  71. engineManager.setAudioRoute(isSoundOnSpeaker: true)
  72. } else {
  73. engineManager.setAudioRoute(isSoundOnSpeaker: false)
  74. }
  75. }
  76. private func subscribeUIEvent() {
  77. EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_CurrentUserHasVideoStream, responder: self)
  78. EngineEventCenter.shared.subscribeEngine(event: .onConferenceInfoChanged, observer: self)
  79. EngineEventCenter.shared.subscribeEngine(event: .onStartedRoom, observer: self)
  80. EngineEventCenter.shared.subscribeEngine(event: .onJoinedRoom, observer: self)
  81. EngineEventCenter.shared.subscribeEngine(event: .onInitialRoomInfo, observer: self)
  82. EngineEventCenter.shared.subscribeEngine(event: .onUserInfoChanged, observer: self)
  83. EngineEventCenter.shared.subscribeEngine(event: .onInitialSelfUserInfo, observer: self)
  84. }
  85. private func unsubscribeUIEvent() {
  86. EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_CurrentUserHasVideoStream, responder: self)
  87. EngineEventCenter.shared.unsubscribeEngine(event: .onConferenceInfoChanged, observer: self)
  88. EngineEventCenter.shared.unsubscribeEngine(event: .onStartedRoom, observer: self)
  89. EngineEventCenter.shared.unsubscribeEngine(event: .onJoinedRoom, observer: self)
  90. EngineEventCenter.shared.unsubscribeEngine(event: .onInitialRoomInfo, observer: self)
  91. EngineEventCenter.shared.unsubscribeEngine(event: .onUserInfoChanged, observer: self)
  92. EngineEventCenter.shared.unsubscribeEngine(event: .onInitialSelfUserInfo, observer: self)
  93. }
  94. private func switchMicItemAction(sender: UIButton) {
  95. EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": true])
  96. sender.isSelected = !sender.isSelected
  97. if sender.isSelected {
  98. engineManager.setAudioRoute(isSoundOnSpeaker: true)
  99. } else {
  100. engineManager.setAudioRoute(isSoundOnSpeaker: false)
  101. }
  102. }
  103. private func switchCameraItemAction(sender: UIButton) {
  104. EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": true])
  105. engineManager.switchCamera()
  106. }
  107. private func updateTimer(totalSeconds: UInt) {
  108. let second: UInt = totalSeconds % 60
  109. let minute: UInt = (totalSeconds / 60) % 60
  110. let hour: UInt = totalSeconds / 3_600
  111. var timerText: String
  112. if hour > 0 {
  113. timerText = String(format: "%.2d:%.2d:%.2d", hour, minute, second)
  114. } else {
  115. timerText = String(format: "%.2d:%.2d", minute, second)
  116. }
  117. self.viewResponder?.updateTimerLabel(text: timerText)
  118. }
  119. func dropDownAction(sender: UIView) {
  120. RoomRouter.shared.presentPopUpViewController(viewType: .roomInfoViewType, height: 290.scale375Height())
  121. }
  122. func exitAction(sender: UIView) {
  123. EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ShowExitRoomView, param: [:])
  124. }
  125. func updateTimerLabelText() {
  126. let timeInterval: TimeInterval = Date().timeIntervalSince1970
  127. let timeStamp = Int(timeInterval)
  128. var totalSeconds: UInt = UInt(labs(timeStamp - store.timeStampOnEnterRoom))
  129. guard topMenuTimer == nil, store.isEnteredRoom else { return }
  130. updateTimer(totalSeconds: totalSeconds)
  131. topMenuTimer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
  132. topMenuTimer?.schedule(deadline: .now(), repeating: .seconds(1))
  133. topMenuTimer?.setEventHandler { [weak self] in
  134. guard let self = self else { return }
  135. totalSeconds += 1
  136. self.updateTimer(totalSeconds: totalSeconds)
  137. }
  138. topMenuTimer?.resume()
  139. }
  140. deinit {
  141. unsubscribeUIEvent()
  142. topMenuTimer?.cancel()
  143. topMenuTimer = nil
  144. debugPrint("deinit \(self)")
  145. }
  146. }
  147. extension TopViewModel: RoomKitUIEventResponder {
  148. func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable : Any]?) {
  149. switch key {
  150. case .TUIRoomKitService_CurrentUserHasVideoStream:
  151. guard let hasVideo = info?["hasVideo"] as? Bool else { return }
  152. guard let item = viewItems.first(where: { $0.buttonType == .switchCamaraItemType }) else { return }
  153. item.isHidden = !hasVideo
  154. viewResponder?.updateStackView(item: item)
  155. default: break
  156. }
  157. }
  158. }
  159. extension TopViewModel: RoomEngineEventResponder {
  160. func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
  161. switch name {
  162. case .onConferenceInfoChanged:
  163. guard let conferenceInfo = param?["conferenceInfo"] as? TUIConferenceInfo else { return }
  164. guard let modifyFlag = param?["modifyFlag"] as? TUIConferenceModifyFlag else { return }
  165. guard modifyFlag.contains(.roomName) else { return }
  166. viewResponder?.updateMeetingNameLabel(conferenceInfo.basicRoomInfo.name)
  167. case .onStartedRoom, .onJoinedRoom:
  168. updateTimerLabelText()
  169. case .onInitialRoomInfo:
  170. guard let roomInfo = param?["roomInfo"] as? TUIRoomInfo else { return }
  171. viewResponder?.updateMeetingNameLabel(roomInfo.name)
  172. case .onUserInfoChanged:
  173. #if RTCube_APPSTORE
  174. guard let modifyFlag = param?["modifyFlag"] as? TUIUserInfoModifyFlag, modifyFlag.contains(.userRole) else { return }
  175. guard let userInfo = param?["userInfo"] as? TUIUserInfo else { return }
  176. handleUserRoleChanged(userId: userInfo.userId, userRole: userInfo.userRole)
  177. #endif
  178. break
  179. case .onInitialSelfUserInfo:
  180. #if RTCube_APPSTORE
  181. handleUserRoleChanged(userId: currentUser.userId, userRole: currentUser.userRole)
  182. #endif
  183. break
  184. default: break
  185. }
  186. }
  187. }
  188. #if RTCube_APPSTORE
  189. extension TopViewModel {
  190. private func injectReport() {
  191. guard store.isEnteredRoom, currentUser.userRole == .generalUser else { return }
  192. viewItems.append(createReportItem())
  193. }
  194. private func createReportItem() -> ButtonItemData {
  195. let reportItem = ButtonItemData()
  196. reportItem.normalIcon = "room_report"
  197. reportItem.buttonType = .reportItemType
  198. reportItem.backgroundColor = UIColor(0xA3AEC7)
  199. reportItem.resourceBundle = tuiRoomKitBundle()
  200. reportItem.action = { [weak self] sender in
  201. guard let self = self, let button = sender as? UIButton else { return }
  202. self.reportItemAction(sender: button)
  203. }
  204. return reportItem
  205. }
  206. private func reportItemAction(sender: UIButton) {
  207. viewResponder?.showReportView()
  208. }
  209. private func handleUserRoleChanged(userId: String, userRole: TUIRole) {
  210. guard userId == currentUser.userId else { return }
  211. if let item = viewItems.first(where: { $0.buttonType == .reportItemType }) {
  212. item.isHidden = userRole != .generalUser
  213. viewResponder?.updateStackView(item: item)
  214. } else if userRole == .generalUser {
  215. let reportItem = createReportItem()
  216. viewItems.append(reportItem)
  217. viewResponder?.addStackSubview(item: reportItem)
  218. }
  219. }
  220. }
  221. #endif