356 lines
14 KiB
Swift
356 lines
14 KiB
Swift
|
|
//
|
||
|
|
// UserListViewModel.swift
|
||
|
|
// TUIRoomKit
|
||
|
|
//
|
||
|
|
// Created by janejntang on 2023/1/4.
|
||
|
|
// Copyright © 2023 Tencent. All rights reserved.
|
||
|
|
//
|
||
|
|
|
||
|
|
import Foundation
|
||
|
|
import RTCRoomEngine
|
||
|
|
import Factory
|
||
|
|
|
||
|
|
protocol UserListViewResponder: NSObject {
|
||
|
|
func updateBottomControlView(isHidden: Bool)
|
||
|
|
func reloadUserListView()
|
||
|
|
func makeToast(text: String)
|
||
|
|
func updateUserManagerViewDisplayStatus(isHidden: Bool)
|
||
|
|
func updateMuteAllAudioButtonState(isSelect: Bool)
|
||
|
|
func updateMuteAllVideoButtonState(isSelect: Bool)
|
||
|
|
func showAlert(title: String?, message: String?, sureTitle:String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?)
|
||
|
|
func updateListStateView()
|
||
|
|
func updateMemberLabel(count: Int)
|
||
|
|
func updateUserListTableView()
|
||
|
|
}
|
||
|
|
|
||
|
|
class UserListViewModel: NSObject {
|
||
|
|
var userId: String = ""
|
||
|
|
var userName: String = ""
|
||
|
|
var attendeeList: [UserEntity] = []
|
||
|
|
var invitationList: [TUIInvitation] = []
|
||
|
|
var engineManager: EngineManager {
|
||
|
|
EngineManager.shared
|
||
|
|
}
|
||
|
|
var store: RoomStore {
|
||
|
|
engineManager.store
|
||
|
|
}
|
||
|
|
var currentUser: UserEntity {
|
||
|
|
store.currentUser
|
||
|
|
}
|
||
|
|
var roomInfo: TUIRoomInfo {
|
||
|
|
store.roomInfo
|
||
|
|
}
|
||
|
|
let timeoutNumber: Double = 60
|
||
|
|
weak var viewResponder: UserListViewResponder?
|
||
|
|
lazy var userListType: UserListType = isSeatEnabled ? .onStageUsers: .allUsers
|
||
|
|
var onStageCount: Int {
|
||
|
|
store.seatList.count
|
||
|
|
}
|
||
|
|
var offStageCount: Int {
|
||
|
|
store.offSeatList.count
|
||
|
|
}
|
||
|
|
var allUserCount: Int {
|
||
|
|
store.attendeeList.count
|
||
|
|
}
|
||
|
|
var isShownBottomControlView: Bool {
|
||
|
|
return store.currentUser.userRole != .generalUser
|
||
|
|
}
|
||
|
|
lazy var isSeatEnabled: Bool = {
|
||
|
|
return roomInfo.isSeatEnabled
|
||
|
|
}()
|
||
|
|
lazy var isShownNotificationView: Bool = {
|
||
|
|
return false
|
||
|
|
}()
|
||
|
|
lazy var isSearching: Bool = {
|
||
|
|
return false
|
||
|
|
}()
|
||
|
|
lazy var searchText: String = {
|
||
|
|
return ""
|
||
|
|
}()
|
||
|
|
var invitationUserList: [TUIInvitation] {
|
||
|
|
return conferenceStore.selectCurrent(ConferenceInvitationSelectors.getInvitationList)
|
||
|
|
}
|
||
|
|
|
||
|
|
@Injected(\.conferenceStore) var conferenceStore
|
||
|
|
|
||
|
|
override init() {
|
||
|
|
super.init()
|
||
|
|
updateAttendeeList()
|
||
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_CurrentUserRoleChanged, responder: self)
|
||
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_RenewUserList, responder: self)
|
||
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_RenewSeatList, responder: self)
|
||
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_RoomOwnerChanged, responder: self)
|
||
|
|
EngineEventCenter.shared.subscribeEngine(event: .onAllUserMicrophoneDisableChanged, observer: self)
|
||
|
|
EngineEventCenter.shared.subscribeEngine(event: .onAllUserCameraDisableChanged, observer: self)
|
||
|
|
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_CurrentUserRoleChanged, responder: self)
|
||
|
|
}
|
||
|
|
|
||
|
|
deinit {
|
||
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_CurrentUserRoleChanged, responder: self)
|
||
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_RenewUserList, responder: self)
|
||
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_RenewSeatList, responder: self)
|
||
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_RoomOwnerChanged, responder: self)
|
||
|
|
EngineEventCenter.shared.unsubscribeEngine(event: .onAllUserMicrophoneDisableChanged, observer: self)
|
||
|
|
EngineEventCenter.shared.unsubscribeEngine(event: .onAllUserCameraDisableChanged, observer: self)
|
||
|
|
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_CurrentUserRoleChanged, responder: self)
|
||
|
|
debugPrint("deinit \(self)")
|
||
|
|
}
|
||
|
|
|
||
|
|
func updateAttendeeList() {
|
||
|
|
var userList: [UserEntity] = []
|
||
|
|
switch userListType {
|
||
|
|
case .allUsers:
|
||
|
|
userList = store.attendeeList
|
||
|
|
case .onStageUsers:
|
||
|
|
userList = store.seatList
|
||
|
|
case .offStageUsers:
|
||
|
|
userList = store.offSeatList
|
||
|
|
case .notInRoomUsers:
|
||
|
|
userList = invitationList.map{ UserEntity(invitation: $0) }
|
||
|
|
}
|
||
|
|
if isSearching, searchText.count > 0 {
|
||
|
|
let searchArray = userList.filter({ model -> Bool in
|
||
|
|
return (model.userName.contains(searchText))
|
||
|
|
})
|
||
|
|
attendeeList = searchArray
|
||
|
|
} else {
|
||
|
|
attendeeList = userList
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func muteAllAudioAction(sender: UIButton, view: UserListView) {
|
||
|
|
let isSelected = sender.isSelected
|
||
|
|
viewResponder?.showAlert(title: sender.isSelected ? .allUnmuteTitle : .allMuteTitle,
|
||
|
|
message: sender.isSelected ? .allUnmuteMessage : .allMuteMessage,
|
||
|
|
sureTitle: sender.isSelected ? .confirmReleaseText : .allMuteActionText,
|
||
|
|
declineTitle: .cancelText, sureBlock: { [weak self] in
|
||
|
|
guard let self = self else { return }
|
||
|
|
if self.roomInfo.isMicrophoneDisableForAllUser != !isSelected {
|
||
|
|
self.engineManager.muteAllAudioAction(isMute: !isSelected) {
|
||
|
|
} onError: { [weak self] _, message in
|
||
|
|
guard let self = self else { return }
|
||
|
|
self.viewResponder?.makeToast(text:message)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
let text: String = isSelected ? .allUnMuteAudioText : .allMuteAudioText
|
||
|
|
self.viewResponder?.makeToast(text: text)
|
||
|
|
}
|
||
|
|
}, declineBlock: nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
func muteAllVideoAction(sender: UIButton, view: UserListView) {
|
||
|
|
let isSelected = sender.isSelected
|
||
|
|
viewResponder?.showAlert(title: sender.isSelected ? .allUnmuteVideoTitle : .allMuteVideoTitle,
|
||
|
|
message: sender.isSelected ? .allUnmuteVideoMessage : .allMuteVideoMessage,
|
||
|
|
sureTitle: sender.isSelected ? .confirmReleaseText : .allMuteVideoActionText,
|
||
|
|
declineTitle: .cancelText, sureBlock: { [weak self] in
|
||
|
|
guard let self = self else { return }
|
||
|
|
if self.roomInfo.isCameraDisableForAllUser != !isSelected {
|
||
|
|
self.engineManager.muteAllVideoAction(isMute: !isSelected) {
|
||
|
|
} onError: { [weak self] _, message in
|
||
|
|
guard let self = self else { return }
|
||
|
|
self.viewResponder?.makeToast(text:message)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
let text: String = isSelected ? .allUnMuteVideoText : .allMuteVideoText
|
||
|
|
self.viewResponder?.makeToast(text: text)
|
||
|
|
}
|
||
|
|
}, declineBlock: nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
func showUserManageViewAction(userId: String, userName: String) {
|
||
|
|
self.userId = userId
|
||
|
|
self.userName = userName
|
||
|
|
guard checkShowManagerView() else { return }
|
||
|
|
viewResponder?.updateUserManagerViewDisplayStatus(isHidden: false)
|
||
|
|
}
|
||
|
|
|
||
|
|
private func checkShowManagerView() -> Bool {
|
||
|
|
guard let userInfo = engineManager.store.attendeeList.first(where: { $0.userId == userId }) else { return false }
|
||
|
|
if currentUser.userRole == .roomOwner, userId != currentUser.userId {
|
||
|
|
return true
|
||
|
|
} else if currentUser.userRole == .administrator, userId != currentUser.userId, userInfo.userRole == .generalUser {
|
||
|
|
return true
|
||
|
|
} else {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func inviteSeatAction(sender: UIButton) {
|
||
|
|
guard let userInfo = attendeeList.first(where: { $0.userId == userId }) else { return }
|
||
|
|
guard store.seatList.count < roomInfo.maxSeatCount else {
|
||
|
|
RoomRouter.makeToastInCenter(toast: .theStageIsFullText, duration: 0.5)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
engineManager.takeUserOnSeatByAdmin(userId: userId, 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)
|
||
|
|
}
|
||
|
|
|
||
|
|
func checkSelfInviteAbility(invitee: UserEntity) -> Bool {
|
||
|
|
if currentUser.userRole == .roomOwner {
|
||
|
|
return true
|
||
|
|
} else if currentUser.userRole == .administrator, invitee.userRole == .generalUser {
|
||
|
|
return true
|
||
|
|
} else {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func changeListState(type: UserListType) {
|
||
|
|
userListType = type
|
||
|
|
updateAttendeeList()
|
||
|
|
viewResponder?.reloadUserListView()
|
||
|
|
}
|
||
|
|
|
||
|
|
func compareLists(oldList: [TUIInvitation], newList: [TUIInvitation]) -> (added: [TUIInvitation], removed: [TUIInvitation], changed: [TUIInvitation]) {
|
||
|
|
var added = [TUIInvitation]()
|
||
|
|
var removed = [TUIInvitation]()
|
||
|
|
var changed = [TUIInvitation]()
|
||
|
|
let oldDict = Dictionary(uniqueKeysWithValues: oldList.map { ($0.invitee.userId, $0) })
|
||
|
|
let newDict = Dictionary(uniqueKeysWithValues: newList.map { ($0.invitee.userId, $0) })
|
||
|
|
|
||
|
|
for newInvitation in newList {
|
||
|
|
if oldDict[newInvitation.invitee.userId] == nil {
|
||
|
|
added.append(newInvitation)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for oldInvitation in oldList {
|
||
|
|
if newDict[oldInvitation.invitee.userId] == nil {
|
||
|
|
removed.append(oldInvitation)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for newInvitation in newList {
|
||
|
|
if let oldInvitation = oldDict[newInvitation.invitee.userId], oldInvitation != newInvitation {
|
||
|
|
changed.append(newInvitation)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return (added, removed, changed)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
extension UserListViewModel: RoomKitUIEventResponder {
|
||
|
|
func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable : Any]?) {
|
||
|
|
switch key {
|
||
|
|
case .TUIRoomKitService_RenewUserList, .TUIRoomKitService_RenewSeatList:
|
||
|
|
viewResponder?.updateMemberLabel(count: allUserCount)
|
||
|
|
updateAttendeeList()
|
||
|
|
viewResponder?.reloadUserListView()
|
||
|
|
viewResponder?.updateListStateView()
|
||
|
|
case .TUIRoomKitService_CurrentUserRoleChanged:
|
||
|
|
guard let userRole = info?["userRole"] as? TUIRole else { return }
|
||
|
|
viewResponder?.updateBottomControlView(isHidden: userRole == .generalUser)
|
||
|
|
case .TUIRoomKitService_RoomOwnerChanged:
|
||
|
|
viewResponder?.reloadUserListView()
|
||
|
|
default: break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
extension UserListViewModel: RoomEngineEventResponder {
|
||
|
|
func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
|
||
|
|
switch name {
|
||
|
|
case .onAllUserCameraDisableChanged:
|
||
|
|
guard let isDisable = param?["isDisable"] as? Bool else { return }
|
||
|
|
viewResponder?.updateMuteAllVideoButtonState(isSelect: isDisable)
|
||
|
|
case .onAllUserMicrophoneDisableChanged:
|
||
|
|
guard let isDisable = param?["isDisable"] as? Bool else { return }
|
||
|
|
viewResponder?.updateMuteAllAudioButtonState(isSelect: isDisable)
|
||
|
|
default: break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
extension UserListViewModel: RaiseHandApplicationNotificationViewListener {
|
||
|
|
func onShown() {
|
||
|
|
isShownNotificationView = true
|
||
|
|
viewResponder?.updateUserListTableView()
|
||
|
|
}
|
||
|
|
|
||
|
|
func onHidden() {
|
||
|
|
isShownNotificationView = false
|
||
|
|
viewResponder?.updateUserListTableView()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private extension String {
|
||
|
|
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 allMuteTitle: String {
|
||
|
|
localized("All current and incoming members will be muted")
|
||
|
|
}
|
||
|
|
static var allMuteVideoTitle: String {
|
||
|
|
localized("All current and incoming members will be restricted from video")
|
||
|
|
}
|
||
|
|
static var allMuteMessage: String {
|
||
|
|
localized("Members will unable to turn on the microphone")
|
||
|
|
}
|
||
|
|
static var allMuteVideoMessage: String {
|
||
|
|
localized("Members will unable to turn on video")
|
||
|
|
}
|
||
|
|
static var allUnmuteMessage: String {
|
||
|
|
localized("Members will be able to turn on the microphone")
|
||
|
|
}
|
||
|
|
static var allUnmuteVideoMessage: String {
|
||
|
|
localized("Members will be able to turn on video")
|
||
|
|
}
|
||
|
|
static var allUnmuteTitle: String {
|
||
|
|
localized("All members will be unmuted")
|
||
|
|
}
|
||
|
|
static var allUnmuteVideoTitle: String {
|
||
|
|
localized("All members will not be restricted from video")
|
||
|
|
}
|
||
|
|
static var cancelText: String {
|
||
|
|
localized("Cancel")
|
||
|
|
}
|
||
|
|
static var allMuteActionText: String {
|
||
|
|
localized("Mute All")
|
||
|
|
}
|
||
|
|
static var allMuteVideoActionText: String {
|
||
|
|
localized("Stop all video")
|
||
|
|
}
|
||
|
|
static var confirmReleaseText: String {
|
||
|
|
localized("Confirm release")
|
||
|
|
}
|
||
|
|
static var allMuteAudioText: String {
|
||
|
|
localized("All audios disabled")
|
||
|
|
}
|
||
|
|
static var allUnMuteAudioText: String {
|
||
|
|
localized("All audios enabled")
|
||
|
|
}
|
||
|
|
static var allMuteVideoText: String {
|
||
|
|
localized("All videos disabled")
|
||
|
|
}
|
||
|
|
static var allUnMuteVideoText: String {
|
||
|
|
localized("All videos enabled")
|
||
|
|
}
|
||
|
|
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")
|
||
|
|
}
|
||
|
|
}
|