提交
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// InviteToJoinRoomManager.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/7/3.
|
||||
// You can choose to invite members and send invitations.
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
import TUICore
|
||||
|
||||
class InviteToJoinRoomManager {
|
||||
class func startInviteToJoinRoom(message: RoomMessageModel, inviter: TUILoginUserInfo) {
|
||||
let inviteJoinModel = InviteJoinModel(message: message, inviter: inviter)
|
||||
pushSelectGroupMemberViewController(groupId: message.groupId) { responseData in
|
||||
guard let modelList =
|
||||
responseData[TUICore_TUIGroupObjectFactory_SelectGroupMemberVC_ResultUserList] as? [TUIUserModel]
|
||||
else { return }
|
||||
var invitedList: [String] = []
|
||||
for userModel in modelList {
|
||||
invitedList.append(userModel.userId)
|
||||
}
|
||||
inviteToJoinRoom(inviteJoinModel: inviteJoinModel, invitedList: invitedList)
|
||||
}
|
||||
}
|
||||
|
||||
class func pushSelectGroupMemberViewController(groupId: String, callback: @escaping TUIValueResultCallback) {
|
||||
let param = [TUICore_TUIGroupObjectFactory_SelectGroupMemberVC_GroupID: groupId]
|
||||
if let navigateController = RoomMessageManager.shared.navigateController {
|
||||
navigateController.push(TUICore_TUIGroupObjectFactory_SelectGroupMemberVC_Classic, param: param) { responseData in
|
||||
callback(responseData)
|
||||
}
|
||||
} else {
|
||||
let nav = UINavigationController()
|
||||
let currentViewController = getCurrentWindowViewController()
|
||||
currentViewController?.present(TUICore_TUIGroupObjectFactory_SelectGroupMemberVC_Classic,
|
||||
param: param, embbedIn: nav,
|
||||
forResult: { responseData in
|
||||
callback(responseData)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class func inviteToJoinRoom(inviteJoinModel: InviteJoinModel, invitedList: [String]) {
|
||||
guard invitedList.count > 0 else { return }
|
||||
let dataDict = inviteJoinModel.getDicFromInviteJoinModel(inviteJoinModel: inviteJoinModel)
|
||||
guard let dataString = dataDict.convertToString() else { return }
|
||||
let pushInfo = V2TIMOfflinePushInfo()
|
||||
invitedList.forEach { userId in
|
||||
V2TIMManager.sharedInstance().invite(userId,
|
||||
data: dataString,
|
||||
onlineUserOnly: true,
|
||||
offlinePushInfo: pushInfo,
|
||||
timeout: 60) {
|
||||
debugPrint("invite,success")
|
||||
} fail: { code, message in
|
||||
debugPrint("invite,code:\(code),message:\(String(describing: message))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class private func getCurrentWindowViewController() -> UIViewController? {
|
||||
var keyWindow: UIWindow?
|
||||
for window in UIApplication.shared.windows {
|
||||
if window.isMember(of: UIWindow.self), window.isKeyWindow {
|
||||
keyWindow = window
|
||||
break
|
||||
}
|
||||
}
|
||||
guard let rootController = keyWindow?.rootViewController else {
|
||||
return nil
|
||||
}
|
||||
func findCurrentController(from vc: UIViewController?) -> UIViewController? {
|
||||
if let nav = vc as? UINavigationController {
|
||||
return findCurrentController(from: nav.topViewController)
|
||||
} else if let tabBar = vc as? UITabBarController {
|
||||
return findCurrentController(from: tabBar.selectedViewController)
|
||||
} else if let presented = vc?.presentedViewController {
|
||||
return findCurrentController(from: presented)
|
||||
}
|
||||
return vc
|
||||
}
|
||||
let viewController = findCurrentController(from: rootController)
|
||||
return viewController
|
||||
}
|
||||
}
|
||||
125
TUIKit/TUIRoomKit/RoomExtension/Model/Manager/RoomManager.swift
Normal file
125
TUIKit/TUIRoomKit/RoomExtension/Model/Manager/RoomManager.swift
Normal file
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// RoomManager.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/7/3.
|
||||
// Manage rooms, including room creation, entry, exit, destruction and conversion operations of the host
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
import TUICore
|
||||
|
||||
class RoomManager {
|
||||
static let shared = RoomManager()
|
||||
private var engineManager: EngineManager {
|
||||
EngineManager.shared
|
||||
}
|
||||
private var roomInfo: TUIRoomInfo {
|
||||
engineManager.store.roomInfo
|
||||
}
|
||||
private lazy var userId: String = {
|
||||
return TUILogin.getUserID() ?? engineManager.store.currentUser.userId
|
||||
}()
|
||||
private let messageManager = RoomMessageManager.shared
|
||||
let roomObserver: RoomObserver = RoomObserver()
|
||||
var roomId: String?
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
|
||||
func isEnteredOtherRoom(roomId: String) -> Bool {
|
||||
return roomInfo.roomId != roomId && roomInfo.roomId != ""
|
||||
}
|
||||
|
||||
func createRoom(roomInfo: TUIRoomInfo) {
|
||||
roomId = roomInfo.roomId
|
||||
roomObserver.registerObserver()
|
||||
engineManager.createRoom(roomInfo: roomInfo) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.roomObserver.createdRoom()
|
||||
self.enterRoom(roomId: roomInfo.roomId, isShownConferenceViewController: false)
|
||||
} onError: { _, message in
|
||||
RoomRouter.makeToast(toast: message)
|
||||
}
|
||||
}
|
||||
|
||||
func enterRoom(roomId: String, isShownConferenceViewController: Bool = true) {
|
||||
roomObserver.registerObserver()
|
||||
engineManager.store.isImAccess = true
|
||||
self.roomId = roomId
|
||||
engineManager.enterRoom(roomId: roomId, enableAudio: engineManager.store.isOpenMicrophone, enableVideo: engineManager.store.isOpenCamera, isSoundOnSpeaker: true) { [weak self] roomInfo in
|
||||
guard let self = self else { return }
|
||||
self.roomObserver.enteredRoom()
|
||||
guard isShownConferenceViewController else { return }
|
||||
let vc = ConferenceMainViewController()
|
||||
RoomRouter.shared.push(viewController: vc)
|
||||
} onError: { _, message in
|
||||
RoomRouter.makeToast(toast: message)
|
||||
}
|
||||
}
|
||||
|
||||
func exitRoom(onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
engineManager.exitRoom { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.refreshSource()
|
||||
self.messageManager.isReadyToSendMessage = true
|
||||
onSuccess()
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.refreshSource()
|
||||
if code.rawValue == -2_102 {
|
||||
self.destroyRoom(onSuccess: onSuccess, onError: onError)
|
||||
} else {
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func destroyRoom(onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
engineManager.destroyRoom { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.roomObserver.messageModel.roomState = .destroyed
|
||||
self.refreshSource()
|
||||
self.messageManager.isReadyToSendMessage = true
|
||||
onSuccess()
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.refreshSource()
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
private func refreshSource() {
|
||||
roomId = nil
|
||||
TUILogin.setCurrentBusinessScene(.None)
|
||||
roomObserver.userList = []
|
||||
roomObserver.unregisterObserver()
|
||||
}
|
||||
|
||||
private func changeUserRole(userId: String, role: TUIRole, onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
engineManager.changeUserRole(userId: userId, role: .roomOwner) {
|
||||
onSuccess()
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func exitOrDestroyPreviousRoom(onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
if roomInfo.ownerId == userId {
|
||||
if let userModel = engineManager.store.attendeeList.first(where: { $0.userId != userId }) {
|
||||
changeUserRole(userId: userModel.userId, role: .roomOwner) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.exitRoom(onSuccess: onSuccess, onError: onError)
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.destroyRoom(onSuccess: onSuccess, onError: onError)
|
||||
}
|
||||
} else {
|
||||
destroyRoom(onSuccess: onSuccess, onError: onError)
|
||||
}
|
||||
} else {
|
||||
exitRoom(onSuccess: onSuccess, onError: onError)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// RoomMessageManager.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
// Manage messages, including sending messages and modifying messages
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import RTCRoomEngine
|
||||
|
||||
class RoomMessageManager: NSObject {
|
||||
static let shared = RoomMessageManager()
|
||||
private var engineManager: EngineManager {
|
||||
EngineManager.shared
|
||||
}
|
||||
private lazy var userId: String = {
|
||||
return TUILogin.getUserID() ?? engineManager.store.currentUser.userId
|
||||
}()
|
||||
weak var navigateController: UINavigationController?
|
||||
var isReadyToSendMessage: Bool = true
|
||||
var groupId: String = ""
|
||||
private override init() {
|
||||
super.init()
|
||||
TUICore.registerEvent(TUICore_TUIChatNotify, subKey: TUICore_TUIChatNotify_SendMessageSubKey, object: self)
|
||||
}
|
||||
|
||||
func sendRoomMessageToGroup() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
guard BusinessSceneUtil.canJoinRoom() else { return }
|
||||
if self.engineManager.store.isEnteredRoom {
|
||||
RoomManager.shared.exitOrDestroyPreviousRoom() { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.sendMessage()
|
||||
} onError: { [weak self] code, message in
|
||||
guard let self = self else { return }
|
||||
self.sendMessage()
|
||||
debugPrint("exitRoom,code:\(code),message:\(message)")
|
||||
}
|
||||
} else {
|
||||
self.sendMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func sendMessage() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
guard self.isReadyToSendMessage else {
|
||||
self.changeReadyToSendMessage()
|
||||
return
|
||||
}
|
||||
self.isReadyToSendMessage = false
|
||||
FetchRoomId.getRoomId { [weak self] roomId in
|
||||
guard let self = self else { return }
|
||||
let messageModel = RoomMessageModel()
|
||||
messageModel.groupId = self.groupId
|
||||
messageModel.roomId = roomId
|
||||
messageModel.ownerName = TUILogin.getNickName() ?? ""
|
||||
messageModel.owner = self.userId
|
||||
let messageDic = messageModel.getDictFromMessageModel()
|
||||
guard let jsonString = messageDic.convertToString() else { return }
|
||||
let jsonData = jsonString.data(using: String.Encoding.utf8)
|
||||
let message = V2TIMManager.sharedInstance().createCustomMessage(jsonData)
|
||||
message?.supportMessageExtension = true
|
||||
let param = [TUICore_TUIChatService_SendMessageMethod_MsgKey: message]
|
||||
TUICore.callService(TUICore_TUIChatService, method: TUICore_TUIChatService_SendMessageMethod, param: param as [AnyHashable : Any])
|
||||
RoomManager.shared.roomId = roomId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resendRoomMessage(message: RoomMessageModel,dic:[String: Any]) {
|
||||
if message.messageId == "" {
|
||||
self.modifyMessage(message: message, dic: dic)
|
||||
return
|
||||
}
|
||||
V2TIMManager.sharedInstance().findMessages([message.messageId]) { [weak self] messageArray in
|
||||
guard let self = self else { return }
|
||||
guard let array = messageArray else { return }
|
||||
for previousMessage in array where previousMessage.msgID == message.messageId {
|
||||
self.modifyMessage(message: message, dic: dic)
|
||||
}
|
||||
} fail: { [weak self] code, messageString in
|
||||
guard let self = self else { return }
|
||||
self.modifyMessage(message: message, dic: dic)
|
||||
}
|
||||
}
|
||||
|
||||
private func modifyMessage(message: RoomMessageModel, dic:[String: Any]) {
|
||||
guard let message = message.getMessage() else { return }
|
||||
guard var customElemDic = TUITool.jsonData2Dictionary(message.customElem.data) as? [String: Any] else { return }
|
||||
for (key, value) in dic {
|
||||
customElemDic[key] = value
|
||||
}
|
||||
guard let jsonString = customElemDic.convertToString() else { return }
|
||||
let jsonData = jsonString.data(using: String.Encoding.utf8)
|
||||
message.customElem.data = jsonData
|
||||
V2TIMManager.sharedInstance().modifyMessage(message) { code, desc, msg in
|
||||
if code == 0 {
|
||||
debugPrint("modifyMessage,success")
|
||||
} else {
|
||||
debugPrint("modifyMessage,code:\(code),message:\(String(describing: desc))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomMessageManager {
|
||||
private func changeReadyToSendMessage() {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 20) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.isReadyToSendMessage = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomMessageManager: TUINotificationProtocol {
|
||||
func onNotifyEvent(_ key: String, subKey: String, object anObject: Any?, param: [AnyHashable : Any]?) {
|
||||
guard key == TUICore_TUIChatNotify, subKey == TUICore_TUIChatNotify_SendMessageSubKey else { return }
|
||||
guard let code = param?[TUICore_TUIChatNotify_SendMessageSubKey_Code] as? Int, code == 0 else { return }
|
||||
guard let message = param?[TUICore_TUIChatNotify_SendMessageSubKey_Message] as? V2TIMMessage else { return }
|
||||
let messageModel = RoomMessageModel()
|
||||
messageModel.updateMessage(message: message)
|
||||
guard messageModel.messageId.count > 0, messageModel.roomState == .creating, messageModel.roomId == RoomManager.shared.roomId else { return }
|
||||
let roomInfo = TUIRoomInfo()
|
||||
roomInfo.roomId = messageModel.roomId
|
||||
RoomManager.shared.createRoom(roomInfo: roomInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// InviteJoinModel.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/7/3.
|
||||
// Signaling Model for inviting others into the room
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import RTCRoomEngine
|
||||
|
||||
class InviteJoinModel {
|
||||
private var message: RoomMessageModel
|
||||
private var inviter: TUILoginUserInfo
|
||||
var platform: String = "iOS"
|
||||
var version: Int = 1
|
||||
var businessID: String = "ROOM_INVITE_ACTION"
|
||||
var roomId: String = ""
|
||||
var extInfo: String = ""
|
||||
var data: [String:Any] = [:]
|
||||
|
||||
init(message: RoomMessageModel, inviter: TUILoginUserInfo) {
|
||||
self.message = message
|
||||
self.inviter = inviter
|
||||
self.roomId = message.roomId
|
||||
self.data = getInviteJoinModelDataDic()
|
||||
}
|
||||
|
||||
func getDicFromInviteJoinModel(inviteJoinModel: InviteJoinModel) -> [String: Any] {
|
||||
guard let dict = ["platform": inviteJoinModel.platform,
|
||||
"version": inviteJoinModel.version,
|
||||
"businessID": inviteJoinModel.businessID,
|
||||
"roomId": inviteJoinModel.roomId,
|
||||
"extInfo": inviteJoinModel.extInfo,
|
||||
"data": inviteJoinModel.data,
|
||||
] as? [String: Any] else { return [:] }
|
||||
return dict
|
||||
}
|
||||
|
||||
private func getInviteJoinModelDataDic() -> [String: Any] {
|
||||
let messageDic = message.getDictFromMessageModel()
|
||||
let inviterDic = getInviterUserInfoDic(inviter: inviter)
|
||||
return ["inviter":inviterDic, "roomInfo": messageDic]
|
||||
}
|
||||
|
||||
private func getInviterUserInfoDic(inviter: TUILoginUserInfo) -> [String: Any] {
|
||||
return ["avatarUrl": inviter.avatarUrl,
|
||||
"userId": inviter.userId,
|
||||
"userName": inviter.userName,
|
||||
]
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// RoomMessageModel.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
|
||||
class RoomMessageModel {
|
||||
enum RoomState: String {
|
||||
case creating
|
||||
case created
|
||||
case destroying
|
||||
case destroyed
|
||||
}
|
||||
private var message: V2TIMMessage?
|
||||
var version: Int = 1
|
||||
var businessID: String = BussinessID_GroupRoomMessage
|
||||
var groupId: String = ""
|
||||
var messageId: String = ""
|
||||
var roomId: String = ""
|
||||
var owner: String = ""
|
||||
var ownerName: String = ""
|
||||
var roomState: RoomState = .creating
|
||||
var memberCount: Int = 0
|
||||
var userList: [[String:Any]] = []
|
||||
init() {}
|
||||
|
||||
func updateMessage(message: V2TIMMessage) {
|
||||
self.message = message
|
||||
self.messageId = message.msgID ?? ""
|
||||
guard var dict = getMessageCustomElemDic(message: message) else { return }
|
||||
self.version = dict["version"] as? Int ?? 1
|
||||
self.businessID = dict["businessID"] as? String ?? BussinessID_GroupRoomMessage
|
||||
self.groupId = dict["groupId"] as? String ?? ""
|
||||
self.roomId = dict["roomId"] as? String ?? ""
|
||||
self.owner = dict["owner"] as? String ?? ""
|
||||
self.ownerName = dict["ownerName"] as? String ?? ""
|
||||
if let roomState = dict["roomState"] as? String {
|
||||
self.roomState = RoomState(rawValue: roomState) ?? .creating
|
||||
} else {
|
||||
self.roomState = .creating
|
||||
}
|
||||
self.userList = dict["userList"] as? [[String:Any]] ?? []
|
||||
self.memberCount = dict["memberCount"] as? Int ?? userList.count
|
||||
dict["messageId"] = self.messageId
|
||||
setMessageCustomElemData(dict: dict, message: message)
|
||||
}
|
||||
|
||||
private func getMessageCustomElemDic(message: V2TIMMessage) -> [String: Any]? {
|
||||
guard let customElem = message.customElem else { return nil}
|
||||
guard let customData = customElem.data else { return nil}
|
||||
guard let dataString = String(data: customData, encoding: String.Encoding.utf8) else { return nil }
|
||||
guard let data = dataString.data(using: String.Encoding.utf8) else { return nil }
|
||||
guard let dict = try? JSONSerialization.jsonObject(with: data,
|
||||
options: .mutableContainers) as? [String : Any] else { return nil }
|
||||
return dict
|
||||
}
|
||||
|
||||
private func setMessageCustomElemData(dict: [String: Any], message: V2TIMMessage) {
|
||||
guard let jsonString = dict.convertToString() else { return }
|
||||
let jsonData = jsonString.data(using: String.Encoding.utf8)
|
||||
message.customElem.data = jsonData
|
||||
}
|
||||
|
||||
func getDictFromMessageModel() -> [String: Any] {
|
||||
let dict = ["version": version,
|
||||
"businessID": businessID,
|
||||
"groupId": groupId,
|
||||
"roomId": roomId,
|
||||
"owner": owner,
|
||||
"ownerName": ownerName,
|
||||
"roomState": roomState.rawValue,
|
||||
"memberCount": memberCount,
|
||||
"messageId": messageId,
|
||||
"userList": userList,
|
||||
] as [String : Any]
|
||||
return dict
|
||||
}
|
||||
|
||||
func getMessage() -> V2TIMMessage? {
|
||||
return message
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// RoomObserver.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/7/3.
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
import TUICore
|
||||
|
||||
@objc public protocol RoomObserverListener {
|
||||
@objc optional func onRoomEnter(messageId: String, code: Int, message: String) -> Void
|
||||
@objc optional func onRoomExit(messageId: String) -> Void
|
||||
}
|
||||
|
||||
class RoomObserver: NSObject {
|
||||
var messageModel = RoomMessageModel()
|
||||
private let messageManager = RoomMessageManager.shared
|
||||
var engineManager: EngineManager {
|
||||
EngineManager.shared
|
||||
}
|
||||
var roomEngine: TUIRoomEngine {
|
||||
engineManager.roomEngine
|
||||
}
|
||||
lazy var userList: [[String: Any]] = {
|
||||
return messageModel.userList
|
||||
}()
|
||||
private lazy var userId: String = {
|
||||
return TUILogin.getUserID() ?? EngineManager.shared.store.currentUser.userId
|
||||
}()
|
||||
typealias Weak<T> = () -> T?
|
||||
private var listenerArray: [Weak<RoomObserverListener>] = []
|
||||
override init() {
|
||||
super.init()
|
||||
EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_RoomOwnerChanged, responder: self)
|
||||
EngineEventCenter.shared.subscribeEngine(event: .onExitedRoom, observer: self)
|
||||
EngineEventCenter.shared.subscribeEngine(event: .onDestroyedRoom, observer: self)
|
||||
}
|
||||
|
||||
func registerObserver() {
|
||||
roomEngine.addObserver(self)
|
||||
}
|
||||
|
||||
func unregisterObserver() {
|
||||
roomEngine.removeObserver(self)
|
||||
}
|
||||
|
||||
func addListener(listener: RoomObserverListener) {
|
||||
let weakObserver = { [weak listener] in return listener }
|
||||
self.listenerArray.append(weakObserver)
|
||||
}
|
||||
|
||||
func removeListener(listener: RoomObserverListener) {
|
||||
listenerArray.removeAll(where: {$0() === listener})
|
||||
}
|
||||
|
||||
private func refreshSource() {
|
||||
RoomManager.shared.roomId = nil
|
||||
TUILogin.setCurrentBusinessScene(.None)
|
||||
engineManager.roomEngine.removeObserver(self)
|
||||
userList = []
|
||||
unregisterObserver()
|
||||
}
|
||||
|
||||
func createdRoom() {
|
||||
TUILogin.setCurrentBusinessScene(.InMeetingRoom)
|
||||
messageModel.roomState = .created
|
||||
let userInfo = TUIUserInfo()
|
||||
userInfo.userId = userId
|
||||
userInfo.avatarUrl = TUILogin.getFaceUrl() ?? ""
|
||||
userInfo.userName = TUILogin.getNickName() ?? ""
|
||||
addUserList(userInfo: userInfo)
|
||||
let prefixUserList = Array(userList.prefix(5))
|
||||
messageManager.resendRoomMessage(message: messageModel, dic: ["userList":prefixUserList,
|
||||
"memberCount":userList.count,
|
||||
"roomState":RoomMessageModel.RoomState.created.rawValue,])
|
||||
}
|
||||
|
||||
func enteredRoom() {
|
||||
TUILogin.setCurrentBusinessScene(.InMeetingRoom)
|
||||
getUserList(nextSequence: 0)
|
||||
}
|
||||
|
||||
func exitedRoom() {
|
||||
RoomVideoFloatView.dismiss()
|
||||
userList = userList.filter { [weak self] userDic in
|
||||
guard let self = self, let userId = userDic["userId"] as? String else { return false }
|
||||
return userId != self.userId
|
||||
}
|
||||
if messageModel.owner == userId {
|
||||
let prefixUserList = Array(userList.prefix(5))
|
||||
messageManager.resendRoomMessage(message: messageModel, dic: ["userList":prefixUserList, "memberCount":userList.count])
|
||||
}
|
||||
for weakObserver in listenerArray {
|
||||
if let listener = weakObserver() {
|
||||
listener.onRoomExit?(messageId: self.messageModel.messageId)
|
||||
}
|
||||
}
|
||||
messageManager.isReadyToSendMessage = true
|
||||
refreshSource()
|
||||
}
|
||||
|
||||
func destroyedRoom() {
|
||||
RoomVideoFloatView.dismiss()
|
||||
messageModel.roomState = .destroyed
|
||||
if messageModel.owner == userId {
|
||||
messageManager.resendRoomMessage(message: messageModel, dic: ["roomState":RoomMessageModel.RoomState.destroyed.rawValue])
|
||||
}
|
||||
messageManager.isReadyToSendMessage = true
|
||||
refreshSource()
|
||||
}
|
||||
|
||||
deinit {
|
||||
EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_RoomOwnerChanged, responder: self)
|
||||
EngineEventCenter.shared.unsubscribeEngine(event: .onExitedRoom, observer: self)
|
||||
EngineEventCenter.shared.unsubscribeEngine(event: .onDestroyedRoom, observer: self)
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomObserver: TUIRoomObserver {
|
||||
func onRemoteUserEnterRoom(roomId: String, userInfo: TUIUserInfo) {
|
||||
addUserList(userInfo: userInfo)
|
||||
guard userList.count > 1 else { return }
|
||||
if messageModel.owner == userId {
|
||||
let prefixUserList = Array(userList.prefix(5))
|
||||
messageManager.resendRoomMessage(message: messageModel, dic: ["userList":prefixUserList,"memberCount":userList.count])
|
||||
}
|
||||
}
|
||||
|
||||
func onRemoteUserLeaveRoom(roomId: String, userInfo: TUIUserInfo) {
|
||||
userList = userList.filter { userDic in
|
||||
if let userId = userDic["userId"] as? String, userId != userInfo.userId {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if messageModel.owner == userId {
|
||||
let prefixUserList = Array(userList.prefix(5))
|
||||
messageManager.resendRoomMessage(message: messageModel, dic: ["memberCount":userList.count,"userList":prefixUserList])
|
||||
}
|
||||
}
|
||||
|
||||
func onRoomDismissed(roomId: String, reason: TUIRoomDismissedReason) {
|
||||
RoomVideoFloatView.dismiss()
|
||||
messageManager.isReadyToSendMessage = true
|
||||
refreshSource()
|
||||
}
|
||||
|
||||
func onKickedOutOfRoom(roomId: String, reason: TUIKickedOutOfRoomReason, message: String) {
|
||||
RoomVideoFloatView.dismiss()
|
||||
messageManager.isReadyToSendMessage = true
|
||||
refreshSource()
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomObserver {
|
||||
private func getUserList(nextSequence: Int) {
|
||||
engineManager.roomEngine.getUserList(nextSequence: nextSequence) { [weak self] list, nextSequence in
|
||||
guard let self = self else { return }
|
||||
list.forEach { userInfo in
|
||||
self.addUserList(userInfo: userInfo)
|
||||
}
|
||||
if nextSequence != 0 {
|
||||
self.getUserList(nextSequence: nextSequence)
|
||||
}
|
||||
} onError: { code, message in
|
||||
debugPrint("getUserList:code:\(code),message:\(message)")
|
||||
}
|
||||
}
|
||||
|
||||
private func addUserList(userInfo: TUIUserInfo) {
|
||||
if getUserItem(userInfo.userId) == nil {
|
||||
let userDic: [String: Any] = ["userId":userInfo.userId,"userName":userInfo.userName,"faceUrl": userInfo.avatarUrl]
|
||||
userList.append(userDic)
|
||||
}
|
||||
}
|
||||
|
||||
private func getUserItem(_ userId: String) -> String? {
|
||||
for userDic in userList {
|
||||
if let userIdString = userDic["userId"] as? String, userIdString == userId {
|
||||
return userIdString
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomObserver: RoomKitUIEventResponder {
|
||||
func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable : Any]?) {
|
||||
switch key {
|
||||
case .TUIRoomKitService_RoomOwnerChanged:
|
||||
guard let userId = info?["owner"] as? String else { return }
|
||||
messageManager.resendRoomMessage(message: messageModel, dic: ["owner": userId])
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomObserver: RoomEngineEventResponder {
|
||||
func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) {
|
||||
switch name {
|
||||
case .onExitedRoom:
|
||||
exitedRoom()
|
||||
case .onDestroyedRoom:
|
||||
destroyedRoom()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// RoomMessageExtensionObserver.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/6/2.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import TIMCommon
|
||||
|
||||
class RoomMessageExtensionObserver: NSObject {
|
||||
static let shared = RoomMessageExtensionObserver()
|
||||
let roomMessageManager: RoomMessageManager = RoomMessageManager.shared
|
||||
var settingMenuNavigationController: UINavigationController?
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
@objc private func pushChatExtensionRoomSettingsViewController() {
|
||||
if let nav = settingMenuNavigationController {
|
||||
let vc = ChatExtensionRoomSettingsViewController(isOpenMicrophone: EngineManager.shared.store.isOpenMicrophone,
|
||||
isOpenCamera: EngineManager.shared.store.isOpenCamera)
|
||||
nav.pushViewController(vc, animated: true)
|
||||
}
|
||||
}
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
extension RoomMessageExtensionObserver: TUIExtensionProtocol {
|
||||
func onGetExtension(_ key: String, param: [AnyHashable : Any]?) -> [TUIExtensionInfo]? {
|
||||
if key == TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID {
|
||||
guard let groupID = param?[TUICore_TUIChatExtension_InputViewMoreItem_GroupID] as? String, groupID != "" else { return nil }
|
||||
guard let isNeedRoom = param?[TUICore_TUIChatExtension_InputViewMoreItem_FilterRoom] as? Bool, !isNeedRoom else { return nil }
|
||||
let info = TUIExtensionInfo()
|
||||
info.weight = 200
|
||||
info.text = .meetingText
|
||||
let defaultImage = UIImage(named: "room_quick_meeting", in: tuiRoomKitBundle(), compatibleWith: nil) ?? UIImage()
|
||||
info.icon = TUICoreThemeConvert.getTUIDynamicImage(imageKey: "room_quick_meeting_image", defaultImage: defaultImage)
|
||||
info.onClicked = { [weak self] param in
|
||||
guard let self = self else { return }
|
||||
if let vc = param[TUICore_TUIChatExtension_InputViewMoreItem_PushVC] as? UINavigationController {
|
||||
self.roomMessageManager.navigateController = vc
|
||||
}
|
||||
if let groupId = param[TUICore_TUIChatExtension_InputViewMoreItem_GroupID] as? String {
|
||||
self.roomMessageManager.groupId = groupId
|
||||
}
|
||||
self.roomMessageManager.sendRoomMessageToGroup()
|
||||
}
|
||||
return [info]
|
||||
}
|
||||
if key == TUICore_TUIContactExtension_MeSettingMenu_ClassicExtensionID {
|
||||
if let nav = param?[TUICore_TUIContactExtension_MeSettingMenu_Nav] as? UINavigationController {
|
||||
self.settingMenuNavigationController = nav
|
||||
}
|
||||
let data = TUICommonTextCellData()
|
||||
data.key = .roomDeviceSetText
|
||||
data.showAccessory = true
|
||||
let cell = TUICommonTextCell()
|
||||
cell.fill(with: data)
|
||||
cell.mm_h = 60
|
||||
cell.mm_w = UIScreen.main.bounds.width
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(pushChatExtensionRoomSettingsViewController))
|
||||
cell.addGestureRecognizer(tap)
|
||||
let info = TUIExtensionInfo()
|
||||
let param = [TUICore_TUIContactExtension_MeSettingMenu_Weight: 460,
|
||||
TUICore_TUIContactExtension_MeSettingMenu_View: cell,
|
||||
TUICore_TUIContactExtension_MeSettingMenu_Data: data,
|
||||
] as [String : Any]
|
||||
info.data = param
|
||||
return [info]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static var meetingText: String {
|
||||
localized("Quick meeting")
|
||||
}
|
||||
static var roomDeviceSetText: String {
|
||||
localized("Meeting Settings")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// RoomSetListItemData.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/6/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class RoomSetListItemData {
|
||||
var titleText: String = ""
|
||||
var isSwitchOn: Bool = false
|
||||
var action: ((Any)->Void)?
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// TUIRoomImAccessFactory.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/10.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
|
||||
class TUIRoomImAccessFactory: NSObject {
|
||||
static let shared = TUIRoomImAccessFactory()
|
||||
private override init() {
|
||||
super.init()
|
||||
}
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
extension TUIRoomImAccessFactory: TUIObjectProtocol {
|
||||
func onCreateObject(_ method: String, param: [AnyHashable : Any]?) -> Any? {
|
||||
if method == TUICore_TUIRoomImAccessFactory_GetRoomMessageViewMethod {
|
||||
guard let message = param?[TUICore_TUIRoomImAccessFactory_GetRoomMessageViewMethod_Message] as?
|
||||
V2TIMMessage else { return UIView(frame: .zero) }
|
||||
let roomMessageModel = RoomMessageModel()
|
||||
roomMessageModel.updateMessage(message: message)
|
||||
let viewModel = RoomMessageViewModel(message: roomMessageModel)
|
||||
let view = RoomMessageView(viewModel: viewModel)
|
||||
return view
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// TUIRoomImAccessService.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/6/2.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
|
||||
class TUIRoomImAccessService: NSObject, TUIServiceProtocol {
|
||||
static let shared = TUIRoomImAccessService()
|
||||
var isShownInvitedToJoinRoomView: Bool = false
|
||||
weak var inviteViewController: UIViewController?
|
||||
var inviteWindow: UIWindow?
|
||||
private override init() {
|
||||
super.init()
|
||||
initRoomMessage()
|
||||
initSignalingListener()
|
||||
initThemeResource()
|
||||
}
|
||||
func initRoomMessage() {
|
||||
TUICore.callService(TUICore_TUIChatService, method: TUICore_TUIChatService_AppendCustomMessageMethod, param:
|
||||
[BussinessID: BussinessID_GroupRoomMessage, TMessageCell_Name: "RoomMessageBubbleCell",
|
||||
TMessageCell_Data_Name:"RoomMessageBubbleCellData",])
|
||||
}
|
||||
func initSignalingListener() {
|
||||
V2TIMManager.sharedInstance().addSignalingListener(listener: self)
|
||||
}
|
||||
func initThemeResource() {
|
||||
TUICoreThemeConvert.initThemeResource()
|
||||
}
|
||||
}
|
||||
|
||||
extension TUIRoomImAccessService: V2TIMSignalingListener {
|
||||
func onReceiveNewInvitation(_ inviteID: String, inviter: String, groupID: String, inviteeList: [String], data: String?) {
|
||||
guard let data = data else { return }
|
||||
let dict = data.convertToDic()
|
||||
guard let businessID = dict?["businessID"] as? String else { return }
|
||||
guard businessID == "ROOM_INVITE_ACTION" else { return }
|
||||
guard let roomId = dict?["roomId"] as? String else { return }
|
||||
guard let userId = TUILogin.getUserID() else { return }
|
||||
let dataDic = dict?["data"] as? NSDictionary ?? [:]
|
||||
let inviter = dataDic["inviter"] as? NSDictionary ?? [:]
|
||||
let avatarUrl = inviter["avatarUrl"] as? String ?? ""
|
||||
let inviterUserName = inviter["userName"] as? String ?? ""
|
||||
guard inviteeList.contains(userId) else { return }
|
||||
let businessScene = TUILogin.getCurrentBusinessScene()
|
||||
guard businessScene == .None || businessScene == .InMeetingRoom else { return }
|
||||
showInvitedToJoinRoomView(inviterUserName: inviterUserName, inviteUserAvatarUrl: avatarUrl, roomId: roomId)
|
||||
}
|
||||
private func showInvitedToJoinRoomView(inviterUserName: String, inviteUserAvatarUrl: String, roomId: String) {
|
||||
if isShownInvitedToJoinRoomView {
|
||||
return
|
||||
}
|
||||
isShownInvitedToJoinRoomView = true
|
||||
let inviteViewController = InvitedToJoinRoomViewController(inviteUserName: inviterUserName,inviteUserAvatarUrl:
|
||||
inviteUserAvatarUrl, roomId: roomId)
|
||||
let nav = UINavigationController(rootViewController: inviteViewController)
|
||||
nav.setNavigationBarHidden(true, animated: true)
|
||||
inviteWindow = UIWindow(frame: UIScreen.main.bounds)
|
||||
inviteWindow?.windowLevel = .alert - 1
|
||||
guard let inviteWindow = inviteWindow else { return }
|
||||
inviteWindow.rootViewController = nav
|
||||
inviteWindow.isHidden = false
|
||||
inviteWindow.makeKeyAndVisible()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// BusinessSceneUtil.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/7/3.
|
||||
// Determine the current scene and set the current scene
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
|
||||
class BusinessSceneUtil {
|
||||
class func canJoinRoom() -> Bool {
|
||||
let businessScene = TUILogin.getCurrentBusinessScene()
|
||||
return businessScene == .InMeetingRoom || businessScene == .None
|
||||
}
|
||||
|
||||
class func setJoinRoomFlag() {
|
||||
TUILogin.setCurrentBusinessScene(.InMeetingRoom)
|
||||
}
|
||||
|
||||
class func clearJoinRoomFlag() {
|
||||
TUILogin.setCurrentBusinessScene(.None)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TUICoreThemeConvert.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2024/1/29.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
|
||||
class TUICoreThemeConvert {
|
||||
|
||||
static func initThemeResource() {
|
||||
TUIThemeManager.share().registerThemeResourcePath(getTUIRoomKitThemePath(), for: .roomKit)
|
||||
}
|
||||
|
||||
static func getTUIRoomKitThemePath() -> String {
|
||||
return getTUIGetBundlePath("TUIRoomKitTheme", TUICore_TUIRoomImAccessService)
|
||||
}
|
||||
|
||||
static func getTUIDynamicImage(imageKey: String, defaultImage: UIImage) -> UIImage? {
|
||||
return TUITheme.dynamicImage(imageKey, module: .roomKit, defaultImage: defaultImage)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user