This commit is contained in:
启星
2025-08-08 10:49:36 +08:00
parent 6400cf78bb
commit b5ce3d580a
8780 changed files with 978183 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
//
// FloatWindowViewModel.swift
// TUICallKit
//
// Created by vincepzhang on 2023/2/27.
//
import Foundation
import TUICallEngine
class FloatingWindowViewModel {
let callStatusObserver = Observer()
let mediaTypeObserver = Observer()
let remoteUserListObserver = Observer()
let timeCountObserver = Observer()
let sceneObserver = Observer()
let selfPlayoutVolumeObserver = Observer()
let remotePlayoutVolumeObserver = Observer()
let callTime: Observable<Int> = Observable(0)
let mediaType: Observable<TUICallMediaType> = Observable(.unknown)
let selfCallStatus: Observable<TUICallStatus> = Observable(.none)
let remoteUserList: Observable<[User]> = Observable(Array())
let selfUser: Observable<User> = Observable(User())
let scene: Observable<TUICallScene> = Observable(TUICallScene.single)
let currentSpeakUser: Observable<User> = Observable(User())
init() {
callTime.value = TUICallState.instance.timeCount.value
selfCallStatus.value = TUICallState.instance.selfUser.value.callStatus.value
mediaType.value = TUICallState.instance.mediaType.value
remoteUserList.value = TUICallState.instance.remoteUserList.value
selfUser.value = TUICallState.instance.selfUser.value
scene.value = TUICallState.instance.scene.value
registerObserve()
}
deinit {
TUICallState.instance.selfUser.value.callStatus.removeObserver(callStatusObserver)
TUICallState.instance.mediaType.removeObserver(mediaTypeObserver)
TUICallState.instance.remoteUserList.removeObserver(remoteUserListObserver)
TUICallState.instance.timeCount.removeObserver(timeCountObserver)
TUICallState.instance.scene.removeObserver(sceneObserver)
TUICallState.instance.selfUser.value.playoutVolume.removeObserver(selfPlayoutVolumeObserver)
for index in 0..<TUICallState.instance.remoteUserList.value.count {
guard index < TUICallState.instance.remoteUserList.value.count else {
break
}
TUICallState.instance.remoteUserList.value[index].playoutVolume.removeObserver(remotePlayoutVolumeObserver)
}
}
func registerObserve() {
registerCallStatusObserver()
registerMediaTypeObserver()
registerRemoteUserListObserver()
registerTimeCountObserver()
registerSceneObserver()
registerVolumeObserver()
}
func registerCallStatusObserver() {
TUICallState.instance.selfUser.value.callStatus.addObserver(callStatusObserver, closure: { [weak self] newValue, _ in
guard let self = self else { return }
self.selfCallStatus.value = newValue
})
}
func registerMediaTypeObserver() {
TUICallState.instance.mediaType.addObserver(mediaTypeObserver, closure: { [weak self] newValue, _ in
guard let self = self else { return }
self.mediaType.value = newValue
})
}
func registerRemoteUserListObserver() {
TUICallState.instance.remoteUserList.addObserver(remoteUserListObserver, closure: { [weak self] newValue, _ in
guard let self = self else { return }
self.remoteUserList.value = newValue
if !newValue.contains(where: { $0.id.value == self.currentSpeakUser.value.id.value }) {
self.updateCurrentSpeakUser(user: TUICallState.instance.selfUser.value)
}
})
}
func registerTimeCountObserver() {
TUICallState.instance.timeCount.addObserver(timeCountObserver, closure: { [weak self] newValue, _ in
guard let self = self else { return }
self.callTime.value = newValue
})
}
func registerSceneObserver() {
TUICallState.instance.scene.addObserver(sceneObserver) { [weak self] newValue, _ in
guard let self = self else { return }
self.scene.value = newValue
}
}
func registerVolumeObserver() {
TUICallState.instance.selfUser.value.playoutVolume.addObserver(selfPlayoutVolumeObserver) { [weak self] newValue, _ in
guard let self = self else { return }
if newValue > 30 {
self.updateCurrentSpeakUser(user: TUICallState.instance.selfUser.value)
}
}
for index in 0..<TUICallState.instance.remoteUserList.value.count {
guard index < TUICallState.instance.remoteUserList.value.count else {
break
}
let user = TUICallState.instance.remoteUserList.value[index]
TUICallState.instance.remoteUserList.value[index].playoutVolume.addObserver(remotePlayoutVolumeObserver) { [weak self] newValue, _ in
guard let self = self else { return }
if newValue > 30 {
self.updateCurrentSpeakUser(user: user)
}
}
}
}
func updateCurrentSpeakUser(user: User) {
if user.id.value.count > 0 && currentSpeakUser.value.id.value != user.id.value {
self.currentSpeakUser.value = user
}
}
func getCallTimeString() -> String {
return GCDTimer.secondToHMSString(second: callTime.value)
}
func startRemoteView(user: User, videoView: TUIVideoView){
CallEngineManager.instance.startRemoteView(user: user, videoView: videoView)
}
}

