| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- //
- // UserListManagerViewModel.swift
- // TUIRoomKit
- //
- // Created by janejntang on 2023/2/10.
- // Copyright © 2023 Tencent. All rights reserved.
- //
- import Foundation
- import RTCRoomEngine
- protocol UserListManagerViewEventResponder: AnyObject {
- func makeToast(text: String)
- func showAlert(title: String?, message: String?, sureTitle:String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?)
- func setUserListManagerViewHidden(isHidden: Bool)
- func dismissView()
- func updateUI(item: ButtonItemData)
- func updateStackView(items:[ButtonItemData])
- func addStackView(item: ButtonItemData, index: Int?)
- func removeStackView(itemType: ButtonItemData.ButtonType)
- func showUserNameInputView(userName: String?)
- }
- class UserListManagerViewModel: NSObject {
- var selectUserId: String = ""
- let timeoutNumber: Double = 60
- var userListManagerItems: [ButtonItemData] = []
- weak var viewResponder: UserListManagerViewEventResponder?
- var engineManager: EngineManager {
- EngineManager.shared
- }
- var roomInfo: TUIRoomInfo {
- engineManager.store.roomInfo
- }
- var currentUser: UserEntity {
- engineManager.store.currentUser
- }
- var attendeeList: [UserEntity] {
- engineManager.store.attendeeList
- }
- var selectUserInfo: UserEntity? {
- attendeeList.first(where: { $0.userId == selectUserId } )
- }
-
- init(selectUserId: String) {
- self.selectUserId = selectUserId
- super.init()
- createManagerItems()
- subscribeEngine()
- }
-
- deinit {
- unsubscribeEngine()
- debugPrint("self:\(self)")
- }
-
- private func subscribeEngine() {
- EngineEventCenter.shared.subscribeEngine(event: .onUserVideoStateChanged, observer: self)
- EngineEventCenter.shared.subscribeEngine(event: .onUserAudioStateChanged, observer: self)
- EngineEventCenter.shared.subscribeEngine(event: .onSendMessageForUserDisableChanged, observer: self)
- EngineEventCenter.shared.subscribeEngine(event: .onSeatListChanged, observer: self)
- EngineEventCenter.shared.subscribeEngine(event: .onUserInfoChanged, observer: self)
- }
-
- private func unsubscribeEngine() {
- EngineEventCenter.shared.unsubscribeEngine(event: .onUserVideoStateChanged, observer: self)
- EngineEventCenter.shared.unsubscribeEngine(event: .onUserAudioStateChanged, observer: self)
- EngineEventCenter.shared.unsubscribeEngine(event: .onSendMessageForUserDisableChanged, observer: self)
- EngineEventCenter.shared.unsubscribeEngine(event: .onSeatListChanged, observer: self)
- EngineEventCenter.shared.unsubscribeEngine(event: .onUserInfoChanged, observer: self)
- }
-
- private func createManagerItems() {
- if checkMediaShownState() {
- userListManagerItems.append(muteAudioItem)
- userListManagerItems.append(muteVideoItem)
- }
- if checkInviteSeatItemShownState() {
- userListManagerItems.append(inviteSeatItem)
- }
- if checkChangeHostItemShownState() {
- userListManagerItems.append(changeHostItem)
- }
- if checkSetAdministratorItemShownState() {
- userListManagerItems.append(setAdministratorItem)
- }
- if checkModifyTheNameItemShownState() {
- userListManagerItems.append(modifyTheNameItem)
- }
- if checkMuteMessageItemShownState() {
- userListManagerItems.append(muteMessageItem)
- }
- if checkKickOutItemShownState() {
- userListManagerItems.append(kickOutItem)
- }
- }
-
- private lazy var muteAudioItem: ButtonItemData = {
- let item = ButtonItemData()
- item.normalTitle = .muteText
- item.normalIcon = "room_unMute_audio"
- item.selectedTitle = .requestOpenAudioText
- item.selectedIcon = "room_mute_audio"
- item.resourceBundle = tuiRoomKitBundle()
- item.buttonType = .muteAudioItemType
- item.isSelect = !(selectUserInfo?.hasAudioStream ?? true)
- item.hasLineView = true
- item.action = { [weak self] sender in
- guard let self = self, let button = sender as? UIButton else { return }
- self.muteAudioAction(sender: button)
- }
- return item
- }()
-
- private lazy var muteVideoItem: ButtonItemData = {
- let item = ButtonItemData()
- item.normalTitle = .closeVideoText
- item.normalIcon = "room_unMute_video"
- item.selectedTitle = .requestOpenVideoText
- item.selectedIcon = "room_mute_video"
- item.resourceBundle = tuiRoomKitBundle()
- item.buttonType = .muteVideoItemType
- item.isSelect = !(selectUserInfo?.hasVideoStream ?? true)
- item.hasLineView = true
- item.action = { [weak self] sender in
- guard let self = self, let button = sender as? UIButton else { return }
- self.muteVideoAction(sender: button)
- }
- return item
- }()
-
- private lazy var inviteSeatItem: ButtonItemData = {
- let item = ButtonItemData()
- item.normalTitle = .inviteSeatText
- item.normalIcon = "room_invite_seat"
- item.selectedTitle = .stepDownSeatText
- item.selectedIcon = "room_step_down_seat"
- item.resourceBundle = tuiRoomKitBundle()
- item.isSelect = selectUserInfo?.isOnSeat ?? false
- item.hasLineView = true
- item.action = { [weak self] sender in
- guard let self = self, let button = sender as? UIButton else { return }
- self.inviteSeatAction(sender: button)
- }
- return item
- }()
-
- private lazy var changeHostItem: ButtonItemData = {
- let item = ButtonItemData()
- item.normalTitle = .changeHostText
- item.normalIcon = "room_change_host"
- item.resourceBundle = tuiRoomKitBundle()
- item.hasLineView = true
- item.action = { [weak self] sender in
- guard let self = self, let button = sender as? UIButton else { return }
- self.changeHostAction(sender: button)
- }
- return item
- }()
-
- private lazy var muteMessageItem: ButtonItemData = {
- let item = ButtonItemData()
- item.normalTitle = .muteMessageText
- item.normalIcon = "room_mute_message"
- item.selectedTitle = .unMuteMessageText
- item.selectedIcon = "room_unMute_message"
- item.resourceBundle = tuiRoomKitBundle()
- item.isSelect = selectUserInfo?.disableSendingMessage ?? false
- item.hasLineView = true
- item.action = { [weak self] sender in
- guard let self = self, let button = sender as? UIButton else { return }
- self.muteMessageAction(sender: button)
- }
- return item
- }()
-
- private lazy var kickOutItem: ButtonItemData = {
- let item = ButtonItemData()
- item.normalTitle = .kickOutRoomText
- item.normalIcon = "room_kickOut_room"
- item.resourceBundle = tuiRoomKitBundle()
- item.hasLineView = true
- item.action = { [weak self] sender in
- guard let self = self, let button = sender as? UIButton else { return }
- self.kickOutAction(sender: button)
- }
- return item
- }()
-
- private lazy var setAdministratorItem: ButtonItemData = {
- let item = ButtonItemData()
- item.normalTitle = .setAsAdministratorText
- item.selectedTitle = .undoAdministratorText
- item.normalIcon = "room_set_administrator"
- item.selectedIcon = "room_undo_administrator"
- item.resourceBundle = tuiRoomKitBundle()
- item.isSelect = selectUserInfo?.userRole == .administrator
- item.hasLineView = true
- item.action = { [weak self] sender in
- guard let self = self, let button = sender as? UIButton else { return }
- self.setAdministratorAction(sender: button)
- }
- return item
- }()
-
- private lazy var modifyTheNameItem: ButtonItemData = {
- let item = ButtonItemData()
- item.normalTitle = .modifyTheName
- item.normalIcon = "room_modify_name"
- item.resourceBundle = tuiRoomKitBundle()
- item.hasLineView = true
- item.action = { [weak self] sender in
- guard let self = self, let button = sender as? UIButton else { return }
- self.modifyTheNameAction(sender: button)
- }
- return item
- }()
-
- func backBlockAction(sender: UIView) {
- sender.isHidden = true
- }
-
- private func checkMediaShownState() -> Bool {
- guard selectUserId != currentUser.userId else { return false }
- if !roomInfo.isSeatEnabled {
- return true
- } else if let selectUserInfo = selectUserInfo, selectUserInfo.isOnSeat {
- return true
- } else {
- return false
- }
- }
-
- private func checkInviteSeatItemShownState() -> Bool {
- return roomInfo.isSeatEnabled && selectUserId != currentUser.userId
- }
-
- private func checkChangeHostItemShownState() -> Bool {
- return currentUser.userRole == .roomOwner && selectUserId != currentUser.userId
- }
-
- private func checkMuteMessageItemShownState() -> Bool {
- return selectUserId != currentUser.userId
- }
-
- private func checkKickOutItemShownState() -> Bool {
- return currentUser.userRole == .roomOwner && selectUserId != currentUser.userId
- }
-
- private func checkSetAdministratorItemShownState() -> Bool {
- return currentUser.userRole == .roomOwner && selectUserId != currentUser.userId
- }
-
- private func checkModifyTheNameItemShownState() -> Bool {
- return true
- }
- }
- extension UserListManagerViewModel {
- private func addViewItem(buttonItem: ButtonItemData, index: Int) {
- guard !isContainedViewItem(buttonType: buttonItem.buttonType) else { return }
- if userListManagerItems.count > index + 1 {
- userListManagerItems.insert(buttonItem, at: index)
- viewResponder?.addStackView(item: buttonItem, index: index)
- } else {
- userListManagerItems.append(buttonItem)
- viewResponder?.addStackView(item: buttonItem, index: nil)
- }
- }
-
- private func removeViewItem(buttonType: ButtonItemData.ButtonType) {
- userListManagerItems.removeAll(where: { $0.buttonType == buttonType })
- viewResponder?.removeStackView(itemType: buttonType)
- }
-
- private func isContainedViewItem(buttonType: ButtonItemData.ButtonType) -> Bool {
- return userListManagerItems.contains(where: { $0.buttonType == buttonType })
- }
-
- private func muteAudioAction(sender: UIButton) {
- guard let userInfo = attendeeList.first(where: { $0.userId == selectUserId }) else { return }
- if userInfo.hasAudioStream {
- engineManager.closeRemoteDeviceByAdmin(userId: selectUserId, device: .microphone)
- } else {
- engineManager.openRemoteDeviceByAdmin(userId: selectUserId, device: .microphone)
- viewResponder?.makeToast(text: .invitedOpenAudioText)
- }
- viewResponder?.dismissView()
- }
-
- private func muteVideoAction(sender: UIButton) {
- guard let userInfo = selectUserInfo else { return }
- if userInfo.hasVideoStream {
- engineManager.closeRemoteDeviceByAdmin(userId: selectUserId, device: .camera)
- } else {
- engineManager.openRemoteDeviceByAdmin(userId: selectUserId, device: .camera)
- viewResponder?.makeToast(text: .invitedOpenVideoText)
- }
- viewResponder?.dismissView()
- }
-
- func inviteSeatAction(sender: UIButton) {
- guard let userInfo = selectUserInfo else { return }
- sender.isSelected = !sender.isSelected
- if sender.isSelected {
- guard engineManager.store.seatList.count < roomInfo.maxSeatCount else {
- RoomRouter.makeToastInCenter(toast: .theStageIsFullText, duration: 0.5)
- viewResponder?.dismissView()
- return
- }
- engineManager.takeUserOnSeatByAdmin(userId: selectUserId, timeout: timeoutNumber) { _,_ in
- let text: String = localizedReplace(.onStageText, replace: userInfo.userName)
- RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
- } onRejected: { requestId, userId, message in
- let text: String = localizedReplace(.refusedTakeSeatInvitationText, replace: userInfo.userName)
- RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
- } onCancelled: { _, _ in
- } onTimeout: { _, _ in
- let text: String = localizedReplace(.takeSeatInvitationTimeoutText, replace: userInfo.userName)
- RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
- } onError: { _, _, code, message in
- let text: String = code == .requestIdRepeat ? .receivedSameRequestText : message
- RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
- }
- viewResponder?.makeToast(text: .invitedTakeSeatText)
- } else {
- engineManager.kickUserOffSeatByAdmin(userId: selectUserId)
- }
- viewResponder?.dismissView()
- }
-
- private func changeHostAction(sender: UIButton) {
- guard let userInfo = attendeeList.first(where: { $0.userId == selectUserId }) else { return }
- let title = localizedReplace(.transferHostTitle, replace: userInfo.userName)
- viewResponder?.showAlert(title: title, message: .transferHostMessage, sureTitle: .transferHostsureText, declineTitle: .cancelText, sureBlock: { [weak self] in
- guard let self = self else { return }
- if self.roomInfo.isScreenShareDisableForAllUser, self.currentUser.hasScreenStream {
- self.engineManager.stopScreenCapture()
- }
- self.engineManager.changeUserRole(userId: self.selectUserId, role: .roomOwner) { [weak self] in
- guard let self = self else { return }
- let text = localizedReplace(.haveTransferredMasterText, replace: userInfo.userName)
- self.viewResponder?.makeToast(text: text)
- self.viewResponder?.dismissView()
- } onError: { [weak self] code, message in
- guard let self = self else { return }
- self.viewResponder?.dismissView()
- }
- }, declineBlock: nil)
- }
-
- private func muteMessageAction(sender: UIButton) {
- guard let userInfo = attendeeList.first(where: { $0.userId == selectUserId }) else { return }
- engineManager.disableSendingMessageByAdmin(userId: selectUserId, isDisable: !userInfo.disableSendingMessage)
- viewResponder?.dismissView()
- }
-
- func kickOutAction(sender: UIButton) {
- let kickOutTitle = localizedReplace(.kickOutText, replace: selectUserInfo?.userName ?? "")
- viewResponder?.showAlert(title: kickOutTitle, message: nil, sureTitle: .alertOkText, declineTitle: .cancelText, sureBlock: { [weak self] in
- guard let self = self else { return }
- self.engineManager.kickRemoteUserOutOfRoom(userId: self.selectUserId)
- self.viewResponder?.dismissView()
- }, declineBlock: nil)
- }
-
- private func setAdministratorAction(sender: UIButton) {
- guard let userInfo = selectUserInfo else { return }
- let role: TUIRole = userInfo.userRole == .administrator ? .generalUser : .administrator
- engineManager.changeUserRole(userId: selectUserId, role: role) { [weak self] in
- guard let self = self else { return }
- let setAdministratorText = localizedReplace(.setUpAdministratorText, replace: userInfo.userName)
- let removeAdministratorText = localizedReplace(.removedAdministratorText, replace: userInfo.userName)
- let text: String = role == .administrator ? setAdministratorText : removeAdministratorText
- self.viewResponder?.makeToast(text: text)
- if role == .generalUser, self.roomInfo.isScreenShareDisableForAllUser, userInfo.hasScreenStream {
- self.engineManager.closeRemoteDeviceByAdmin(userId: userInfo.userId, device: .screenSharing)
- }
- self.viewResponder?.dismissView()
- } onError: { [weak self] code, message in
- guard let self = self else { return }
- self.viewResponder?.dismissView()
- debugPrint("changeUserRole,code:\(code),message:\(message)")
- }
- }
-
- private func modifyTheNameAction(sender: UIButton) {
- viewResponder?.showUserNameInputView(userName: selectUserInfo?.userName)
- }
- }
- extension UserListManagerViewModel: RoomEngineEventResponder {
- func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
- switch name {
- case .onUserAudioStateChanged:
- guard let userId = param?["userId"] as? String else { return }
- guard let hasAudio = param?["hasAudio"] as? Bool else { return }
- guard userId == selectUserId else { return }
- muteAudioItem.isSelect = !hasAudio
- viewResponder?.updateUI(item: muteAudioItem)
- case .onUserVideoStateChanged:
- guard let userId = param?["userId"] as? String else { return }
- guard let hasVideo = param?["hasVideo"] as? Bool else { return }
- guard let streamType = param?["streamType"] as? TUIVideoStreamType else { return }
- guard userId == selectUserId, streamType == .cameraStream else { return }
- muteVideoItem.isSelect = !hasVideo
- viewResponder?.updateUI(item: muteVideoItem)
- case .onSendMessageForUserDisableChanged:
- guard let userId = param?["userId"] as? String else { return }
- guard let muted = param?["muted"] as? Bool else { return }
- guard userId == selectUserId else { return }
- muteMessageItem.isSelect = muted
- viewResponder?.updateUI(item: muteMessageItem)
- case .onSeatListChanged:
- if let seated = param?["seated"] as? [TUISeatInfo], seated.first(where: { $0.userId == selectUserId }) != nil {
- inviteSeatItem.isSelect = true
- viewResponder?.updateUI(item: inviteSeatItem)
- addViewItem(buttonItem: muteAudioItem, index: 0)
- addViewItem(buttonItem: muteVideoItem, index: 1)
- }
- if let left = param?["left"] as? [TUISeatInfo], left.first(where: { $0.userId == selectUserId }) != nil {
- inviteSeatItem.isSelect = false
- viewResponder?.updateUI(item: inviteSeatItem)
- removeViewItem(buttonType: .muteAudioItemType)
- removeViewItem(buttonType: .muteVideoItemType)
- }
- case .onUserInfoChanged:
- guard let modifyFlag = param?["modifyFlag"] as? TUIUserInfoModifyFlag, modifyFlag.contains(.userRole) else { return }
- guard let userInfo = param?["userInfo"] as? TUIUserInfo else { return }
- guard userInfo.userId == currentUser.userId || userInfo.userId == selectUserId else { return }
- guard currentUser.userId != selectUserId else { return }
- guard let selectUserInfo = selectUserInfo else { return }
- userListManagerItems.removeAll()
- if currentUser.userRole == .roomOwner {
- createManagerItems()
- viewResponder?.updateStackView(items: userListManagerItems)
- } else if currentUser.userRole == .administrator, selectUserInfo.userRole == .generalUser {
- createManagerItems()
- viewResponder?.updateStackView(items: userListManagerItems)
- } else {
- viewResponder?.dismissView()
- }
- default: break
- }
- }
- }
- extension UserListManagerViewModel: UserNameInputViewResponder {
- func changeUserName(name: String) {
- engineManager.changeUserNameCard(userid: selectUserId, nameCard: name) { [weak self] in
- guard let self = self else { return }
- self.viewResponder?.makeToast(text: .nameChangedSuccessfully)
- } onError: { [weak self] _, _ in
- guard let self = self else { return }
- self.viewResponder?.makeToast(text: .nameChangeFailed)
- }
- }
-
- func onEndEditing() {
- viewResponder?.dismissView()
- }
- }
- private extension String {
- static var muteText: String {
- localized("Mute")
- }
- static var requestOpenVideoText: String {
- localized("Ask to start video")
- }
- static var requestOpenAudioText: String {
- localized("Ask to unmute")
- }
- static var closeVideoText: String {
- localized("Stop video")
- }
- static var changeHostText: String {
- localized("Make host")
- }
- static var muteMessageText: String {
- localized("Disable chat")
- }
- static var unMuteMessageText: String {
- localized("Enable chat")
- }
- static var kickOutRoomText: String {
- localized("Remove")
- }
- static var stepDownSeatText: String {
- localized("Leave the stage")
- }
- static var inviteSeatText: String {
- localized("Invite to stage")
- }
- static var invitedTakeSeatText: String {
- localized("The audience has been invited to the stage")
- }
- static var refusedTakeSeatInvitationText: String {
- localized("xx refused to go on stage")
- }
- static var takeSeatInvitationTimeoutText: String {
- localized("The invitation to xx to go on stage has timed out")
- }
- static var invitedOpenAudioText: String {
- localized("The audience has been invited to open the audio")
- }
- static var invitedOpenVideoText: String {
- localized("The audience has been invited to open the video")
- }
- static var kickOutText: String {
- localized("Do you want to move xx out of the conference?")
- }
- static var setAsAdministratorText: String {
- localized("Set as administrator")
- }
- static var undoAdministratorText: String {
- localized("Undo administrator")
- }
- static var haveTransferredMasterText: String {
- localized("The host has been transferred to xx")
- }
- static var setUpAdministratorText: String {
- localized("xx has been set as conference admin")
- }
- static var removedAdministratorText: String {
- localized("The conference admin status of xx has been withdrawn")
- }
- static var alertOkText: String {
- localized("OK")
- }
- static var cancelText: String {
- localized("Cancel")
- }
- static var transferHostTitle: String {
- localized("Transfer the host to xx")
- }
- static var transferHostMessage: String {
- localized("After transfer the host, you will become a general user")
- }
- static var transferHostsureText: String {
- localized("Confirm transfer")
- }
- static var receivedSameRequestText: String {
- localized("This member has already received the same request, please try again later")
- }
- static var onStageText: String {
- localized("xx is on stage")
- }
- static var theStageIsFullText: String {
- localized("The stage is full")
- }
- static let modifyTheName = localized("Modify the name")
- static let nameChangedSuccessfully = localized("Name changed successfully")
- static let nameChangeFailed = localized("Name change failed")
- }
|