Files
fanyin-ios/TUIKit/TUIRoomKit/Source/View/ViewModel/BottomViewModel.swift
2025-08-12 14:27:12 +08:00

807 lines
34 KiB
Swift

//
// BottomViewModel.swift
// Alamofire
//
// Created by aby on 2022/12/22.
// Copyright © 2022 Tencent. All rights reserved.
//
import Foundation
import RTCRoomEngine
import TUICore
import Factory
import Combine
protocol BottomViewModelResponder: AnyObject {
func updateButtonView(item: ButtonItemData)
func makeToast(text: String)
func updataBottomView(isUp:Bool)
func updateStackView(items: [ButtonItemData])
func showAlert(title: String?, message: String?, sureTitle:String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?)
}
class BottomViewModel: NSObject {
private(set) var viewItems: [ButtonItemData] = []
weak var viewResponder: BottomViewModelResponder?
var engineManager: EngineManager {
EngineManager.shared
}
var engineEventCenter: EngineEventCenter {
EngineEventCenter.shared
}
var roomInfo: TUIRoomInfo {
engineManager.store.roomInfo
}
var currentUser: UserEntity {
engineManager.store.currentUser
}
var attendeeList: [UserEntity] {
engineManager.store.attendeeList
}
var inviteSeatList: [RequestEntity] {
engineManager.store.inviteSeatList
}
var isCalledFromShareScreen = false
var cancellableSet = Set<AnyCancellable>()
private lazy var memberItem: ButtonItemData = {
let memberItem = ButtonItemData()
memberItem.normalTitle = String(format: .memberText, attendeeList.count)
memberItem.normalIcon = "room_member"
memberItem.resourceBundle = tuiRoomKitBundle()
memberItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.memberAction(sender: button)
}
return memberItem
}()
private lazy var muteAudioItem: ButtonItemData = {
let muteAudioItem = ButtonItemData()
muteAudioItem.normalTitle = .muteAudioText
muteAudioItem.selectedTitle = .unMuteAudioText
muteAudioItem.normalIcon = "room_unMute_audio"
muteAudioItem.selectedIcon = "room_mic_off"
muteAudioItem.resourceBundle = tuiRoomKitBundle()
muteAudioItem.buttonType = .muteAudioItemType
muteAudioItem.isSelect = !currentUser.hasAudioStream
muteAudioItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.muteAudioAction(sender: button)
}
return muteAudioItem
}()
private lazy var muteVideoItem: ButtonItemData = {
let muteVideoItem = ButtonItemData()
muteVideoItem.normalTitle = .muteVideoText
muteVideoItem.selectedTitle = .unMuteVideoText
muteVideoItem.normalIcon = "room_camera_on"
muteVideoItem.selectedIcon = "room_camera_off"
muteVideoItem.resourceBundle = tuiRoomKitBundle()
muteVideoItem.buttonType = .muteVideoItemType
muteVideoItem.isSelect = !currentUser.hasVideoStream
muteVideoItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.muteVideoAction(sender: button)
}
return muteVideoItem
}()
private lazy var shareScreenItem: ButtonItemData = {
let shareScreenItem = ButtonItemData()
shareScreenItem.normalTitle = .shareScreenOnText
shareScreenItem.selectedTitle = .shareScreenOffText
shareScreenItem.normalIcon = "room_shareScreen_on"
shareScreenItem.selectedIcon = "room_shareScreen_off"
shareScreenItem.resourceBundle = tuiRoomKitBundle()
shareScreenItem.buttonType = .shareScreenItemType
shareScreenItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.shareScreenAction(sender: button)
}
return shareScreenItem
}()
private lazy var chatItem: ButtonItemData = {
let chatItem = ButtonItemData()
chatItem.normalIcon = "room_chat"
chatItem.normalTitle = .chatText
chatItem.resourceBundle = tuiRoomKitBundle()
chatItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.chatAction(sender: button)
}
return chatItem
}()
private lazy var moreItem: ButtonItemData = {
let moreItem = ButtonItemData()
moreItem.normalTitle = .unfoldText
moreItem.normalIcon = "room_more"
moreItem.selectedTitle = .dropText
moreItem.selectedIcon = "room_drop"
moreItem.resourceBundle = tuiRoomKitBundle()
moreItem.buttonType = .moreItemType
moreItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.moreAction(sender: button)
}
return moreItem
}()
private lazy var floatItem: ButtonItemData = {
let floatItem = ButtonItemData()
floatItem.normalTitle = .floatText
floatItem.normalIcon = "room_float"
floatItem.resourceBundle = tuiRoomKitBundle()
floatItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.floatAction(sender: button)
}
return floatItem
}()
private lazy var setupItem: ButtonItemData = {
let setupItem = ButtonItemData()
setupItem.normalTitle = .setupText
setupItem.normalIcon = "room_setting"
setupItem.resourceBundle = tuiRoomKitBundle()
setupItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.setupAction(sender: button)
}
return setupItem
}()
private lazy var inviteItem: ButtonItemData = {
let inviteItem = ButtonItemData()
inviteItem.normalTitle = .inviteText
inviteItem.normalIcon = "room_invite"
inviteItem.resourceBundle = tuiRoomKitBundle()
inviteItem.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.inviteAction(sender: button)
}
return inviteItem
}()
private lazy var raiseHandApplyItem: ButtonItemData = {
let item = ButtonItemData()
item.normalTitle = .stageManagementText
item.normalIcon = "room_hand_raise_list"
item.resourceBundle = tuiRoomKitBundle()
item.buttonType = .raiseHandApplyItemType
item.noticeText = String(inviteSeatList.count)
item.hasNotice = inviteSeatList.count > 0
item.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.raiseHandApplyAction(sender: button)
}
return item
}()
private lazy var raiseHandItem: ButtonItemData = {
let item = ButtonItemData()
item.normalTitle = currentUser.userRole == .generalUser ? .applyJoinStageText : .joinStageText
item.normalIcon = "room_apply_join_stage"
item.selectedIcon = "room_cancel_request"
item.selectedTitle = .cancelStageText
item.resourceBundle = tuiRoomKitBundle()
item.buttonType = .raiseHandItemType
item.isSelect = engineManager.store.selfTakeSeatRequestId != nil
item.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.raiseHandAction(sender: button)
}
return item
}()
private lazy var leaveSeatHandItem: ButtonItemData = {
let item = ButtonItemData()
item.normalIcon = "room_leave_seat"
item.selectedIcon = "room_apply_join_stage"
item.normalTitle = .leaveSeatText
item.selectedTitle = .applyJoinStageText
item.buttonType = .leaveSeatItemType
item.resourceBundle = tuiRoomKitBundle()
item.action = { [weak self] sender in
guard let self = self, let button = sender as? UIButton else { return }
self.leaveSeatAction(sender: button)
}
return item
}()
private lazy var invitationPopupPublisher = {
conferenceStore.select(ViewSelectors.getShowinvitationPopupView)
}()
override init() {
super.init()
createBottomData()
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_UserOnSeatChanged, responder: self)
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_CurrentUserRoleChanged, responder: self)
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_CurrentUserHasAudioStream, responder: self)
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_CurrentUserHasVideoStream, responder: self)
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_SomeoneSharing, responder: self)
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_RenewUserList, responder: self)
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_RenewSeatList, responder: self)
EngineEventCenter.shared.subscribeEngine(event: .onAllUserCameraDisableChanged, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onAllUserMicrophoneDisableChanged, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onInitialSelfUserInfo, observer: self)
EngineEventCenter.shared.subscribeEngine(event: .onRemoteUserEnterRoom, observer: self)
NotificationCenter.default.addObserver(self,
selector: #selector(onUserScreenCaptureStarted),
name: UIScreen.capturedDidChangeNotification, object: nil)
invitationPopupPublisher
.receive(on: DispatchQueue.mainQueue)
.sink { [weak self] showInvitationPopupView in
guard let self = self else { return }
if showInvitationPopupView {
showMemberSelectViewAction()
}
}
.store(in: &cancellableSet)
}
func createBottomData() {
creatBaseBottomData()
createMoreBottomData()
}
func creatBaseBottomData(){
viewItems.append(memberItem)
viewItems.append(muteAudioItem)
viewItems.append(muteVideoItem)
if roomInfo.isSeatEnabled {
if currentUser.userRole == .roomOwner {
viewItems.append(raiseHandApplyItem)
} else {
if currentUser.isOnSeat {
viewItems.append(leaveSeatHandItem)
} else {
viewItems.append(raiseHandItem)
}
}
if currentUser.userRole == .administrator {
viewItems.append(raiseHandApplyItem)
}
}
viewItems.append(shareScreenItem)
if hasTUIChatItem() {
viewItems.append(chatItem)
}
viewItems.append(moreItem)
updateAudioItem()
updateVideoItem()
}
func createMoreBottomData(){
viewItems.append(inviteItem)
viewItems.append(floatItem)
viewItems.append(setupItem)
reorderTheMoreItem()
}
func memberAction(sender: UIButton) {
RoomRouter.shared.presentPopUpViewController(viewType: .userListViewType, height: 720.scale375Height(), backgroundColor: UIColor(0x17181F))
}
func muteAudioAction(sender: UIButton) {
engineEventCenter.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": true])
if currentUser.hasAudioStream {
engineManager.muteLocalAudio()
return
}
//If all hosts are muted, ordinary members of the room cannot turn on their microphones.
if self.roomInfo.isMicrophoneDisableForAllUser && currentUser.userRole == .generalUser {
viewResponder?.makeToast(text: .muteAudioRoomReasonText)
return
}
//If you are speaking in a room with your hand raised and you are not on the microphone, you cannot turn on the microphone.
if roomInfo.isSeatEnabled, !currentUser.isOnSeat {
viewResponder?.makeToast(text: .muteSeatReasonText)
return
}
engineManager.unmuteLocalAudio()
guard !engineManager.store.audioSetting.isMicOpened else { return }
engineManager.openLocalMicrophone()
}
func muteVideoAction(sender: UIButton) {
engineEventCenter.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": true])
if currentUser.hasVideoStream {
engineManager.closeLocalCamera()
return
}
//If the entire host bans paintings, ordinary members of the room cannot turn on the camera.
if self.roomInfo.isCameraDisableForAllUser && self.currentUser.userRole == .generalUser {
viewResponder?.makeToast(text: .muteVideoRoomReasonText)
return
}
//If you are speaking in a room with your hands raised and you are not on the mic, you cannot turn on the camera.
if roomInfo.isSeatEnabled, !currentUser.isOnSeat {
viewResponder?.makeToast(text: .muteSeatReasonText)
return
}
engineManager.setLocalVideoView(streamType: .cameraStream, view: nil)
engineManager.openLocalCamera()
}
func raiseHandApplyAction(sender: UIButton) {
RoomRouter.shared.presentPopUpViewController(viewType: .raiseHandApplicationListViewType, height: 720.scale375Height(), backgroundColor: UIColor(0x22262E))
}
func raiseHandAction(sender: UIButton) {
sender.isSelected = !sender.isSelected
engineEventCenter.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": true])
if sender.isSelected {
handleRaiseHandAction()
} else {
handleCancelRaiseHandAction()
}
}
func handleRaiseHandAction() {
_ = engineManager.takeSeat() { [weak self] _,_ in
guard let self = self else { return }
self.viewResponder?.makeToast(text: .takenSeatText)
} onRejected: { [weak self] _, _, _ in
guard let self = self else { return }
self.viewResponder?.makeToast(text: .rejectedTakeSeatText)
self.changeItemSelectState(type: .raiseHandItemType, isSelected: false)
} onTimeout: { [weak self] requestId, userId in
guard let self = self else { return }
self.viewResponder?.makeToast(text: .joinStageApplicationTimedOutText)
self.changeItemSelectState(type: .raiseHandItemType, isSelected: false)
} onError: { [weak self] _, _, code, message in
guard let self = self else { return }
self.changeItemSelectState(type: .raiseHandItemType, isSelected: false)
}
changeItemSelectState(type: .raiseHandItemType)
guard currentUser.userRole == .generalUser else { return }
viewResponder?.makeToast(text: .applicationHasSentText)
}
func handleCancelRaiseHandAction() {
engineManager.cancelTakeSeatRequest()
changeItemSelectState(type: .raiseHandItemType)
viewResponder?.makeToast(text: .joinStageApplicationCancelledText)
}
func leaveSeatAction(sender: UIButton) {
engineEventCenter.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": true])
if currentUser.userRole == .administrator {
engineManager.leaveSeat()
} else {
viewResponder?.showAlert(title: .leaveSeatTitle, message: .leaveSeatMessage, sureTitle: .leaveSeatText, declineTitle: .toastCancelText, sureBlock: { [weak self] in
guard let self = self else { return }
self.engineManager.leaveSeat()
}, declineBlock: nil)
}
}
func shareScreenAction(sender: UIButton) {
if #available(iOS 12.0, *) {
guard let item = viewItems.first(where: { $0.buttonType == .shareScreenItemType })
else { return }
if !item.isSelect {
//If someone else is screen sharing, you can no longer screen share yourself
guard engineManager.store.attendeeList.first(where: {$0.hasScreenStream}) == nil else {
viewResponder?.makeToast(text: .othersScreenSharingText)
return
}
//If you are in a room where you are raising your hand to speak, and you are not on the mic, you cannot share your screen.
guard !(roomInfo.isSeatEnabled && !currentUser.isOnSeat) else {
viewResponder?.makeToast(text: .muteSeatReasonText)
return
}
if TUICore.callService(TUICore_PrivacyService,
method: TUICore_PrivacyService_ScreenShareAntifraudReminderMethod,
param: nil, resultCallback: { [weak self] code, message, param in
guard let self = self else { return }
if code == TUICore_PrivacyService_EnableScreenShareAntifraudReminderMethod_Continue {
self.isCalledFromShareScreen = true
BroadcastLauncher.launch()
}
}) == nil {
isCalledFromShareScreen = true
BroadcastLauncher.launch()
}
} else {
viewResponder?.showAlert(title: .toastTitleText, message: .toastMessageText, sureTitle: .toastStopText, declineTitle: .toastCancelText, sureBlock: { [weak self] in
guard let self = self else { return }
self.engineManager.stopScreenCapture()
}, declineBlock: nil)
}
} else {
viewResponder?.makeToast(text: .versionLowToastText)
}
}
func chatAction(sender: UIButton) {
let user = engineManager.store.currentUser
let roomInfo = engineManager.store.roomInfo
RoomRouter.shared.pushToChatController(user: user, roomInfo: roomInfo)
}
func moreAction(sender: UIButton) {
sender.isSelected = !sender.isSelected
engineEventCenter.notifyUIEvent(key: .TUIRoomKitService_SetToolBarDelayHidden, param: ["isDelay": !sender.isSelected])
viewResponder?.updataBottomView(isUp: sender.isSelected)
changeItemSelectState(type: .moreItemType)
}
func inviteAction(sender: UIButton) {
RoomRouter.shared.presentPopUpViewController(viewType: .inviteViewType, height: 158.scale375Height())
}
func floatAction(sender: UIButton) {
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ShowRoomVideoFloatView, param: [:])
}
func setupAction(sender: UIButton) {
RoomRouter.shared.presentPopUpViewController(viewType: .mediaSettingViewType, height: 709.scale375Height())
}
func showMemberSelectViewAction() {
conferenceStore.dispatch(action: InvitationViewActions.resetPopupViewFlag())
let inRoomUsers = attendeeList.map{ UserInfo(userEntity: $0).convertToUser() }
// TODO: @jeremiawang Use ConferenceRouter to push ContactVC
let participants = ConferenceParticipants(unSelectableList: inRoomUsers)
guard let vc = Container.shared.contactViewController(participants) as? (ContactViewProtocol & UIViewController) else {
return
}
vc.delegate = self
RoomRouter.shared.push(viewController: vc)
}
@objc func onUserScreenCaptureStarted(notification:Notification)
{
guard let screen = notification.object as? UIScreen else {return}
if screen.isCaptured,isCalledFromShareScreen {
engineManager.startScreenCapture()
}
}
deinit {
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_UserOnSeatChanged, responder: self)
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_CurrentUserRoleChanged, responder: self)
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_CurrentUserHasAudioStream, responder: self)
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_CurrentUserHasVideoStream, responder: self)
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_SomeoneSharing, responder: self)
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_RenewUserList, responder: self)
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_RenewSeatList, responder: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onAllUserCameraDisableChanged, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onAllUserMicrophoneDisableChanged, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onInitialSelfUserInfo, observer: self)
EngineEventCenter.shared.unsubscribeEngine(event: .onRemoteUserEnterRoom, observer: self)
NotificationCenter.default.removeObserver(self, name: UIScreen.capturedDidChangeNotification, object: nil)
debugPrint("deinit \(self)")
}
@Injected(\.conferenceStore) private var conferenceStore: ConferenceStore
}
// MARK: - Private
extension BottomViewModel {
private func hasTUIChatItem() -> Bool {
return TUICore.getService(TUICore_TUIChatService) != nil
}
private func changeItemSelectState(type: ButtonItemData.ButtonType, isSelected: Bool? = nil) {
guard let item = viewItems.first(where: { $0.buttonType == type })
else { return }
if let isSelected = isSelected {
item.isSelect = isSelected
} else {
item.isSelect = !item.isSelect
}
viewResponder?.updateButtonView(item: item)
}
private func updateRaiseHandItem() {
guard roomInfo.isSeatEnabled else { return }
raiseHandItem.normalTitle = currentUser.userRole == .generalUser ? .applyJoinStageText : .joinStageText
leaveSeatHandItem.isSelect = false
raiseHandItem.isSelect = false
if currentUser.userRole == .roomOwner {
guard let index = viewItems.firstIndex(where:{ $0.buttonType == .leaveSeatItemType || $0.buttonType == .raiseHandItemType }) else { return }
viewItems.remove(at: index)
} else if let index = viewItems.firstIndex(where:{ $0.buttonType == .leaveSeatItemType || $0.buttonType == .raiseHandItemType }) {
if currentUser.isOnSeat {
viewItems[index] = leaveSeatHandItem
} else {
viewItems[index] = raiseHandItem
}
} else {
if currentUser.isOnSeat {
addViewItem(buttonItem: leaveSeatHandItem, index: 3)
} else {
addViewItem(buttonItem: raiseHandItem, index: 1)
}
}
}
private func reorderTheMoreItem() {
guard viewItems.count > 6 else { return }
guard let index = viewItems.firstIndex(where: { $0.buttonType == .moreItemType }), index != 5 else { return }
viewItems.remove(at: index)
viewItems.insert(moreItem, at: 5)
}
private func removeViewItem(buttonType: ButtonItemData.ButtonType) {
viewItems.removeAll(where: { $0.buttonType == buttonType })
}
private func addViewItem(buttonItem: ButtonItemData, index: Int) {
guard !isContainedViewItem(buttonType: buttonItem.buttonType) else { return }
if viewItems.count > index + 1 {
viewItems.insert(buttonItem, at: index)
} else {
viewItems.append(buttonItem)
}
}
private func isContainedViewItem(buttonType: ButtonItemData.ButtonType) -> Bool {
return viewItems.contains(where: { $0.buttonType == buttonType })
}
private func updateAudioItem() {
if roomInfo.isSeatEnabled, currentUser.userRole == .generalUser, !currentUser.isOnSeat {
//If the audience in the room who raises their hand to speak is not on the microphone, the microphone button will not be displayed.
removeViewItem(buttonType: .muteAudioItemType)
} else if !isContainedViewItem(buttonType: .muteAudioItemType) {
addViewItem(buttonItem: muteAudioItem, index: 1)
}
muteAudioItem.isSelect = !currentUser.hasAudioStream
muteAudioItem.alpha = checkMicAuthority() || currentUser.hasAudioStream ? 1 : 0.5
}
private func updateVideoItem() {
if roomInfo.isSeatEnabled, currentUser.userRole == .generalUser, !currentUser.isOnSeat {
removeViewItem(buttonType: .muteVideoItemType)
} else if !isContainedViewItem(buttonType: .muteVideoItemType) {
addViewItem(buttonItem: muteVideoItem, index: 2)
}
muteVideoItem.isSelect = !currentUser.hasVideoStream
muteVideoItem.alpha = checkCameraAuthority() || currentUser.hasVideoStream ? 1 : 0.5
}
private func checkMicAuthority() -> Bool {
if self.roomInfo.isMicrophoneDisableForAllUser && currentUser.userRole == .generalUser {
return false
}
if roomInfo.isSeatEnabled, !currentUser.isOnSeat {
return false
}
return true
}
private func checkCameraAuthority() -> Bool {
if self.roomInfo.isCameraDisableForAllUser && currentUser.userRole == .generalUser {
return false
}
if roomInfo.isSeatEnabled, !currentUser.isOnSeat {
return false
}
return true
}
private func updateRaiseHandApplyItem() {
guard roomInfo.isSeatEnabled else { return }
raiseHandItem.normalTitle = currentUser.userRole == .generalUser ? .applyJoinStageText : .joinStageText
if currentUser.userRole == .roomOwner {
addViewItem(buttonItem: raiseHandApplyItem, index: 3)
} else if currentUser.userRole == .administrator {
addViewItem(buttonItem: raiseHandApplyItem, index: 4)
} else {
removeViewItem(buttonType: .raiseHandApplyItemType)
}
}
}
extension BottomViewModel: RoomKitUIEventResponder {
func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable : Any]?) {
switch key {
case .TUIRoomKitService_UserOnSeatChanged:
guard roomInfo.isSeatEnabled else { return }
updateRaiseHandItem()
updateAudioItem()
updateVideoItem()
reorderTheMoreItem()
viewResponder?.updateStackView(items: viewItems)
case .TUIRoomKitService_CurrentUserRoleChanged:
updateAudioItem()
updateVideoItem()
updateRaiseHandApplyItem()
updateRaiseHandItem()
reorderTheMoreItem()
viewResponder?.updateStackView(items: viewItems)
case .TUIRoomKitService_CurrentUserHasAudioStream:
guard let hasAudio = info?["hasAudio"] as? Bool else { return }
guard let reason = info?["reason"] as? TUIChangeReason else { return }
if !hasAudio, reason == .byAdmin, !roomInfo.isMicrophoneDisableForAllUser {
if !roomInfo.isSeatEnabled {
viewResponder?.makeToast(text: .noticeMicrophoneOffTitleText)
} else if currentUser.isOnSeat {
viewResponder?.makeToast(text: .noticeMicrophoneOffTitleText)
}
}
updateAudioItem()
viewResponder?.updateButtonView(item: muteAudioItem)
case .TUIRoomKitService_CurrentUserHasVideoStream:
guard let hasVideo = info?["hasVideo"] as? Bool else { return }
guard let reason = info?["reason"] as? TUIChangeReason else { return }
if !hasVideo, reason == .byAdmin, !roomInfo.isCameraDisableForAllUser {
if !roomInfo.isSeatEnabled {
viewResponder?.makeToast(text: .noticeCameraOffTitleText)
} else if currentUser.isOnSeat {
viewResponder?.makeToast(text: .noticeCameraOffTitleText)
}
}
updateVideoItem()
viewResponder?.updateButtonView(item: muteVideoItem)
case .TUIRoomKitService_SomeoneSharing:
guard let userId = info?["userId"] as? String else { return }
guard let hasVideo = info?["hasVideo"] as? Bool else { return }
guard userId == currentUser.userId else { return }
changeItemSelectState(type: .shareScreenItemType, isSelected: hasVideo)
if !hasVideo {
isCalledFromShareScreen = false
}
case .TUIRoomKitService_RenewUserList:
memberItem.normalTitle = String(format: .memberText, attendeeList.count)
viewResponder?.updateButtonView(item: memberItem)
case .TUIRoomKitService_RenewSeatList:
raiseHandApplyItem.noticeText = String(inviteSeatList.count)
raiseHandApplyItem.hasNotice = inviteSeatList.count > 0
viewResponder?.updateButtonView(item: raiseHandApplyItem)
default: break
}
}
}
extension BottomViewModel: RoomEngineEventResponder {
func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
switch name {
case .onAllUserCameraDisableChanged:
updateVideoItem()
viewResponder?.updateButtonView(item: muteVideoItem)
case .onAllUserMicrophoneDisableChanged:
updateAudioItem()
viewResponder?.updateButtonView(item: muteAudioItem)
case .onInitialSelfUserInfo:
updateAudioItem()
updateVideoItem()
updateRaiseHandApplyItem()
updateRaiseHandItem()
reorderTheMoreItem()
viewResponder?.updateStackView(items: viewItems)
case .onRemoteUserEnterRoom:
guard let userInfo = param?["userInfo"] as? TUIUserInfo else { return }
conferenceStore.dispatch(action: ConferenceInvitationActions.removeInvitation(payload: userInfo.userId))
default: break
}
}
}
extension BottomViewModel: ContactViewSelectDelegate {
func onMemberSelected(_ viewController: any ContactViewProtocol, invitees: [User]) {
let userIdList = invitees.map{ $0.userId }
self.conferenceStore.dispatch(action: ConferenceInvitationActions.inviteUsers(payload: (roomInfo.roomId, userIdList)))
RoomRouter.shared.pop()
if !invitees.isEmpty{
viewResponder?.makeToast(text: .inviteEnterRoomSuccesstext)
}
}
}
private extension String {
static var memberText: String {
localized("Users(%lld)")
}
static var muteAudioText: String {
localized("Mute")
}
static var unMuteAudioText: String {
localized("Unmute")
}
static var muteVideoText: String {
localized("Stop video")
}
static var unMuteVideoText: String {
localized("Start video")
}
static var stageManagementText: String {
localized("Applies")
}
static var cancelStageText: String {
localized("Cancel")
}
static var applyJoinStageText: String {
localized("Join stage")
}
static var leaveSeatText: String {
localized("Step down")
}
static var muteSeatReasonText: String {
localized("Can be turned on after taking the stage")
}
static var muteAudioRoomReasonText: String {
localized("All on mute audio, unable to turn on microphone")
}
static var muteVideoRoomReasonText: String {
localized("All on mute video, unable to turn on camera")
}
static var noticeCameraOffTitleText: String {
localized("The conference owner disabled your video.")
}
static var noticeMicrophoneOffTitleText: String {
localized("You were muted by the host.")
}
static var shareScreenOnText: String {
localized("Share")
}
static var shareScreenOffText: String {
localized("Stop")
}
static var versionLowToastText: String {
localized("Your system version is below 12.0. Please update.")
}
static var chatText: String {
localized("Chat")
}
static var unfoldText: String {
localized("More")
}
static var inviteText: String {
localized("Invite")
}
static var floatText: String {
localized("Floating")
}
static var setupText: String {
localized("Settings")
}
static var dropText: String {
localized("Drop")
}
static var rejectedTakeSeatText: String {
localized("Application to go on stage was rejected")
}
static var takenSeatText: String {
localized("Succeed on stage")
}
static var othersScreenSharingText: String {
localized("An existing member is sharing. Please try again later")
}
static var toastTitleText: String {
localized("Share Screen")
}
static var toastMessageText: String {
localized("Stop TUIRoom screen sharing screen live?")
}
static var toastCancelText: String {
localized("Cancel")
}
static var toastStopText: String {
localized("Stop")
}
static var applicationHasSentText: String {
localized("Application has been sent, please wait for the owner/administrator to approve")
}
static var joinStageText: String {
localized("Join stage")
}
static var leaveSeatTitle: String {
localized("Are you sure you want to step down?")
}
static var leaveSeatMessage: String {
localized("To get on stage again, you need to resend the application and wait for the owner/administrator to approve it.")
}
static var joinStageApplicationCancelledText: String {
localized("Application for stage has been cancelled")
}
static var joinStageApplicationTimedOutText: String {
localized("The request to go on stage has timed out")
}
static var inviteEnterRoomSuccesstext: String {
localized("Invitation has been sent, waiting for users to join")
}
}