View File

@@ -0,0 +1,48 @@
//
// SelectGroupMemberViewModel.swift
// TUICallKit
//
// Created by vincepzhang on 2023/5/15.
//
import Foundation
import ImSDK_Plus
class SelectGroupMemberViewModel {
var selfUser: Observable<User> = Observable(User())
var groupMemberList: Observable<[User]> = Observable([])
var groupMemberState: [String: Bool] = [:]
var groupMemberStateOrigin: [String: Bool] = [:]
init() {
updateUserState()
}
func updateUserState() {
selfUser.value = TUICallState.instance.selfUser.value
V2TIMManager.sharedInstance().getGroupMemberList(TUICallState.instance.groupId.value,
filter: .max,
nextSeq: 0) { [weak self] nextSeq, imUserInfoList in
guard let self = self else { return }
guard let imUserInfoList = imUserInfoList else { return }
for imUserInfo in imUserInfoList {
if imUserInfo.userID == self.selfUser.value.id.value {
continue
}
let user = User.convertUserFromImFullInfo(user: imUserInfo)
self.groupMemberList.value.append(user)
self.groupMemberState[user.id.value] = false
self.groupMemberStateOrigin[user.id.value] = false
}
for user in TUICallState.instance.remoteUserList.value {
self.groupMemberState[user.id.value] = true
self.groupMemberStateOrigin[user.id.value] = true
}
} fail: { code, message in
}
}
}

View File

