提交
This commit is contained in:
806
TUIKit/TUIRoomKit/Source/View/ViewModel/BottomViewModel.swift
Normal file
806
TUIKit/TUIRoomKit/Source/View/ViewModel/BottomViewModel.swift
Normal file
@@ -0,0 +1,806 @@
|
||||
//
|
||||
// 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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user