增加换肤功能
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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// NSObject+TUIRoomMessageExtensionObserver.h
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSObject (TUIRoomMessageExtensionObserver)
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// NSObject+TUIRoomMessageExtensionObserver.m
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSObject+TUIRoomMessageExtensionObserver.h"
|
||||
#import "TUICore.h"
|
||||
|
||||
@implementation NSObject (TUIRoomMessageExtensionObserver)
|
||||
|
||||
+ (void) load {
|
||||
if ([self respondsToSelector:@selector(tuiRoomKitExtensionLoad)]) {
|
||||
[self performSelector:@selector(tuiRoomKitExtensionLoad)];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// ServiceInitializer.swift
|
||||
// TUILiveKit
|
||||
//
|
||||
// Created by WesleyLei on 2023/10/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import RTCRoomEngine
|
||||
|
||||
extension NSObject {
|
||||
@objc class func liveKitExtensionLoad() {
|
||||
ServiceInitializer.shared.registerObserver()
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceInitializer {
|
||||
|
||||
private var isLoginEngine: Bool = false
|
||||
static let shared = ServiceInitializer()
|
||||
private var invitationManager: TUIConferenceInvitationManager?
|
||||
private var invitationObserver: InvitationObserverService?
|
||||
|
||||
private init() {
|
||||
}
|
||||
|
||||
func registerObserver() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(logoutSuccess(_:)),
|
||||
name: NSNotification.Name(rawValue: NSNotification.Name.TUILogoutSuccess.rawValue),
|
||||
object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(loginSuccess(_:)),
|
||||
name: NSNotification.Name(rawValue: NSNotification.Name.TUILoginSuccess.rawValue),
|
||||
object: nil)
|
||||
}
|
||||
|
||||
@objc private func logoutSuccess(_ notification: Notification) {
|
||||
logout(onSuccess: nil, onError: nil)
|
||||
removeInvitationObserver()
|
||||
}
|
||||
|
||||
@objc private func loginSuccess(_ notification: Notification) {
|
||||
login(onSuccess: nil, onError: nil)
|
||||
addInvitationObserver()
|
||||
}
|
||||
|
||||
private func addInvitationObserver() {
|
||||
invitationObserver = InvitationObserverService.shared
|
||||
invitationManager = TUIRoomEngine.sharedInstance().getExtension(extensionType: .conferenceInvitationManager) as? TUIConferenceInvitationManager
|
||||
guard let invitationObserver = invitationObserver else { return }
|
||||
invitationManager?.addObserver(invitationObserver)
|
||||
}
|
||||
|
||||
private func removeInvitationObserver() {
|
||||
guard let invitationObserver = invitationObserver else { return }
|
||||
invitationManager?.removeObserver(invitationObserver)
|
||||
}
|
||||
|
||||
func login(onSuccess: TUISuccessBlock?, onError: TUIErrorBlock?) {
|
||||
if isLoginEngine {
|
||||
onSuccess?()
|
||||
} else {
|
||||
let sdkAppId = Int(TUILogin.getSdkAppID())
|
||||
let userId = TUILogin.getUserID() ?? ""
|
||||
let userSig = TUILogin.getUserSig() ?? ""
|
||||
ServiceInitializer.login(sdkAppId: sdkAppId, userId: userId, userSig: userSig) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.isLoginEngine = true
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logout(onSuccess: TUISuccessBlock?, onError: TUIErrorBlock?) {
|
||||
if isLoginEngine {
|
||||
ServiceInitializer.logout {
|
||||
onSuccess?()
|
||||
} onError: { code, message in
|
||||
onError?(code, message)
|
||||
}
|
||||
self.isLoginEngine = false
|
||||
} else {
|
||||
onSuccess?()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension ServiceInitializer {
|
||||
static func login(sdkAppId:Int,userId:String,userSig:String,onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
TUIRoomEngine.login(sdkAppId: sdkAppId, userId: userId, userSig: userSig) {
|
||||
onSuccess()
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
|
||||
static func logout(onSuccess: @escaping TUISuccessBlock, onError: @escaping TUIErrorBlock) {
|
||||
TUIRoomEngine.logout {
|
||||
onSuccess()
|
||||
} onError: { code, message in
|
||||
onError(code, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// TUIRoomMessageExtensionObserver.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
|
||||
extension NSObject {
|
||||
@objc class func tuiRoomKitExtensionLoad() {
|
||||
TUICore.registerExtension(TUICore_TUIChatExtension_InputViewMoreItem_ClassicExtensionID, object: RoomMessageExtensionObserver.shared)
|
||||
TUICore.registerService(TUICore_TUIRoomImAccessService, object: TUIRoomImAccessService.shared)
|
||||
TUICore.registerObjectFactory(TUICore_TUIRoomImAccessFactory, objectFactory: TUIRoomImAccessFactory.shared)
|
||||
TUICore.registerExtension(TUICore_TUIContactExtension_MeSettingMenu_ClassicExtensionID, object: RoomMessageExtensionObserver.shared)
|
||||
ServiceInitializer.shared.registerObserver()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// ChatExtensionRoomSettingsViewController.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/6/26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ChatExtensionRoomSettingsViewController: UIViewController {
|
||||
let roomView: ChatExtensionRoomSettingsView
|
||||
init(isOpenMicrophone: Bool, isOpenCamera: Bool) {
|
||||
let viewModel = ChatExtensionRoomSettingsViewModel(isOpenMicrophone: isOpenMicrophone, isOpenCamera: isOpenCamera)
|
||||
roomView = ChatExtensionRoomSettingsView(viewModel: viewModel)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
view = roomView
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
setNavBar()
|
||||
}
|
||||
|
||||
private func setNavBar() {
|
||||
navigationItem.title = .roomDeviceSetText
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
private extension String {
|
||||
static var roomDeviceSetText: String {
|
||||
localized("Meeting Settings")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// InvitedToJoinRoomViewController.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/25.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class InvitedToJoinRoomViewController: UIViewController {
|
||||
let roomView: InvitedToJoinRoomView
|
||||
let viewModel: InvitedToJoinRoomViewModel
|
||||
init(inviteUserName: String,inviteUserAvatarUrl: String, roomId: String) {
|
||||
viewModel = InvitedToJoinRoomViewModel(inviteUserName: inviteUserName, inviteUserAvatarUrl:inviteUserAvatarUrl, roomId: roomId)
|
||||
roomView = InvitedToJoinRoomView(viewModel: viewModel)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
view = roomView
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
viewModel.stopPlay()
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
//
|
||||
// ChatExtensionRoomSettingsItemView.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/6/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ChatExtensionRoomSettingsItemView: UIView {
|
||||
let itemData: RoomSetListItemData
|
||||
let titleLabel: UILabel = {
|
||||
let view = UILabel()
|
||||
view.backgroundColor = .clear
|
||||
view.textColor = .black
|
||||
view.font = UIFont(name: "PingFangSC-Medium", size: 14)
|
||||
view.minimumScaleFactor = 0.5
|
||||
return view
|
||||
}()
|
||||
|
||||
let rightSwitch: UISwitch = {
|
||||
let view = UISwitch()
|
||||
view.isOn = true
|
||||
view.onTintColor = UIColor(0x0062E3)
|
||||
return view
|
||||
}()
|
||||
|
||||
let line: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = .gray
|
||||
view.alpha = 0.3
|
||||
return view
|
||||
}()
|
||||
|
||||
init(itemData: RoomSetListItemData) {
|
||||
self.itemData = itemData
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private var isViewReady: Bool = false
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
guard !isViewReady else { return }
|
||||
constructViewHierarchy()
|
||||
activateConstraints()
|
||||
bindInteraction()
|
||||
isViewReady = true
|
||||
}
|
||||
|
||||
private func constructViewHierarchy() {
|
||||
addSubview(titleLabel)
|
||||
addSubview(rightSwitch)
|
||||
addSubview(line)
|
||||
}
|
||||
|
||||
private func activateConstraints() {
|
||||
titleLabel.snp.makeConstraints { make in
|
||||
make.leading.equalToSuperview().offset(10)
|
||||
make.centerY.equalToSuperview()
|
||||
make.width.equalTo(100.scale375())
|
||||
}
|
||||
rightSwitch.snp.makeConstraints { make in
|
||||
make.trailing.equalToSuperview().offset(-10)
|
||||
make.centerY.equalToSuperview()
|
||||
}
|
||||
line.snp.makeConstraints { make in
|
||||
make.leading.equalToSuperview().offset(10)
|
||||
make.trailing.equalToSuperview().offset(-10)
|
||||
make.bottom.equalToSuperview().offset(-2)
|
||||
make.height.equalTo(0.5)
|
||||
}
|
||||
}
|
||||
|
||||
private func bindInteraction() {
|
||||
backgroundColor = .white
|
||||
titleLabel.text = itemData.titleText
|
||||
rightSwitch.isOn = itemData.isSwitchOn
|
||||
rightSwitch.addTarget(self, action: #selector(switchAction(sender:)), for: .touchUpInside)
|
||||
}
|
||||
@objc func switchAction(sender: UISwitch) {
|
||||
itemData.action?(sender)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// ChatExtensionRoomSettingsView.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/6/26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ChatExtensionRoomSettingsView: UIView {
|
||||
let viewModel : ChatExtensionRoomSettingsViewModel
|
||||
|
||||
let stackView: UIStackView = {
|
||||
let view = UIStackView()
|
||||
view.axis = .vertical
|
||||
view.alignment = .center
|
||||
view.distribution = .equalSpacing
|
||||
view.spacing = 0
|
||||
return view
|
||||
}()
|
||||
|
||||
init(viewModel: ChatExtensionRoomSettingsViewModel) {
|
||||
self.viewModel = viewModel
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - view layout
|
||||
private var isViewReady: Bool = false
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
guard !isViewReady else { return }
|
||||
backgroundColor = .white
|
||||
constructViewHierarchy()
|
||||
activateConstraints()
|
||||
isViewReady = true
|
||||
}
|
||||
|
||||
private func constructViewHierarchy() {
|
||||
addSubview(stackView)
|
||||
for item in viewModel.roomSettingsViewItems {
|
||||
let view = ChatExtensionRoomSettingsItemView(itemData: item)
|
||||
stackView.addArrangedSubview(view)
|
||||
view.snp.makeConstraints { make in
|
||||
make.height.equalTo(60.scale375())
|
||||
make.width.equalToSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func activateConstraints() {
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(10)
|
||||
make.leading.equalToSuperview().offset(10)
|
||||
make.trailing.equalToSuperview().offset(-10)
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
//
|
||||
// RoomaInviteView.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class InvitedToJoinRoomView: UIView {
|
||||
let viewModel : InvitedToJoinRoomViewModel
|
||||
let userNameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 30)
|
||||
label.textColor = .white
|
||||
return label
|
||||
}()
|
||||
let inviteLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 15)
|
||||
label.text = .inviteMeetingText
|
||||
label.textColor = .white
|
||||
return label
|
||||
}()
|
||||
let userImageView: UIImageView = {
|
||||
let image = UIImageView()
|
||||
return image
|
||||
}()
|
||||
|
||||
let disagreeButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
return button
|
||||
}()
|
||||
let disagreeImageView: UIImageView = {
|
||||
let image = UIImage(named: "room_hangup", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
let imageView = UIImageView(image: image)
|
||||
return imageView
|
||||
}()
|
||||
let disagreeLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = .declineText
|
||||
label.textColor = .red
|
||||
label.textAlignment = .center
|
||||
label.font = UIFont.systemFont(ofSize: 20.0)
|
||||
return label
|
||||
}()
|
||||
let agreeButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
return button
|
||||
}()
|
||||
let agreeImageView: UIImageView = {
|
||||
let image = UIImage(named: "room_handsfree_on", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
let imageView = UIImageView(image: image)
|
||||
return imageView
|
||||
}()
|
||||
let agreeLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.text = .agreeText
|
||||
label.textColor = .white
|
||||
label.textAlignment = .center
|
||||
label.font = UIFont.systemFont(ofSize: 20.0)
|
||||
return label
|
||||
}()
|
||||
|
||||
init(viewModel: InvitedToJoinRoomViewModel) {
|
||||
self.viewModel = viewModel
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// MARK: - view layout
|
||||
private var isViewReady: Bool = false
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
guard !isViewReady else { return }
|
||||
backgroundColor = UIColor(0x242424)
|
||||
constructViewHierarchy()
|
||||
activateConstraints()
|
||||
bindInteraction()
|
||||
isViewReady = true
|
||||
}
|
||||
private func constructViewHierarchy() {
|
||||
addSubview(userNameLabel)
|
||||
addSubview(inviteLabel)
|
||||
addSubview(userImageView)
|
||||
addSubview(disagreeButton)
|
||||
disagreeButton.addSubview(disagreeImageView)
|
||||
disagreeButton.addSubview(disagreeLabel)
|
||||
addSubview(agreeButton)
|
||||
agreeButton.addSubview(agreeImageView)
|
||||
agreeButton.addSubview(agreeLabel)
|
||||
}
|
||||
private func activateConstraints() {
|
||||
userNameLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(30)
|
||||
make.trailing.equalToSuperview().offset(-130)
|
||||
}
|
||||
inviteLabel.snp.makeConstraints { make in
|
||||
make.trailing.equalTo(userNameLabel)
|
||||
make.top.equalTo(userNameLabel.snp.bottom).offset(15)
|
||||
}
|
||||
userImageView.snp.makeConstraints { make in
|
||||
make.top.equalTo(userNameLabel)
|
||||
make.trailing.equalToSuperview().offset(-20)
|
||||
make.width.height.equalTo(80)
|
||||
}
|
||||
disagreeButton.snp.makeConstraints { make in
|
||||
make.bottom.equalToSuperview().offset(-100)
|
||||
make.width.equalTo(80)
|
||||
make.height.equalTo(120)
|
||||
make.leading.equalToSuperview().offset(50)
|
||||
}
|
||||
disagreeImageView.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview()
|
||||
make.centerX.equalToSuperview()
|
||||
make.width.height.equalTo(80)
|
||||
}
|
||||
disagreeLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(disagreeImageView.snp.bottom).offset(5)
|
||||
make.centerX.equalToSuperview()
|
||||
make.width.equalTo(80)
|
||||
make.bottom.equalToSuperview()
|
||||
}
|
||||
agreeButton.snp.makeConstraints { make in
|
||||
make.bottom.equalTo(disagreeButton)
|
||||
make.width.equalTo(80)
|
||||
make.height.equalTo(120)
|
||||
make.trailing.equalToSuperview().offset(-50)
|
||||
}
|
||||
agreeImageView.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview()
|
||||
make.centerX.equalToSuperview()
|
||||
make.width.height.equalTo(80)
|
||||
}
|
||||
agreeLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(agreeImageView.snp.bottom).offset(5)
|
||||
make.centerX.equalToSuperview()
|
||||
make.width.equalTo(80)
|
||||
make.bottom.equalToSuperview()
|
||||
}
|
||||
}
|
||||
private func bindInteraction() {
|
||||
disagreeButton.addTarget(self, action: #selector(disagreeAction(sender:)), for: .touchUpInside)
|
||||
agreeButton.addTarget(self, action: #selector(agreeAction(sender:)), for: .touchUpInside)
|
||||
viewModel.startPlay()
|
||||
setupViewState()
|
||||
}
|
||||
func setupViewState() {
|
||||
userNameLabel.text = viewModel.inviteUserName
|
||||
let placeholderImage = UIImage(named: "room_default_avatar", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
userImageView.sd_setImage(with: URL(string: viewModel.avatarUrl), placeholderImage: placeholderImage)
|
||||
}
|
||||
@objc private func disagreeAction(sender: UIButton) {
|
||||
viewModel.disagreeAction()
|
||||
}
|
||||
@objc private func agreeAction(sender: UIButton) {
|
||||
viewModel.agreeAction()
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static var inviteMeetingText: String {
|
||||
localized("Invite you to a meeting")
|
||||
}
|
||||
static var declineText: String {
|
||||
localized("Decline")
|
||||
}
|
||||
static var agreeText: String {
|
||||
localized("Agree")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// RoomMsgCell.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TIMCommon
|
||||
|
||||
@objc(RoomMessageBubbleCell)
|
||||
class RoomMessageBubbleCell: TUIBubbleMessageCell {
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
guard self.container != nil else { return }
|
||||
let message = RoomMessageModel()
|
||||
let view = RoomMessageView(viewModel: RoomMessageViewModel(message: message))
|
||||
self.container.addSubview(view)
|
||||
view.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
override func fill(with data: TUIBubbleMessageCellData) {
|
||||
super.fill(with: data)
|
||||
let customData = data as? RoomMessageBubbleCellData
|
||||
guard let messageModel = customData?.messageModel as? RoomMessageModel else { return }
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
let subviewArray = self.container.subviews
|
||||
for view in subviewArray where view is RoomMessageView {
|
||||
guard let messageView = view as? RoomMessageView else { continue }
|
||||
messageView.viewModel.changeMessage(message: messageModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override class func getContentSize(_ data: TUIMessageCellData?) -> CGSize {
|
||||
return CGSize(width: 238, height: 157)
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
328
TUIKit/TUIRoomKit/RoomExtension/View/View/RoomMessageView.swift
Normal file
328
TUIKit/TUIRoomKit/RoomExtension/View/View/RoomMessageView.swift
Normal file
@@ -0,0 +1,328 @@
|
||||
//
|
||||
// RoomMessageView.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/9.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
class RoomMessageView: UIView {
|
||||
let viewModel: RoomMessageViewModel
|
||||
private var viewArray: [UIView] = []
|
||||
let roomStatusView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor(0xD1E4FD)
|
||||
return view
|
||||
}()
|
||||
let roomStatusImageView: UIImageView = {
|
||||
let image = UIImageView()
|
||||
return image
|
||||
}()
|
||||
let roomStatusLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 15)
|
||||
return label
|
||||
}()
|
||||
let roomNameLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 20)
|
||||
label.textColor = .black
|
||||
label.adjustsFontSizeToFitWidth = true
|
||||
return label
|
||||
}()
|
||||
let stackView: UIStackView = {
|
||||
let view = UIStackView()
|
||||
view.axis = .horizontal
|
||||
view.alignment = .leading
|
||||
view.distribution = .equalSpacing
|
||||
view.spacing = 2
|
||||
return view
|
||||
}()
|
||||
lazy var inviteUserButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setImage(UIImage(named: "room_chat_invite", in: tuiRoomKitBundle(), compatibleWith: nil), for: .normal)
|
||||
return button
|
||||
}()
|
||||
let userNumberLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 15)
|
||||
label.adjustsFontSizeToFitWidth = true
|
||||
label.textColor = UIColor(0x888888)
|
||||
label.textAlignment = isRTL ? .right : .left
|
||||
return label
|
||||
}()
|
||||
let enterRoomButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setTitle(.enterMeetingText, for: .normal)
|
||||
button.setTitleColor(.white, for: .normal)
|
||||
button.setBackgroundImage(UIColor.tui_color(withHex: "147AFF").trans2Image(), for: .normal)
|
||||
button.setTitle(.alreadyEnteredMeeting, for: .selected)
|
||||
button.setTitleColor(UIColor(0x999999), for: .selected)
|
||||
button.setBackgroundImage(UIColor.tui_color(withHex: "F3F4F4").trans2Image(), for: .selected)
|
||||
button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 15)
|
||||
return button
|
||||
}()
|
||||
let enterRoomStatusLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.adjustsFontSizeToFitWidth = true
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 15)
|
||||
label.textColor = UIColor(0x888888)
|
||||
label.textAlignment = .center
|
||||
return label
|
||||
}()
|
||||
init(viewModel: RoomMessageViewModel) {
|
||||
self.viewModel = viewModel
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
// MARK: - view layout
|
||||
private var isViewReady: Bool = false
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
guard !isViewReady else { return }
|
||||
backgroundColor = .white
|
||||
constructViewHierarchy()
|
||||
activateConstraints()
|
||||
bindInteraction()
|
||||
isViewReady = true
|
||||
}
|
||||
|
||||
func constructViewHierarchy() {
|
||||
addSubview(roomStatusView)
|
||||
roomStatusView.addSubview(roomStatusImageView)
|
||||
roomStatusView.addSubview(roomStatusLabel)
|
||||
roomStatusView.addSubview(roomNameLabel)
|
||||
roomStatusView.addSubview(stackView)
|
||||
setupUserStackView()
|
||||
roomStatusView.addSubview(inviteUserButton)
|
||||
addSubview(enterRoomStatusLabel)
|
||||
addSubview(userNumberLabel)
|
||||
addSubview(enterRoomButton)
|
||||
}
|
||||
func activateConstraints() {
|
||||
roomStatusView.snp.makeConstraints { make in
|
||||
make.top.leading.trailing.equalToSuperview()
|
||||
make.height.equalTo(110)
|
||||
}
|
||||
roomStatusImageView.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(15)
|
||||
make.leading.equalToSuperview().offset(12)
|
||||
}
|
||||
roomStatusLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(roomStatusImageView)
|
||||
make.leading.equalTo(roomStatusImageView.snp.trailing).offset(4)
|
||||
}
|
||||
roomNameLabel.snp.makeConstraints { make in
|
||||
make.top.equalTo(roomStatusImageView.snp.bottom).offset(9)
|
||||
make.leading.equalTo(roomStatusImageView)
|
||||
make.trailing.equalToSuperview().offset(-12)
|
||||
}
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.leading.equalTo(roomNameLabel)
|
||||
make.bottom.equalToSuperview().offset(-4)
|
||||
}
|
||||
inviteUserButton.snp.makeConstraints { make in
|
||||
make.leading.equalTo(stackView.snp.trailing).offset(2)
|
||||
make.width.height.equalTo(24)
|
||||
make.bottom.equalToSuperview().offset(-4)
|
||||
}
|
||||
userNumberLabel.snp.makeConstraints { make in
|
||||
make.bottom.equalToSuperview().offset(-10)
|
||||
make.leading.equalToSuperview().offset(12)
|
||||
make.width.equalTo(100)
|
||||
make.height.equalTo(20)
|
||||
}
|
||||
enterRoomButton.snp.makeConstraints { make in
|
||||
make.trailing.equalToSuperview().offset(-12)
|
||||
make.bottom.equalToSuperview().offset(-10)
|
||||
make.width.equalTo(70)
|
||||
make.height.equalTo(27)
|
||||
}
|
||||
enterRoomStatusLabel.snp.makeConstraints { make in
|
||||
make.bottom.equalToSuperview().offset(-10)
|
||||
make.centerX.equalToSuperview()
|
||||
make.width.equalTo(150)
|
||||
make.height.equalTo(20)
|
||||
}
|
||||
}
|
||||
func bindInteraction() {
|
||||
viewModel.viewResponder = self
|
||||
enterRoomButton.addTarget(self, action: #selector(enterRoomAction(sender:)), for: .touchUpInside)
|
||||
inviteUserButton.addTarget(self, action: #selector(inviteUserAction(sender:)), for: .touchUpInside)
|
||||
let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(sender:)))
|
||||
addGestureRecognizer(longPressGesture)
|
||||
layer.masksToBounds = true
|
||||
layer.borderWidth = 0.5
|
||||
layer.borderColor = UIColor(0xDDDDDD).cgColor
|
||||
setupViewState()
|
||||
}
|
||||
|
||||
@objc func enterRoomAction(sender: UIButton) {
|
||||
viewModel.enterRoomAction()
|
||||
}
|
||||
|
||||
@objc func inviteUserAction(sender: UIButton) {
|
||||
viewModel.inviteUserAction()
|
||||
}
|
||||
|
||||
@objc func longPressAction(sender: UIView) {
|
||||
return
|
||||
}
|
||||
|
||||
func setupViewState() {
|
||||
roomNameLabel.text = (viewModel.message.ownerName ) + .quickMeetingText
|
||||
if viewModel.message.owner != viewModel.userId {
|
||||
inviteUserButton.isHidden = true
|
||||
} else {
|
||||
inviteUserButton.isHidden = false
|
||||
}
|
||||
switch viewModel.message.roomState {
|
||||
case .creating:
|
||||
roomStatusImageView.image = UIImage(named: "room_is_creating", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
roomStatusLabel.text = .meetingText
|
||||
enterRoomStatusLabel.isHidden = false
|
||||
enterRoomStatusLabel.text = .startingMeetingText + "..."
|
||||
userNumberLabel.isHidden = true
|
||||
enterRoomButton.isHidden = true
|
||||
roomStatusView.backgroundColor = UIColor(0xDCEAFD)
|
||||
case .created:
|
||||
roomStatusImageView.image = UIImage(named: "room_created_success", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
roomStatusLabel.text = .meetingText + "." + .inProgressText
|
||||
roomStatusLabel.textColor = UIColor(0x15B72D)
|
||||
enterRoomButton.isHidden = false
|
||||
userNumberLabel.isHidden = false
|
||||
enterRoomStatusLabel.isHidden = true
|
||||
if viewModel.message.memberCount > 1 {
|
||||
userNumberLabel.text = String(viewModel.message.memberCount) + .peopleEnteredMeetingText
|
||||
} else {
|
||||
userNumberLabel.text = .waitingMembersEnterMeetingText
|
||||
}
|
||||
roomStatusView.backgroundColor = UIColor(0xDCEAFD)
|
||||
case .destroyed:
|
||||
roomStatusImageView.image = UIImage(named: "room_has_destroyed", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
roomStatusLabel.text = .meetingText
|
||||
roomStatusLabel.textColor = UIColor(0x888888)
|
||||
enterRoomStatusLabel.isHidden = false
|
||||
enterRoomStatusLabel.text = .meetingEnded
|
||||
enterRoomButton.isHidden = true
|
||||
userNumberLabel.isHidden = true
|
||||
roomStatusView.backgroundColor = UIColor(0xDDDDDD)
|
||||
inviteUserButton.isHidden = true
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
func setupStackView() {
|
||||
if !isViewReady {
|
||||
return
|
||||
}
|
||||
setupUserStackView()
|
||||
layoutIfNeeded()
|
||||
if viewModel.message.memberCount > 1 {
|
||||
userNumberLabel.text = String(viewModel.message.memberCount) + .peopleEnteredMeetingText
|
||||
} else {
|
||||
userNumberLabel.text = .waitingMembersEnterMeetingText
|
||||
}
|
||||
}
|
||||
|
||||
private func setupUserStackView() {
|
||||
var userNumber = 0
|
||||
viewArray.forEach { view in
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
viewModel.message.userList.forEach { userDic in
|
||||
if userNumber >= 5 {
|
||||
let label = UILabel()
|
||||
label.text = "..."
|
||||
viewArray.append(label)
|
||||
stackView.addArrangedSubview(label)
|
||||
label.snp.makeConstraints { make in
|
||||
make.height.equalTo(24)
|
||||
make.width.equalTo(24)
|
||||
}
|
||||
return
|
||||
}
|
||||
let placeholderImage = UIImage(named: "room_default_avatar", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
let imageView = UIImageView()
|
||||
if let userAvatar = userDic["faceUrl"] as? String {
|
||||
imageView.sd_setImage(with: URL(string: userAvatar), placeholderImage: placeholderImage)
|
||||
} else {
|
||||
imageView.image = placeholderImage
|
||||
}
|
||||
viewArray.append(imageView)
|
||||
stackView.addArrangedSubview(imageView)
|
||||
imageView.snp.makeConstraints { make in
|
||||
make.height.equalTo(24)
|
||||
make.width.equalTo(24)
|
||||
}
|
||||
userNumber = userNumber + 1
|
||||
}
|
||||
}
|
||||
|
||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
|
||||
let viewFrame = self.frame
|
||||
let inviteViewFrame = self.inviteUserButton.frame
|
||||
if viewFrame.contains(point) && !inviteViewFrame.contains(point) {
|
||||
return enterRoomButton
|
||||
}
|
||||
return super.hitTest(point, with: event)
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomMessageView: RoomMessageViewResponder {
|
||||
func updateStackView() {
|
||||
setupStackView()
|
||||
}
|
||||
func updateRoomStatus() {
|
||||
setupViewState()
|
||||
}
|
||||
func changeEnterRoomButton(isEnabled: Bool) {
|
||||
enterRoomButton.isEnabled = isEnabled
|
||||
}
|
||||
func updateEnteredRoom() {
|
||||
enterRoomButton.isSelected = true
|
||||
}
|
||||
func updateExitRoom() {
|
||||
enterRoomButton.isSelected = false
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static var inviteMeetingText: String {
|
||||
localized("Invite you to a meeting")
|
||||
}
|
||||
static var enterMeetingText: String {
|
||||
localized("Enter the meeting")
|
||||
}
|
||||
static var alreadyEnteredMeeting: String {
|
||||
localized("Already entered the meeting")
|
||||
}
|
||||
static var meetingText: String {
|
||||
localized("Meeting")
|
||||
}
|
||||
static var inProgressText: String {
|
||||
localized("Ongoing")
|
||||
}
|
||||
static var peopleEnteredMeetingText: String {
|
||||
localized("People have entered the meeting")
|
||||
}
|
||||
static var startingMeetingText: String {
|
||||
localized("Starting meeting")
|
||||
}
|
||||
static var waitingMembersEnterMeetingText: String {
|
||||
localized("Waiting for members to enter the meeting")
|
||||
}
|
||||
static var meetingEnded: String {
|
||||
localized("Meeting ended")
|
||||
}
|
||||
static var quickMeetingText: String {
|
||||
localized("Quick meeting")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// ChatExtensionRoomSettingsViewModel.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/6/26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ChatExtensionRoomSettingsViewModel {
|
||||
var isOpenMicrophone: Bool
|
||||
var isOpenCamera: Bool
|
||||
private let engineManager = EngineManager.shared
|
||||
private(set) var roomSettingsViewItems: [RoomSetListItemData] = []
|
||||
init(isOpenMicrophone: Bool, isOpenCamera: Bool) {
|
||||
self.isOpenMicrophone = isOpenMicrophone
|
||||
self.isOpenCamera = isOpenCamera
|
||||
createRoomSettingsModel()
|
||||
}
|
||||
func createRoomSettingsModel() {
|
||||
let openMicItem = RoomSetListItemData()
|
||||
openMicItem.titleText = .micSeatText
|
||||
openMicItem.isSwitchOn = isOpenMicrophone
|
||||
openMicItem.action = {[weak self] sender in
|
||||
guard let self = self, let view = sender as? UISwitch else { return }
|
||||
self.engineManager.store.isOpenMicrophone = view.isOn
|
||||
}
|
||||
roomSettingsViewItems.append(openMicItem)
|
||||
|
||||
let openCameraItem = RoomSetListItemData()
|
||||
openCameraItem.titleText = .cameraSetText
|
||||
openCameraItem.isSwitchOn = isOpenCamera
|
||||
openCameraItem.action = {[weak self] sender in
|
||||
guard let self = self, let view = sender as? UISwitch else { return }
|
||||
self.engineManager.store.isOpenCamera = view.isOn
|
||||
}
|
||||
roomSettingsViewItems.append(openCameraItem)
|
||||
}
|
||||
}
|
||||
private extension String {
|
||||
static var cameraSetText: String {
|
||||
localized("Join the meeting and start the camera")
|
||||
}
|
||||
static var micSeatText: String {
|
||||
localized("Join the conference and turn on the mic")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//
|
||||
// RoomaInviteViewModel.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/24.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TUICore
|
||||
import AVFAudio
|
||||
|
||||
class InvitedToJoinRoomViewModel: NSObject, AVAudioPlayerDelegate {
|
||||
let inviteUserName: String
|
||||
let roomId: String
|
||||
var avatarUrl: String
|
||||
var displayLink: CADisplayLink?
|
||||
var audioPlayer: AVAudioPlayer = AVAudioPlayer()
|
||||
var startTime: TimeInterval?
|
||||
var endTime: TimeInterval?
|
||||
var messageManager: RoomMessageManager {
|
||||
return RoomMessageManager.shared
|
||||
}
|
||||
private var roomManager: RoomManager {
|
||||
RoomManager.shared
|
||||
}
|
||||
init(inviteUserName: String, inviteUserAvatarUrl: String, roomId: String) {
|
||||
self.inviteUserName = inviteUserName
|
||||
self.roomId = roomId
|
||||
avatarUrl = inviteUserAvatarUrl
|
||||
super.init()
|
||||
playAudio(forResource: "phone_ringing", ofType: "mp3")
|
||||
}
|
||||
|
||||
func startPlay() {
|
||||
audioPlayer.play()
|
||||
}
|
||||
|
||||
func stopPlay() {
|
||||
audioPlayer.stop()
|
||||
}
|
||||
|
||||
func disagreeAction() {
|
||||
stopPlay()
|
||||
closeInvitedToJoinRoomView()
|
||||
}
|
||||
|
||||
func agreeAction() {
|
||||
stopPlay()
|
||||
if EngineManager.shared.store.isEnteredRoom {
|
||||
roomManager.exitOrDestroyPreviousRoom { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.enterRoom()
|
||||
} onError: { code, message in
|
||||
debugPrint("exitRoom, code:\(code), message:\(message)")
|
||||
}
|
||||
} else {
|
||||
enterRoom()
|
||||
}
|
||||
}
|
||||
|
||||
private func enterRoom() {
|
||||
roomManager.enterRoom(roomId: roomId)
|
||||
closeInvitedToJoinRoomView()
|
||||
}
|
||||
|
||||
private func playAudio(forResource: String, ofType: String){
|
||||
if let bundlePath = Bundle.main.path(forResource: forResource, ofType: ofType) {
|
||||
let url = URL(fileURLWithPath: bundlePath)
|
||||
do {
|
||||
try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
|
||||
} catch let error {
|
||||
debugPrint("AVAudioSession set outputAudioPort error:\(error.localizedDescription)")
|
||||
}
|
||||
do {
|
||||
try audioPlayer = AVAudioPlayer(contentsOf: url)
|
||||
audioPlayer.numberOfLoops = -1
|
||||
audioPlayer.delegate = self
|
||||
audioPlayer.prepareToPlay()
|
||||
} catch let error {
|
||||
debugPrint("audioPlayer error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func closeInvitedToJoinRoomView() {
|
||||
TUIRoomImAccessService.shared.inviteWindow?.isHidden = true
|
||||
TUIRoomImAccessService.shared.inviteWindow = nil
|
||||
TUIRoomImAccessService.shared.isShownInvitedToJoinRoomView = false
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// RoomMsgViewModel.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/8.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import TIMCommon
|
||||
import TUICore
|
||||
import RTCRoomEngine
|
||||
|
||||
@objc(RoomMessageBubbleCellData)
|
||||
class RoomMessageBubbleCellData: TUIBubbleMessageCellData {
|
||||
var messageModel: RoomMessageModel?
|
||||
override init(direction: TMsgDirection) {
|
||||
super.init(direction: direction)
|
||||
}
|
||||
override class func getCellData(_ message: V2TIMMessage) -> TUIMessageCellData {
|
||||
let messageModel = RoomMessageModel()
|
||||
messageModel.updateMessage(message: message)
|
||||
if messageModel.roomId == RoomManager.shared.roomId, messageModel.roomState != .destroyed {
|
||||
RoomManager.shared.roomObserver.messageModel.updateMessage(message: message)
|
||||
}
|
||||
let messageCellData = RoomMessageBubbleCellData(direction: message.isSelf ? .MsgDirectionOutgoing : .MsgDirectionIncoming)
|
||||
messageCellData.messageModel = messageModel
|
||||
return messageCellData
|
||||
}
|
||||
|
||||
override class func getDisplayString(_ message: V2TIMMessage) -> String {
|
||||
let businessID = parseBusinessID(message: message)
|
||||
if businessID == BussinessID_GroupRoomMessage {
|
||||
let dict = TUITool.jsonData2Dictionary(message.customElem.data) as? [String: Any]
|
||||
let userName = dict?["ownerName"] as? String ?? ""
|
||||
return userName + .quickMeetingText
|
||||
} else {
|
||||
return super.getDisplayString(message)
|
||||
}
|
||||
}
|
||||
|
||||
private class func parseBusinessID(message: V2TIMMessage?) -> String {
|
||||
guard let message = message else { return "" }
|
||||
let customData = message.customElem.data
|
||||
let dict = TUITool.jsonData2Dictionary(customData)
|
||||
guard let businessID = dict?["businessID"] as? String else { return ""}
|
||||
return businessID
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static var quickMeetingText: String {
|
||||
localized("'s quick meeting")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// RoomMessageViewModel.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/5/10.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
import TUICore
|
||||
import TIMCommon
|
||||
|
||||
protocol RoomMessageViewResponder: NSObject {
|
||||
func updateStackView()
|
||||
func updateRoomStatus()
|
||||
func updateEnteredRoom()
|
||||
func updateExitRoom()
|
||||
}
|
||||
|
||||
class RoomMessageViewModel: NSObject {
|
||||
var message: RoomMessageModel
|
||||
private var engineManager: EngineManager {
|
||||
EngineManager.shared
|
||||
}
|
||||
lazy var userId: String = {
|
||||
return TUILogin.getUserID() ?? EngineManager.shared.store.currentUser.userId
|
||||
}()
|
||||
var messageManager: RoomMessageManager {
|
||||
RoomMessageManager.shared
|
||||
}
|
||||
let roomManager = RoomManager.shared
|
||||
weak var viewResponder: RoomMessageViewResponder?
|
||||
init(message: RoomMessageModel) {
|
||||
self.message = message
|
||||
super.init()
|
||||
roomManager.roomObserver.addListener(listener: self)
|
||||
}
|
||||
|
||||
deinit {
|
||||
roomManager.roomObserver.removeListener(listener: self)
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
|
||||
func changeMessage(message: RoomMessageModel) {
|
||||
self.message = message
|
||||
viewResponder?.updateStackView()
|
||||
viewResponder?.updateRoomStatus()
|
||||
}
|
||||
|
||||
func enterRoomAction() {
|
||||
guard BusinessSceneUtil.canJoinRoom() else { return }
|
||||
if roomManager.isEnteredOtherRoom(roomId: message.roomId) {
|
||||
roomManager.exitOrDestroyPreviousRoom { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.enterRoom()
|
||||
} onError: { [weak self] code, message in
|
||||
debugPrint("exitRoom,code:\(code),message:\(message)")
|
||||
guard let self = self else { return }
|
||||
self.enterRoom()
|
||||
}
|
||||
} else {
|
||||
enterRoom()
|
||||
}
|
||||
}
|
||||
|
||||
private func enterRoom() {
|
||||
if !engineManager.store.isEnteredRoom {
|
||||
roomManager.enterRoom(roomId: message.roomId)
|
||||
} else {
|
||||
EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ShowRoomMainView, param: [:])
|
||||
}
|
||||
}
|
||||
|
||||
func inviteUserAction() {
|
||||
guard message.groupId.count > 0 else { return }
|
||||
let inviter = TUILoginUserInfo()
|
||||
inviter.userId = userId
|
||||
inviter.userName = TUILogin.getNickName() ?? ""
|
||||
inviter.avatarUrl = TUILogin.getFaceUrl() ?? ""
|
||||
InviteToJoinRoomManager.startInviteToJoinRoom(message: message, inviter: inviter)
|
||||
}
|
||||
}
|
||||
|
||||
extension RoomMessageViewModel: RoomObserverListener {
|
||||
func onRoomEnter(messageId: String, code: Int, message: String) {
|
||||
if code == 0, messageId == self.message.messageId {
|
||||
viewResponder?.updateEnteredRoom()
|
||||
}
|
||||
}
|
||||
func onRoomExit(messageId: String) {
|
||||
if messageId == self.message.messageId {
|
||||
viewResponder?.updateExitRoom()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user