@@ -0,0 +1,228 @@
//
// JoinInGroupCallViewModel.swift
// TUICallKit-Swift
//
// Created by noah on 2024/1/23.
//
import UIKit
import TUICallEngine
import ImSDK_Plus
import TUICore
class JoinInGroupCallViewModel: NSObject, V2TIMGroupListener, JoinInGroupCallViewDelegate {
private let callStatusObserver = Observer()
private var joinGroupCallView = JoinInGroupCallView()
private var roomId = TUIRoomId()
private var groupId: String = ""
private var callMediaType: TUICallMediaType = .unknown
private var recordExpansionStatus: Bool = false
override init() {
super.init()
V2TIMManager.sharedInstance().addGroupListener(listener: self)
registerCallStatusObserver()
}
deinit {
TUICallState.instance.selfUser.value.callStatus.removeObserver(callStatusObserver)
}
func getGroupAttributes(_ groupID: String) {
groupId = groupID
recordExpansionStatus = false
guard TUICallState.instance.selfUser.value.callStatus.value == .none else {
return
}
V2TIMManager.sharedInstance().getGroupAttributes(groupID, keys: nil) { [weak self] groupAttributeList in
guard let self = self, let attributeList = groupAttributeList as? [String : String] else {
return
}
self.processGroupAttributeData(attributeList)
} fail: { code, message in
}
}
func setJoinGroupCallView(_ joinGroupView: JoinInGroupCallView) {
joinGroupCallView = joinGroupView
joinGroupCallView.delegate = self
joinGroupCallView.isHidden = true
}
func registerCallStatusObserver() {
TUICallState.instance.selfUser.value.callStatus.addObserver(callStatusObserver, closure: { [weak self] newValue, _ in
guard let self = self else { return }
if newValue == .none && !self.groupId.isEmpty && self.groupId.count > 0 {
self.getGroupAttributes(self.groupId)
}
})
}
// MARK: - Private Method
private func processGroupAttributeData(_ groupAttributeList: [String: String]) {
guard let jsonStr = groupAttributeList["inner_attr_kit_info"], jsonStr.count > 0,
let jsonData = jsonStr.data(using: .utf8),
let groupAttributeDic = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any],
checkBusinessType(groupAttributeDic) else {
hiddenJoinGroupCallView()
return
}
handleRoomId(groupAttributeDic)
handleCallMediaType(groupAttributeDic)
guard let userIdList = getUserIdList(groupAttributeDic), userIdList.count > 0 else {
hiddenJoinGroupCallView()
return
}
if userIdList.contains(TUICallState.instance.selfUser.value.id.value) {
hiddenJoinGroupCallView()
return
}
handleUsersInfo(userIdList)
}
private func checkBusinessType(_ groupAttributeValue: [String: Any]) -> Bool {
guard let businessType = groupAttributeValue["business_type"] as? String else {
return false
}
return businessType == "callkit"
}
private func handleRoomId(_ groupAttributeValue: [String: Any]) {
guard let strRoomId = groupAttributeValue["room_id"] as? String, strRoomId.count > 0 else {
return
}
if groupAttributeValue["room_id_type"] as? Int == 2 {
roomId.strRoomId = strRoomId
return
}
if let intRoomId = UInt32(strRoomId) {
roomId.intRoomId = intRoomId
} else {
roomId.strRoomId = strRoomId
}
}
private func handleCallMediaType(_ groupAttributeValue: [String: Any]) {
guard let callMediaType = groupAttributeValue["call_media_type"] as? String, callMediaType.count > 0 else {
return
}
if callMediaType == "audio" {
self.callMediaType = TUICallMediaType.audio
} else if callMediaType == "video" {
self.callMediaType = TUICallMediaType.video
}
}
private func getUserIdList(_ groupAttributeValue: [String: Any]) -> [String]? {
guard let userInfoList = groupAttributeValue["user_list"] as? [[String: Any]] else {
return nil
}
var userIdList = [String]()
for userInfoDic in userInfoList {
guard let userId = userInfoDic["userid"] as? String else {
break
}
userIdList.append(userId)
}
return userIdList
}
private func handleUsersInfo(_ userIdList: [String]) {
V2TIMManager.sharedInstance().getUsersInfo(userIdList) { [weak self] infoList in
guard let self = self, let userInfoList = infoList else { return }
var userModelList: [User] = Array()
for userInfo in userInfoList {
let userModel = User()
userModel.id.value = userInfo.userID ?? ""
userModel.avatar.value = userInfo.faceURL ?? ""
userModelList.append(userModel)
}
if userModelList.count > 1 {
self.showJoinGroupCallView()
self.joinGroupCallView.updateView(with: userModelList, callMediaType:self.callMediaType)
} else {
self.hiddenJoinGroupCallView()
}
} fail: { code, message in
}
}
private func showJoinGroupCallView() {
joinGroupCallView.isHidden = false
updatePageContent()
postUpdateNotification()
}
func hiddenJoinGroupCallView() {
joinGroupCallView.isHidden = true
if let parentView = joinGroupCallView.superview {
parentView.frame = CGRect(x: 0, y: 0, width: parentView.bounds.size.width, height: 0)
postUpdateNotification()
}
}
func updatePageContent() {
DispatchQueue.main.async {
let joinGroupCallViewHeight = self.recordExpansionStatus ? kJoinGroupCallViewExpandHeight : kJoinGroupCallViewDefaultHeight
self.joinGroupCallView.frame = CGRect(x: self.joinGroupCallView.frame.origin.x,
y: self.joinGroupCallView.frame.origin.y,
width: self.joinGroupCallView.bounds.size.width,
height: joinGroupCallViewHeight)
if let parentView = self.joinGroupCallView.superview {
parentView.frame = CGRect(x: 0,
y: 0,
width: parentView.bounds.size.width,
height: self.joinGroupCallView.bounds.size.height)
}
}
}
func postUpdateNotification() {
DispatchQueue.main.async {
NotificationCenter.default.post(name: Notification.Name(TUICore_TUIChatExtension_ChatViewTopArea_ChangedNotification), object: nil)
}
}
// MARK: - TUICallKitJoinGroupCallViewDelegate
func updatePageContent(isExpand: Bool) {
if recordExpansionStatus != isExpand {
recordExpansionStatus = isExpand
}
updatePageContent()
postUpdateNotification()
}
func joinInGroupCall() {
hiddenJoinGroupCallView()
TUICallKit.createInstance().joinInGroupCall(roomId: roomId, groupId: groupId, callMediaType: callMediaType)
}
// MARK: - V2TIMGroupListener
func onGroupAttributeChanged(_ groupID: String!, attributes: NSMutableDictionary!) {
guard let attributes = attributes as? [String: String],
groupId == groupID else {
return
}
if TUICallState.instance.selfUser.value.callStatus.value != .none {
self.hiddenJoinGroupCallView()
return
}
processGroupAttributeData(attributes)
}
}

