Files
featherVoice/TUIKit/TUIRoomKit/Source/View/ViewModel/TopViewModel.swift

213 lines
7.9 KiB
Swift
Raw Normal View History

2025-08-08 10:49:36 +08:00
//
// TopViewModel.swift
// TUIRoomKit
//
// Created by janejntang on 2022/12/30.
// Copyright © 2022 Tencent. All rights reserved.
//
import Foundation
import RTCRoomEngine
protocol TopViewModelResponder: AnyObject {
func updateTimerLabel(text: String)
func updateStackView(item: ButtonItemData)
func updateMeetingNameLabel(_ text: String)
#if RTCube_APPSTORE
func showReportView()
#endif
}
class TopViewModel: NSObject {
private var topMenuTimer: DispatchSourceTimer?
private(set) var viewItems: [ButtonItemData] = []
var engineManager: EngineManager {
return EngineManager.shared
}
var store: RoomStore {
return engineManager.store
}
weak var viewResponder: TopViewModelResponder?
var roomInfo: TUIRoomInfo {
engineManager.store.roomInfo
}
var currentUser: UserEntity {
engineManager.store.currentUser
}
override init() {
super.init()
createBottomData()
initialStatus()
subscribeUIEvent()
updateTimerLabelText()
}
private func createBottomData() {
let micItem = ButtonItemData()
micItem.normalIcon = "room_earpiece"
micItem.selectedIcon = "room_speakerphone"
micItem.backgroundColor = UIColor(0xA3AEC7)
micItem.resourceBundle = tuiRoomKitBundle()
micItem.isSelect = engineManager.store.audioSetting.isSoundOnSpeaker
micItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.switchMicItemAction(sender: button)
}
viewItems.append(micItem)
let cameraItem = ButtonItemData()
cameraItem.normalIcon = "room_switch_camera"
cameraItem.backgroundColor = UIColor(0xA3AEC7)
cameraItem.resourceBundle = tuiRoomKitBundle()
cameraItem.buttonType = .switchCamaraItemType
cameraItem.isHidden = !currentUser.hasVideoStream
cameraItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.switchCameraItemAction(sender: button)
}
viewItems.append(cameraItem)
#if RTCube_APPSTORE
injectReport()
#endif
}
private func initialStatus() {
if engineManager.store.audioSetting.isSoundOnSpeaker {
engineManager.setAudioRoute(isSoundOnSpeaker: true)
} else {
engineManager.setAudioRoute(isSoundOnSpeaker: false)
}
}
private func subscribeUIEvent() {
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_CurrentUserHasVideoStream, responder: self)
EngineEventCenter.shared.subscribeEngine(event: .onConferenceInfoChanged, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onStartedRoom, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onJoinedRoom, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onInitialRoomInfo, observer: self)
}
private func unsubscribeUIEvent() {
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_CurrentUserHasVideoStream, responder: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onConferenceInfoChanged, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onStartedRoom, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onJoinedRoom, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onInitialRoomInfo, observer: self)
}
private func switchMicItemAction(sender: UIButton) {
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": true])
sender.isSelected = !sender.isSelected
if sender.isSelected {
engineManager.setAudioRoute(isSoundOnSpeaker: true)
} else {
engineManager.setAudioRoute(isSoundOnSpeaker: false)
}
}
private func switchCameraItemAction(sender: UIButton) {
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": true])
engineManager.switchCamera()
}
private func updateTimer(totalSeconds: UInt) {
let second: UInt = totalSeconds % 60
let minute: UInt = (totalSeconds / 60) % 60
let hour: UInt = totalSeconds / 3_600
var timerText: String
if hour > 0 {
timerText = String(format: "%.2d:%.2d:%.2d", hour, minute, second)
} else {
timerText = String(format: "%.2d:%.2d", minute, second)
}
self.viewResponder?.updateTimerLabel(text: timerText)
}
func dropDownAction(sender: UIView) {
RoomRouter.shared.presentPopUpViewController(viewType: .roomInfoViewType, height: 290.scale375Height())
}
func exitAction(sender: UIView) {
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ShowExitRoomView, param: [:])
}
func updateTimerLabelText() {
let timeInterval: TimeInterval = Date().timeIntervalSince1970
let timeStamp = Int(timeInterval)
var totalSeconds: UInt = UInt(labs(timeStamp - store.timeStampOnEnterRoom))
guard topMenuTimer == nil, store.isEnteredRoom else { return }
updateTimer(totalSeconds: totalSeconds)
topMenuTimer = DispatchSource.makeTimerSource(queue: DispatchQueue.main)
topMenuTimer?.schedule(deadline: .now(), repeating: .seconds(1))
topMenuTimer?.setEventHandler { [weak self] in
guard let self = self else { return }
totalSeconds += 1
self.updateTimer(totalSeconds: totalSeconds)
}
topMenuTimer?.resume()
}
deinit {
unsubscribeUIEvent()
topMenuTimer?.cancel()
topMenuTimer = nil
debugPrint("deinit \(self)")
}
}
extension TopViewModel: RoomKitUIEventResponder {
func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable : Any]?) {
switch key {
case .TUIRoomKitService_CurrentUserHasVideoStream:
guard let hasVideo = info?["hasVideo"] as? Bool else { return }
guard let item = viewItems.first(where: { $0.buttonType == .switchCamaraItemType }) else { return }
item.isHidden = !hasVideo
viewResponder?.updateStackView(item: item)
default: break
}
}
}
extension TopViewModel: RoomEngineEventResponder {
func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
switch name {
case .onConferenceInfoChanged:
guard let conferenceInfo = param?["conferenceInfo"] as? TUIConferenceInfo else { return }
guard let modifyFlag = param?["modifyFlag"] as? TUIConferenceModifyFlag else { return }
guard modifyFlag.contains(.roomName) else { return }
viewResponder?.updateMeetingNameLabel(conferenceInfo.basicRoomInfo.name)
case .onStartedRoom, .onJoinedRoom:
updateTimerLabelText()
case .onInitialRoomInfo:
guard let roomInfo = param?["roomInfo"] as? TUIRoomInfo else { return }
viewResponder?.updateMeetingNameLabel(roomInfo.name)
default: break
}
}
}
#if RTCube_APPSTORE
extension TopViewModel {
private func injectReport() {
if currentUser.userId == roomInfo.roomId {
return
}
let reportItem = ButtonItemData()
reportItem.normalIcon = "room_report"
reportItem.backgroundColor = UIColor(0xA3AEC7)
reportItem.resourceBundle = tuiRoomKitBundle()
reportItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.reportItemAction(sender: button)
}
viewItems.append(reportItem)
}
private func reportItemAction(sender: UIButton) {
viewResponder?.showReportView()
}
}
#endif