View File

@@ -0,0 +1,193 @@
//
// TUICallRecordCallsCellViewModel.swift
// TUICallKit
//
// Created by vincepzhang on 2023/8/28.
//
import Foundation
import UIKit
import TUICallEngine
import ImSDK_Plus
import TUICore
class TUICallRecordCallsCellViewModel {
var avatarImage: UIImage = UIImage()
var faceURL: Observable<String> = Observable("")
var titleLabelStr: Observable<String> = Observable("")
var mediaTypeImageStr: String = ""
var resultLabelStr: String = ""
var timeLabelStr: String = ""
var callRecord: TUICallRecords
init(_ record: TUICallRecords) {
callRecord = record
configAvatarImage(callRecord)
configResult(callRecord.result)
configMediaTypeImageName(callRecord.mediaType)
configTitle(callRecord)
configTime(callRecord)
}
private func configAvatarImage(_ callRecord: TUICallRecords) {
if callRecord.scene == .group {
var inviteList = callRecord.inviteList
inviteList.insert(callRecord.inviter, at: 0)
guard let inviteList = inviteList as? [String] else { return }
configGroupAvatarImage(inviteList)
} else if callRecord.scene == .single {
configSingleAvatarImage(callRecord)
} else {
avatarImage = TUICoreDefineConvert.getDefaultGroupAvatarImage()
}
}
private func configGroupAvatarImage(_ inviteList: [String]) {
if inviteList.isEmpty {
avatarImage = TUICoreDefineConvert.getDefaultGroupAvatarImage()
return
}
let inviteStr = inviteList.joined(separator: "#")
getCacheAvatarForInviteStr(inviteStr) { [weak self] avatar in
guard let self = self else { return }
if let avatar = avatar {
self.avatarImage = avatar
} else {
V2TIMManager.sharedInstance()?.getUsersInfo(inviteList, succ: { infoList in
var avatarsList = [String]()
infoList?.forEach { userFullInfo in
if let faceURL = userFullInfo.faceURL, !faceURL.isEmpty {
avatarsList.append(faceURL)
} else {
avatarsList.append("http://placeholder")
}
}
TUIGroupAvatar.createGroupAvatar(avatarsList, finished: { [weak self] image in
guard let self = self else { return }
self.avatarImage = image
self.cacheGroupCallAvatar(image, inviteStr: inviteStr)
})
}, fail: nil)
}
}
}
private func configSingleAvatarImage(_ callRecord: TUICallRecords) {
avatarImage = TUICoreDefineConvert.getDefaultAvatarImage()
var useId = callRecord.inviter
if callRecord.role == .call {
guard let firstInvite = callRecord.inviteList.first as? String else { return }
useId = firstInvite
}
if !useId.isEmpty {
V2TIMManager.sharedInstance()?.getUsersInfo([useId], succ: { [weak self] infoList in
guard let self = self else { return }
if let userFullInfo = infoList?.first {
if let faceURL = userFullInfo.faceURL, !faceURL.isEmpty, faceURL.hasPrefix("http") {
self.faceURL.value = faceURL
}
}
}, fail: nil)
}
}
private func configResult(_ callResultType: TUICallResultType) {
switch callResultType {
case .missed:
resultLabelStr = TUICallKitLocalize(key: "TUICallKit.Recents.missed") ?? "Missed"
case .incoming:
resultLabelStr = TUICallKitLocalize(key: "TUICallKit.Recents.incoming") ?? "Incoming"
case .outgoing:
resultLabelStr = TUICallKitLocalize(key: "TUICallKit.Recents.outgoing") ?? "Outgoing"
case .unknown:
break
@unknown default:
break
}
}
private func configMediaTypeImageName(_ callMediaType: TUICallMediaType) {
if callMediaType == .audio {
mediaTypeImageStr = "ic_recents_audio"
} else if callMediaType == .video {
mediaTypeImageStr = "ic_recents_video"
}
}
func configTitle(_ callRecord: TUICallRecords) {
titleLabelStr.value = ""
var allUsers: [String] = []
switch callRecord.scene {
case .single:
if callRecord.role == .call {
guard let inviteList = callRecord.inviteList as? [String] else { return }
allUsers = inviteList
} else if callRecord.role == .called {
let inviter = callRecord.inviter
allUsers.append(inviter)
}
case .group:
guard let inviteList = callRecord.inviteList as? [String] else { return }
allUsers = inviteList
case .multi:
break
@unknown default:
break
}
User.getUserInfosFromIM(userIDs: allUsers) { [weak self] infoList in
guard let self = self else { return }
let titleArray = infoList.map { $0.remark.value.count > 0
? $0.remark.value
: $0.nickname.value.count > 0 ? $0.nickname.value : $0.id.value }
self.titleLabelStr.value = titleArray.joined(separator: ",")
}
}
private func configTime(_ callRecord: TUICallRecords) {
let beginTime: TimeInterval = callRecord.beginTime / 1_000
if beginTime <= 0 {
return
}
timeLabelStr = TUITool.convertDate(toStr: Date(timeIntervalSince1970: beginTime))
}
// MARK: - Cache
private func getCacheAvatarForInviteStr(_ inviteStr: String, completion: @escaping (UIImage?) -> Void) {
let cacheKey = "group_call_avatar_\(inviteStr)"
let cachePath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first ?? ""
let filePath = "\(cachePath)/\(cacheKey)"
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
if let data = fileManager.contents(atPath: filePath), let image = UIImage(data: data) {
completion(image)
return
}
}
completion(nil)
}
private func cacheGroupCallAvatar(_ avatar: UIImage, inviteStr: String) {
let cacheKey = "group_call_avatar_\(inviteStr)"
let cachePath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first ?? ""
let filePath = "\(cachePath)/\(cacheKey)"
let fileManager = FileManager.default
if let data = avatar.pngData() {
fileManager.createFile(atPath: filePath, contents: data, attributes: nil)
}
}
}

View File

@@ -0,0 +1,205 @@
//
// TUICallRecordCallsViewModel.swift
//
//
// Created by vincepzhang on 2023/8/28.
//
import Foundation
import UIKit
import TUICallEngine
import TUICore
enum TUICallRecordCallsType: Int {
case all
case missed
}
enum TUICallKitRecordCallsUIStyle: Int {
case classic
case minimalist
}
class TUICallRecordCallsViewModel {
var dataSource: Observable<[TUICallRecordCallsCellViewModel]> = Observable(Array())
var allDataSource: [TUICallRecordCallsCellViewModel] = []
var missedDataSource: [TUICallRecordCallsCellViewModel] = []
var recordCallsUIStyle: TUICallKitRecordCallsUIStyle = .minimalist
var recordCallsType: TUICallRecordCallsType = .all
typealias SuccClosureType = @convention(block) (UIViewController) -> Void
typealias FailClosureType = @convention(block) (Int, String) -> Void
func queryRecentCalls() {
let filter = TUICallRecentCallsFilter()
TUICallEngine.createInstance().queryRecentCalls(filter: filter, succ: { [weak self] callRecords in
guard let self = self else { return }
var viewModelList: [TUICallRecordCallsCellViewModel] = []
callRecords.forEach { callRecord in
let viewModel = TUICallRecordCallsCellViewModel(callRecord)
viewModelList.append(viewModel)
}
self.updateDataSource(viewModelList)
}, fail: {})
}
func updateDataSource(_ viewModelList: [TUICallRecordCallsCellViewModel]) {
if viewModelList.isEmpty {
return
}
cleanAllSource()
allDataSource = viewModelList
viewModelList.forEach { viewModel in
if viewModel.callRecord.result == .missed {
missedDataSource.append(viewModel)
}
}
reloadDataSource()
}
func switchRecordCallsType(_ type: TUICallRecordCallsType) {
recordCallsType = type
reloadDataSource()
}
func reloadDataSource() {
switch recordCallsType {
case .all:
dataSource.value = allDataSource
case .missed:
dataSource.value = missedDataSource
}
}
func cleanAllSource() {
dataSource.value.removeAll()
allDataSource.removeAll()
missedDataSource.removeAll()
}
func cleanSource(viewModel: TUICallRecordCallsCellViewModel) {
allDataSource.removeAll() { $0.callRecord.callId == viewModel.callRecord.callId }
missedDataSource.removeAll() { $0.callRecord.callId == viewModel.callRecord.callId }
}
func cleanSource(callId: String) {
allDataSource.removeAll() { $0.callRecord.callId == callId }
missedDataSource.removeAll() { $0.callRecord.callId == callId }
}
func repeatCall(_ indexPath: IndexPath) {
let cellViewModel = dataSource.value[indexPath.row]
if cellViewModel.callRecord.scene == .single {
repeatSingleCall(cellViewModel.callRecord)
}
}
func repeatSingleCall(_ callRecord: TUICallRecords) {
var userId = callRecord.inviteList.first
if callRecord.role == .called {
userId = callRecord.inviter
}
guard let userId = userId as? String else { return }
TUICallKit.createInstance().call(userId: userId, callMediaType: callRecord.mediaType)
}
func deleteAllRecordCalls() {
var callIdList: [String] = []
if recordCallsType == .all {
callIdList = getCallIdList(allDataSource)
} else if recordCallsType == .missed {
callIdList = getCallIdList(missedDataSource)
}
TUICallEngine.createInstance().deleteRecordCalls(callIdList, succ: { [weak self] succList in
guard let self = self else { return }
succList.forEach { callId in
self.cleanSource(callId: callId)
}
self.reloadDataSource()
}, fail: {})
}
func getCallIdList(_ cellViewModelArray: [TUICallRecordCallsCellViewModel]) -> [String] {
var callIdList: [String] = []
if cellViewModelArray.isEmpty {
return callIdList
}
cellViewModelArray.forEach { obj in
callIdList.append(obj.callRecord.callId)
}
return callIdList
}
func deleteRecordCall(_ indexPath: IndexPath) {
if indexPath.row < 0 || indexPath.row >= dataSource.value.count {
return
}
let viewModel = dataSource.value[indexPath.row]
TUICallEngine.createInstance().deleteRecordCalls([viewModel.callRecord.callId], succ: { [weak self] _ in
guard let self = self else { return }
self.cleanSource(viewModel: viewModel)
self.reloadDataSource()
}, fail: {})
}
func jumpUserInfoController(indexPath: IndexPath, navigationController: UINavigationController) {
if indexPath.row < 0 || indexPath.row >= dataSource.value.count {
return
}
let cellViewModel = dataSource.value[indexPath.row]
let groupId = cellViewModel.callRecord.groupId
var userId = cellViewModel.callRecord.inviter
if cellViewModel.callRecord.role == .call {
guard let firstUserId = cellViewModel.callRecord.inviteList.first as? String else { return }
userId = firstUserId
}
if !groupId.isEmpty {
let param: [String: Any] = [TUICore_TUIContactObjectFactory_GetGroupInfoVC_GroupID: groupId]
if TUICallKitRecordCallsUIStyle.classic == recordCallsUIStyle {
navigationController.push(TUICore_TUIContactObjectFactory_GetGroupInfoVC_Classic, param: param, forResult: nil)
} else {
navigationController.push(TUICore_TUIContactObjectFactory_GetGroupInfoVC_Minimalist, param: param, forResult: nil)
}
} else if !userId.isEmpty {
getUserOrFriendProfileVCWithUserID(userId: userId) { viewController in
navigationController.pushViewController(viewController, animated: true)
} fail: { code, desc in
TUITool.makeToastError(Int(code), msg: desc)
}
}
}
func getUserOrFriendProfileVCWithUserID(userId: String, succ: @escaping SuccClosureType, fail: @escaping FailClosureType) {
let param: NSDictionary = [
TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_UserIDKey: userId,
TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_SuccKey: succ,
TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod_FailKey: fail,
]
if TUICallKitRecordCallsUIStyle.classic == self.recordCallsUIStyle {
TUICore.createObject(TUICore_TUIContactObjectFactory,
key: TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod, param: param as? [AnyHashable : Any])
} else {
TUICore.createObject(TUICore_TUIContactObjectFactory_Minimalist,
key: TUICore_TUIContactObjectFactory_GetUserOrFriendProfileVCMethod, param: param as? [AnyHashable : Any])
}
}
}

View File

@@ -0,0 +1,91 @@
//
// GroupCallVideoCellViewModel.swift
// TUICallKit
//
// Created by vincepzhang on 2023/3/6.
//
import Foundation
import TUICallEngine
class GroupCallVideoCellViewModel {
let showLargeViewUserIdObserver = Observer()
let remoteUserStatusObserver = Observer()
let remoteUserVideoAvailableObserver = Observer()
let remotePlayoutVolumeObserver = Observer()
let remoteNetworkQualityObserver = Observer()
var remoteUser = User()
let isShowLargeViewUserId: Observable<Bool> = Observable(false)
let remoteUserStatus: Observable<TUICallStatus> = Observable(.none)
let remoteUserVideoAvailable: Observable<Bool> = Observable(false)
let remoteUserVolume: Observable<Float> = Observable(0)
let remoteIsShowLowNetworkQuality: Observable<Bool> = Observable(false)
init(remote: User) {
remoteUser = remote
isShowLargeViewUserId.value = (TUICallState.instance.showLargeViewUserId.value == remote.id.value) && (remote.id.value.count > 0)
registerObserve()
}
deinit {
TUICallState.instance.showLargeViewUserId.removeObserver(showLargeViewUserIdObserver)
for index in 0..<TUICallState.instance.remoteUserList.value.count {
guard index < TUICallState.instance.remoteUserList.value.count else {
break
}
if TUICallState.instance.remoteUserList.value[index].id.value == remoteUser.id.value {
TUICallState.instance.remoteUserList.value[index].callStatus.removeObserver(remoteUserStatusObserver)
TUICallState.instance.remoteUserList.value[index].videoAvailable.removeObserver(remoteUserVideoAvailableObserver)
TUICallState.instance.remoteUserList.value[index].networkQualityReminder.removeObserver(remoteNetworkQualityObserver)
TUICallState.instance.remoteUserList.value[index].playoutVolume.removeObserver(remotePlayoutVolumeObserver)
}
}
}
func registerObserve() {
TUICallState.instance.showLargeViewUserId.addObserver(showLargeViewUserIdObserver, closure: { [weak self] newValue, _ in
guard let self = self else { return }
self.isShowLargeViewUserId.value = (newValue == self.remoteUser.id.value)
})
for index in 0..<TUICallState.instance.remoteUserList.value.count {
guard index < TUICallState.instance.remoteUserList.value.count else {
break
}
if TUICallState.instance.remoteUserList.value[index].id.value == remoteUser.id.value {
remoteUserStatus.value = TUICallState.instance.remoteUserList.value[index].callStatus.value
remoteUserVideoAvailable.value = TUICallState.instance.remoteUserList.value[index].videoAvailable.value
TUICallState.instance.remoteUserList.value[index].callStatus.addObserver(remoteUserStatusObserver,
closure: { [weak self] newValue, _ in
guard let self = self else { return }
self.remoteUserStatus.value = newValue
})
TUICallState.instance.remoteUserList.value[index].videoAvailable.addObserver(remoteUserVideoAvailableObserver,
closure: { [weak self] newValue, _ in
guard let self = self else { return }
self.remoteUserVideoAvailable.value = newValue
})
TUICallState.instance.remoteUserList.value[index].networkQualityReminder.addObserver(remoteNetworkQualityObserver)
{ [weak self] newValue, _ in
guard let self = self else { return }
self.remoteIsShowLowNetworkQuality.value = newValue
}
TUICallState.instance.remoteUserList.value[index].playoutVolume.addObserver(remotePlayoutVolumeObserver)
{ [weak self] newValue, _ in
guard let self = self else { return }
self.remoteUserVolume.value = newValue
}
}
}
}
}