diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 4c83a0908..8029fa30b 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,5 +1,9 @@ ## 发布日志 +### Version 1.7.0 @ 2023.12.15 +- iOS & Android: 优化界面,修改UI问题; +- iOS & Android: 修改roomId生成逻辑; + ### Version 1.6.1 @ 2023.11.10 - Android & iOS:优化产品体验,进一步美化横屏UI; - Android & iOS:优化产品体验,解决演讲者模式小画面切换抖动,设置小画面切换间隔为5秒; diff --git a/iOS/Example/App/AppDelegate.swift b/iOS/Example/App/AppDelegate.swift index 3b08e6177..f90691e20 100644 --- a/iOS/Example/App/AppDelegate.swift +++ b/iOS/Example/App/AppDelegate.swift @@ -45,14 +45,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return } let prePareViewController = RoomPrePareViewController() - let nav = UINavigationController(rootViewController: prePareViewController) + let nav = RoomNavigationController(rootViewController: prePareViewController) nav.modalPresentationStyle = .fullScreen getCurrentWindowViewController()?.present(nav, animated: true) } func showLoginViewController() { let loginVC = TRTCLoginViewController() - let nav = UINavigationController(rootViewController: loginVC) + let nav = RoomNavigationController(rootViewController: loginVC) if let keyWindow = SceneDelegate.getCurrentWindow() { keyWindow.rootViewController = nav keyWindow.makeKeyAndVisible() diff --git a/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/Contents.json b/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/Contents.json deleted file mode 100644 index 5fa55112c..000000000 --- a/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "filename" : "tencentText.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "tencentText@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "tencentText@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText.png b/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText.png deleted file mode 100644 index 4101166ee..000000000 Binary files a/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText.png and /dev/null differ diff --git a/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText@2x.png b/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText@2x.png deleted file mode 100644 index 38c8670d3..000000000 Binary files a/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText@2x.png and /dev/null differ diff --git a/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText@3x.png b/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText@3x.png deleted file mode 100644 index a7ebb49d2..000000000 Binary files a/iOS/Example/App/Assets.xcassets/room_tencent_text.imageset/tencentText@3x.png and /dev/null differ diff --git a/iOS/Example/App/Main/View/Component/ListCellItemView.swift b/iOS/Example/App/Main/View/Component/ListCellItemView.swift index 99eebd909..7f15e8e1f 100644 --- a/iOS/Example/App/Main/View/Component/ListCellItemView.swift +++ b/iOS/Example/App/Main/View/Component/ListCellItemView.swift @@ -164,7 +164,6 @@ class ListCellItemView: UIView { let color = UIColor(0xBBBBBB) textField.attributedPlaceholder = NSAttributedString(string: item.fieldPlaceholderText,attributes: [NSAttributedString.Key.foregroundColor:color]) - textField.becomeFirstResponder() } else { textField.text = item.fieldText } diff --git a/iOS/Example/App/Main/View/Component/RoomNavigationController.swift b/iOS/Example/App/Main/View/Component/RoomNavigationController.swift new file mode 100644 index 000000000..56bf7982c --- /dev/null +++ b/iOS/Example/App/Main/View/Component/RoomNavigationController.swift @@ -0,0 +1,30 @@ +// +// RoomNavigationController.swift +// DemoApp +// +// Created by 唐佳宁 on 2023/12/11. +// + +import Foundation +import UIKit + +class RoomNavigationController: UINavigationController { + override init(rootViewController: UIViewController) { + super.init(rootViewController: rootViewController) + interactivePopGestureRecognizer?.isEnabled = false + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + guard let supportedInterfaceOrientations = topViewController?.supportedInterfaceOrientations as? UIInterfaceOrientationMask + else { return .portrait } + return supportedInterfaceOrientations + } + override var shouldAutorotate: Bool { + guard let shouldAutorotate = topViewController?.shouldAutorotate else { return false } + return shouldAutorotate + } +} diff --git a/iOS/Example/App/Main/View/Controller/CreateRoomViewController.swift b/iOS/Example/App/Main/View/Controller/CreateRoomViewController.swift index 120bcafd7..3a2b3967f 100644 --- a/iOS/Example/App/Main/View/Controller/CreateRoomViewController.swift +++ b/iOS/Example/App/Main/View/Controller/CreateRoomViewController.swift @@ -26,11 +26,7 @@ class CreateRoomViewController: UIViewController { private var enableLocalVideo: Bool = true private var isSoundOnSpeaker: Bool = true let roomHashNumber: Int = 0x3B9AC9FF - lazy var roomId: String = { - let userId = currentUserId - let result = "\(String(describing: userId))_room_kit".hash & roomHashNumber - return String(result) - }() + var roomId: String? let backButton: UIButton = { let button = UIButton(type: .custom) @@ -68,16 +64,8 @@ class CreateRoomViewController: UIViewController { navigationController?.setNavigationBarHidden(false, animated: false) navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton) UIApplication.shared.isIdleTimerDisabled = false - UIDevice.current.setValue(UIDeviceOrientation.portrait.rawValue, forKey: "orientation") renewRootViewState() - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - appDelegate.orientation = .portrait - } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - appDelegate.orientation = .allButUpsideDown + setupRoomId() } @objc @@ -96,17 +84,13 @@ extension CreateRoomViewController { roomTypeItem.titleText = .roomTypeText roomTypeItem.messageText = .freedomSpeakText roomTypeItem.hasOverAllAction = true + roomTypeItem.hasButton = true roomTypeItem.action = { [weak self] sender in guard let self = self else { return } self.switchRoomTypeClick() } inputViewItems.append(roomTypeItem) - let createRoomIdItem = ListCellItemData() - createRoomIdItem.titleText = .roomNumText - createRoomIdItem.messageText = roomId - inputViewItems.append(createRoomIdItem) - let userNameItem = ListCellItemData() userNameItem.titleText = .userNameText userNameItem.messageText = currentUserName @@ -148,6 +132,11 @@ extension CreateRoomViewController { func enterButtonClick(sender: UIButton) { rootView?.updateEnterButtonState(isEnabled: false) rootView?.updateLoadingState(isStarted: true) + guard let roomId = self.roomId else { + self.view.makeToast(.generatingRoomIdText) + self.renewRootViewState() + return + } roomInfo.roomId = roomId roomInfo.name = currentUserName.truncateUtf8String(maxByteLength: 30) roomInfo.speechMode = roomSpeechMode @@ -213,6 +202,39 @@ extension CreateRoomViewController { view.freedomButton.isSelected = false chooseSpeechMode = .applySpeakAfterTakingSeat } + + private func setupRoomId() { + let roomId = getRandomRoomId(numberOfDigits: 6) + checkIfRoomIdExists(roomId: roomId) { [weak self] in + guard let self = self else { return } + self.setupRoomId() + } onNotExist: { [weak self] in + guard let self = self else { return } + self.roomId = roomId + } + } + + //获取随机数roomId,numberOfDigits为位数 + private func getRandomRoomId(numberOfDigits: Int) -> String { + var numberOfDigit = numberOfDigits > 0 ? numberOfDigits : 1 + numberOfDigit = numberOfDigit < 10 ? numberOfDigit : 9 + let minNumber = Int(truncating: NSDecimalNumber(decimal: pow(10, numberOfDigit - 1))) + let maxNumber = Int(truncating: NSDecimalNumber(decimal: pow(10, numberOfDigit))) - 1 + let randomNumber = arc4random_uniform(UInt32(maxNumber - minNumber)) + UInt32(minNumber) + return String(randomNumber) + } + + private func checkIfRoomIdExists(roomId: String, onExist: @escaping () -> (), onNotExist: @escaping () -> ()) { + V2TIMManager.sharedInstance().getGroupsInfo([roomId]) { infoResult in + if infoResult?.first?.resultCode == 0 { + onExist() + } else { + onNotExist() + } + } fail: { code, message in + onNotExist() + } + } } private extension String { @@ -222,9 +244,6 @@ private extension String { static var roomTypeText: String { RoomDemoLocalize("Demo.TUIRoomKit.room.type") } - static var roomNumText: String { - RoomDemoLocalize("Demo.TUIRoomKit.room.num") - } static var openCameraText: String { RoomDemoLocalize("Demo.TUIRoomKit.open.video") } @@ -243,6 +262,9 @@ private extension String { static var videoConferenceText: String { RoomDemoLocalize("Demo.TUIRoomKit.video.conference") } + static var generatingRoomIdText: String { + RoomDemoLocalize("Demo.TUIRoomKit.generating.roomId") + } func truncateUtf8String(maxByteLength: Int) -> String { let length = self.utf8.count if length <= maxByteLength { diff --git a/iOS/Example/App/Main/View/Controller/EnterRoomViewController.swift b/iOS/Example/App/Main/View/Controller/EnterRoomViewController.swift index 88205c864..913d47537 100644 --- a/iOS/Example/App/Main/View/Controller/EnterRoomViewController.swift +++ b/iOS/Example/App/Main/View/Controller/EnterRoomViewController.swift @@ -62,16 +62,7 @@ class EnterRoomViewController: UIViewController { navigationController?.setNavigationBarHidden(false, animated: false) navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton) UIApplication.shared.isIdleTimerDisabled = false - UIDevice.current.setValue(UIDeviceOrientation.portrait.rawValue, forKey: "orientation") renewRootViewState() - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - appDelegate.orientation = .portrait - } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - appDelegate.orientation = .allButUpsideDown } @objc func backButtonClick(sender: UIButton) { diff --git a/iOS/Example/App/Main/View/Controller/RoomPrePareViewController.swift b/iOS/Example/App/Main/View/Controller/RoomPrePareViewController.swift index 3fdac31c9..843306c7f 100644 --- a/iOS/Example/App/Main/View/Controller/RoomPrePareViewController.swift +++ b/iOS/Example/App/Main/View/Controller/RoomPrePareViewController.swift @@ -31,13 +31,6 @@ class RoomPrePareViewController: UIViewController { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(true, animated: false) UIApplication.shared.isIdleTimerDisabled = false - UIDevice.current.setValue(UIDeviceOrientation.portrait.rawValue, forKey: "orientation") - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } - appDelegate.orientation = .portrait } override func loadView() { diff --git a/iOS/Example/App/Main/View/View/PrePareView.swift b/iOS/Example/App/Main/View/View/PrePareView.swift index f0a36d369..b107c4dc2 100644 --- a/iOS/Example/App/Main/View/View/PrePareView.swift +++ b/iOS/Example/App/Main/View/View/PrePareView.swift @@ -7,6 +7,7 @@ import UIKit import TUICore +import TUIRoomEngine class PrePareView: UIView { @@ -49,25 +50,18 @@ class PrePareView: UIView { return button }() - let tencentBigView: UIView = { - let view = UIView(frame: .zero) - let iconImageView = UIImageView(frame: .zero) - iconImageView.image = UIImage(named: "room_tencent") - view.addSubview(iconImageView) - iconImageView.snp.makeConstraints { make in - make.top.equalToSuperview() - make.centerX.equalToSuperview() - make.width.equalTo(136) - make.height.equalTo(36) - } - let textImageView = UIImageView(frame: .zero) - textImageView.image = UIImage(named: "room_tencent_text") - view.addSubview(textImageView) - textImageView.snp.makeConstraints { make in - make.top.equalTo(iconImageView.snp.bottom).offset(5) - make.centerX.equalToSuperview() - } - return view + let signImageView: UIView = { + let imageView = UIImageView(frame: .zero) + imageView.image = UIImage(named: "room_tencent") + return imageView + }() + + let signLabel: UILabel = { + let label = UILabel() + label.textAlignment = .center + label.font = UIFont.systemFont(ofSize: 24, weight: .bold) + label.text = .videoConferencingText + return label }() let joinRoomButton: UIButton = { @@ -101,7 +95,9 @@ class PrePareView: UIView { return tip }() - init() { + private let signViewHeight: CGFloat = 36 + + init() { super.init(frame: .zero) } @@ -142,7 +138,8 @@ class PrePareView: UIView { topViewContainer.addSubview(userNameLabel) topViewContainer.addSubview(debugButton) topViewContainer.addSubview(switchLanguageButton) - addSubview(tencentBigView) + addSubview(signImageView) + addSubview(signLabel) addSubview(joinRoomButton) addSubview(createRoomButton) addSubview(appVersionTipLabel) @@ -176,13 +173,17 @@ class PrePareView: UIView { make.trailing.equalToSuperview().offset(-20) make.width.height.equalTo(30) } - tencentBigView.snp.makeConstraints { make in - make.width.equalTo(136.scale375()) - make.height.equalTo(36.scale375()) - make.leading.equalToSuperview().offset(119.scale375()) + signImageView.snp.makeConstraints { make in + make.width.equalTo(136) + make.height.equalTo(signViewHeight) + make.centerX.equalToSuperview() make.top.equalToSuperview().offset(157.scale375()) } - + signLabel.snp.makeConstraints { make in + make.top.equalTo(signImageView.snp.bottom).offset(5) + make.centerX.equalToSuperview() + make.height.equalTo(signViewHeight) + } joinRoomButton.snp.makeConstraints { make in make.height.equalTo(60.scale375()) make.width.equalTo(204.scale375()) @@ -217,6 +218,8 @@ class PrePareView: UIView { let placeholderImage = UIImage(named: "room_default_avatar") avatarButton.sd_setImage(with: URL(string: TUILogin.getFaceUrl() ?? ""), for: .normal, placeholderImage: placeholderImage) userNameLabel.text = TUILogin.getNickName() ?? "" + guard let image = getGradientImage(size: CGSize(width: UIScreen.main.bounds.width, height: signViewHeight)) else { return } + signLabel.textColor = UIColor(patternImage: image) } @objc @@ -244,6 +247,21 @@ class PrePareView: UIView { rootViewController?.switchLanguageAction() joinRoomButton.setTitle(.joinRoomText, for: .normal) createRoomButton.setTitle(.createRoomText, for: .normal) + signLabel.text = .videoConferencingText + } + + private func getGradientImage(size:CGSize) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) + guard let context = UIGraphicsGetCurrentContext() else{ return nil } + let colorSpace = CGColorSpaceCreateDeviceRGB() + guard let gradientRef = CGGradient(colorsSpace: colorSpace, colors: [UIColor(0x00CED9).cgColor, UIColor(0x0C59F2).cgColor] + as CFArray, locations: nil) else { return nil } + let startPoint = CGPoint(x: 0, y: 0) + let endPoint = CGPoint(x: size.width, y: 0) + context.drawLinearGradient(gradientRef, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(arrayLiteral: .drawsBeforeStartLocation,.drawsAfterEndLocation)) + let gradientImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return gradientImage } deinit { @@ -258,5 +276,8 @@ private extension String { static var createRoomText: String { RoomDemoLocalize("Demo.TUIRoomKit.create.room") } + static var videoConferencingText: String { + RoomDemoLocalize("Demo.TUIRoomKit.video.conferencing") + } } diff --git a/iOS/Example/App/Main/resource/localized/en.lproj/DemoLocalized.strings b/iOS/Example/App/Main/resource/localized/en.lproj/DemoLocalized.strings index 5e31bdb1b..3fef35169 100644 --- a/iOS/Example/App/Main/resource/localized/en.lproj/DemoLocalized.strings +++ b/iOS/Example/App/Main/resource/localized/en.lproj/DemoLocalized.strings @@ -25,4 +25,5 @@ "Demo.TUIRoomKit.raise.speaker" = "Raise hand Speaker Room"; "Demo.TUIRoomKit.video.conference" = "`s quick meeting"; "Demo.TUIRoomKit.prepareSetting" = "Prepare Settings"; -"Demo.TUIRoomKit.tip" = "Tencent Cloud TUIRoomKit"; +"Demo.TUIRoomKit.video.conferencing" = "Multi-party video conferencing"; +"Demo.TUIRoomKit.generating.roomId" = "Generating room number, please try again later"; diff --git a/iOS/Example/App/Main/resource/localized/zh-Hans.lproj/DemoLocalized.strings b/iOS/Example/App/Main/resource/localized/zh-Hans.lproj/DemoLocalized.strings index c8fa59386..ccd485b51 100644 --- a/iOS/Example/App/Main/resource/localized/zh-Hans.lproj/DemoLocalized.strings +++ b/iOS/Example/App/Main/resource/localized/zh-Hans.lproj/DemoLocalized.strings @@ -25,4 +25,5 @@ "Demo.TUIRoomKit.raise.speaker" = "举手发言房间"; "Demo.TUIRoomKit.video.conference" = "的快速会议"; "Demo.TUIRoomKit.prepareSetting" = "进房前设置"; -"Demo.TUIRoomKit.tip" = "腾讯云 TUIRoomKit"; +"Demo.TUIRoomKit.video.conferencing" = "多人视频会议"; +"Demo.TUIRoomKit.generating.roomId" = "正在生成房间号,请稍后重试"; diff --git a/iOS/Example/App/SceneDelegate.swift b/iOS/Example/App/SceneDelegate.swift index 363e40c23..d8aa4ecf1 100644 --- a/iOS/Example/App/SceneDelegate.swift +++ b/iOS/Example/App/SceneDelegate.swift @@ -18,7 +18,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.backgroundColor = UIColor.white let loginVC = TRTCLoginViewController() - let nav = UINavigationController(rootViewController: loginVC) + let nav = RoomNavigationController(rootViewController: loginVC) window?.rootViewController = nav window?.makeKeyAndVisible() } diff --git a/iOS/Example/DemoApp.xcodeproj/project.pbxproj b/iOS/Example/DemoApp.xcodeproj/project.pbxproj index 6277e7c89..dcb2bad6a 100644 --- a/iOS/Example/DemoApp.xcodeproj/project.pbxproj +++ b/iOS/Example/DemoApp.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -34,6 +34,7 @@ 7D93C86D2A80D0F80062B2CC /* EnterRoomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D93C86C2A80D0F80062B2CC /* EnterRoomViewController.swift */; }; 7D93C86F2A80D3B40062B2CC /* ListCellItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D93C86E2A80D3B40062B2CC /* ListCellItemView.swift */; }; 7D93C8722A80DE280062B2CC /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D93C8712A80DE280062B2CC /* String+Extension.swift */; }; + 7D9FC0D72B2C97EB0029F834 /* RoomNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D9FC0D62B2C97EB0029F834 /* RoomNavigationController.swift */; }; C18CE615279005D10071C120 /* ReplayKitLocalized.m in Sources */ = {isa = PBXBuildFile; fileRef = C18CE607279005D10071C120 /* ReplayKitLocalized.m */; }; C18CE616279005D10071C120 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C18CE608279005D10071C120 /* InfoPlist.strings */; }; C18CE617279005D10071C120 /* ReplayKitLocalized.strings in Resources */ = {isa = PBXBuildFile; fileRef = C18CE60A279005D10071C120 /* ReplayKitLocalized.strings */; }; @@ -116,6 +117,7 @@ 7D93C86C2A80D0F80062B2CC /* EnterRoomViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnterRoomViewController.swift; sourceTree = ""; }; 7D93C86E2A80D3B40062B2CC /* ListCellItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCellItemView.swift; sourceTree = ""; }; 7D93C8712A80DE280062B2CC /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; + 7D9FC0D62B2C97EB0029F834 /* RoomNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomNavigationController.swift; sourceTree = ""; }; 906FB864AA4AD0E6330C2287 /* Pods_TXReplayKit_Screen.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TXReplayKit_Screen.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 90F2D7D94B9B392568BE9C57 /* Pods-TXReplayKit_Screen.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TXReplayKit_Screen.release.xcconfig"; path = "Target Support Files/Pods-TXReplayKit_Screen/Pods-TXReplayKit_Screen.release.xcconfig"; sourceTree = ""; }; 9A118A5B5DE110B8217C48CB /* Pods-DemoApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DemoApp.release.xcconfig"; path = "Target Support Files/Pods-DemoApp/Pods-DemoApp.release.xcconfig"; sourceTree = ""; }; @@ -262,6 +264,7 @@ 7D93C8502A80C9500062B2CC /* Component */ = { isa = PBXGroup; children = ( + 7D9FC0D62B2C97EB0029F834 /* RoomNavigationController.swift */, 7D93C8512A80C9780062B2CC /* RoomTypeView.swift */, 7D93C86E2A80D3B40062B2CC /* ListCellItemView.swift */, ); @@ -643,6 +646,7 @@ FA3A4AC12A543E9B0061C7DC /* RoomFileBroswerCell.swift in Sources */, 7D5B0C9D2A3AB75600CC1585 /* UserAgreementViewController+UI.swift in Sources */, 7D93C85D2A80CB3D0062B2CC /* ListCellItemData.swift in Sources */, + 7D9FC0D72B2C97EB0029F834 /* RoomNavigationController.swift in Sources */, 7D5B0C9B2A3AB75600CC1585 /* TRTCRegisterRootView.swift in Sources */, 7D93C86B2A80D0E50062B2CC /* CreateRoomViewController.swift in Sources */, FA3A4ABF2A543A660061C7DC /* RoomFileBroswerModel.swift in Sources */, diff --git a/iOS/Example/Login/TRTCLoginViewController.swift b/iOS/Example/Login/TRTCLoginViewController.swift index 101ed82cd..1fe439ac8 100644 --- a/iOS/Example/Login/TRTCLoginViewController.swift +++ b/iOS/Example/Login/TRTCLoginViewController.swift @@ -15,6 +15,14 @@ class TRTCLoginViewController: UIViewController { let loading = UIActivityIndicatorView() + override var shouldAutorotate: Bool { + return false + } + + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + return .portrait + } + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() view.bringSubviewToFront(loading) diff --git a/iOS/Example/Login/TRTCRegisterViewController.swift b/iOS/Example/Login/TRTCRegisterViewController.swift index d04b1014a..f63522053 100644 --- a/iOS/Example/Login/TRTCRegisterViewController.swift +++ b/iOS/Example/Login/TRTCRegisterViewController.swift @@ -16,6 +16,14 @@ import TUICore class TRTCRegisterViewController: UIViewController { let loading = UIActivityIndicatorView(style: .large) + override var shouldAutorotate: Bool { + return false + } + + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + return .portrait + } + override func viewDidLoad() { super.viewDidLoad() TUICSToastManager.setDefaultPosition(TUICSToastPositionBottom) diff --git a/iOS/Example/Podfile b/iOS/Example/Podfile index 5200e979d..e9c315769 100644 --- a/iOS/Example/Podfile +++ b/iOS/Example/Podfile @@ -12,7 +12,7 @@ def tool pod 'Alamofire' pod 'TUICore' pod 'TUIChat' - pod 'TUIRoomEngine','1.6.1' + pod 'TUIRoomEngine','1.7.0' pod 'TXLiteAVSDK_TRTC' pod 'TXAppBasic' pod 'TIMCommon' @@ -47,6 +47,22 @@ post_install do |installer| config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' end end + xcode_version = `xcrun xcodebuild -version | grep Xcode | cut -d' ' -f2`.to_f + if xcode_version >= 15 + xcconfig_path = config.base_configuration_reference.real_path + xcconfig = File.read(xcconfig_path) + if xcconfig.include?("OTHER_LDFLAGS") == false + xcconfig = xcconfig + "\n" + 'OTHER_LDFLAGS = $(inherited) "-ld64"' + else + if xcconfig.include?("OTHER_LDFLAGS = $(inherited)") == false + xcconfig = xcconfig.sub("OTHER_LDFLAGS", "OTHER_LDFLAGS = $(inherited)") + end + if xcconfig.include?("-ld64") == false + xcconfig = xcconfig.sub("OTHER_LDFLAGS = $(inherited)", 'OTHER_LDFLAGS = $(inherited) "-ld64"') + end + end + File.open(xcconfig_path, "w") { |file| file << xcconfig } + end end end end diff --git a/iOS/TUIRoomKit/Resources/Localized/ar.lproj/TUIRoomKitLocalized.strings b/iOS/TUIRoomKit/Resources/Localized/ar.lproj/TUIRoomKitLocalized.strings index 56e498340..dc18086fd 100644 --- a/iOS/TUIRoomKit/Resources/Localized/ar.lproj/TUIRoomKitLocalized.strings +++ b/iOS/TUIRoomKit/Resources/Localized/ar.lproj/TUIRoomKitLocalized.strings @@ -21,6 +21,7 @@ "TUIRoom.sure.destroy.room" = "هل أنت متأكد أنك تريد إنهاء الاجتماع؟"; "TUIRoom.sure.leave.room" = "هل أنت متأكد أنك تريد مغادرة الغرفة"; +"TUIRoom.wait" = "يتمسك"; "TUIRoom.destroy.room.cancel" = "يتمسك"; "TUIRoom.ok" = "يتأكد"; "TUIRoom.cancel" = "يلغي"; @@ -71,7 +72,7 @@ "TUIRoom.leave.seat" = "انزل"; "TUIRoom.hand.down" = "بدون بذل الكثير من الجهد"; "TUIRoom.invite.to.speak" = "يدعوك المضيف للتحدث على المسرح"; -"TUIRoom.agree.to.speak" = "يدعوك المضيف للتحدث على المسرح، ويمكنك تشغيل الكاميرا والميكروفون بعد اعتلاء المسرح"; +"TUIRoom.agree.to.speak" = "يدعوك المضيف للتحدث على خشبة المسرح. وبمجرد صعودك على خشبة المسرح، يمكنك تشغيل الكاميرا وإلغاء كتم صوتك."; "TUIRoom.decline" = "يرفض"; "TUIRoom.agree" = "يوافق"; "TUIRoom.quick.meeting" = "اجتماع سريع"; @@ -153,7 +154,8 @@ "TUIRoom.share.off" = "التوقف عن المشاركة"; "TUIRoom.unfold" = "يوسع"; "TUIRoom.drop" = "وضع بعيدا"; -"TUIRoom.leave.room.tip" = "إذا كنت لا ترغب في إنهاء الغرفة، فقم بتعيين مضيف جديد قبل مغادرة الغرفة"; +"TUIRoom.appoint.owner" = "إذا كنت لا ترغب في إنهاء الغرفة، فقم بتعيين مضيف جديد قبل مغادرة الغرفة"; +"TUIRoom.leave.room.tip" = "هل أنت متأكد أنك تريد مغادرة الغرفة؟"; "TUIRoom.room.type" = "نوع الغرفة"; "TUIRoom.more" = "أكثر"; "TUIRoom.inviteMember" = "دعوة أعضاء"; @@ -181,3 +183,4 @@ "TUIRoom.take.seat.invitation.timeout" = "انتهت مهلة الدعوة للانضمام إلى ماي."; "TUIRoom.open.video.invitation.timeout" = "انتهت مهلة الدعوة لبدء الفيديو"; "TUIRoom.open.audio.invitation.timeout" = "انتهت مهلة الدعوة لتمكين الصوت"; +"TUIRoom.sure.kick.out" = "هل تريد نقل xx خارج الغرفة؟"; diff --git a/iOS/TUIRoomKit/Resources/Localized/en.lproj/TUIRoomKitLocalized.strings b/iOS/TUIRoomKit/Resources/Localized/en.lproj/TUIRoomKitLocalized.strings index ba946268b..ebb7e98dd 100644 --- a/iOS/TUIRoomKit/Resources/Localized/en.lproj/TUIRoomKitLocalized.strings +++ b/iOS/TUIRoomKit/Resources/Localized/en.lproj/TUIRoomKitLocalized.strings @@ -22,6 +22,7 @@ "TUIRoom.input.room.num" = "Enter a room ID"; "TUIRoom.sure.destroy.room" = "Are you sure you want to dismiss the meeting?"; "TUIRoom.sure.leave.room" = "Are you sure you want to leave the room?"; +"TUIRoom.wait" = "Wait"; "TUIRoom.destroy.room.cancel" = "Cancel"; "TUIRoom.ok" = "OK"; "TUIRoom.cancel" = "Cancel"; @@ -88,7 +89,7 @@ "TUIRoom.leave.seat" = "Step down"; "TUIRoom.hand.down" = "Hands down"; "TUIRoom.invite.to.speak" = "The host invites you to speak on stage"; -"TUIRoom.agree.to.speak" = "The host invites you to speak on stage, and you can turn on the camera and microphone after taking the stage"; +"TUIRoom.agree.to.speak" = "The host invites you to speak on stage. Once on stage, you can turn on the camera and unmute yourself."; "TUIRoom.decline" = "Decline"; "TUIRoom.agree" = "Agree"; "TUIRoom.quick.meeting" = "Quick meeting"; @@ -97,8 +98,8 @@ "TUIRoom.me" = "Me"; "TUIRoom.role.owner" = "Owner"; "TUIRoom.invite.members" = "Invite members to stage"; -"TUIRoom.agree.seat" = "Agree to come on stage"; -"TUIRoom.disagree.seat" = "Disagree to come on stage"; +"TUIRoom.agree.seat" = "Agree take seat"; +"TUIRoom.disagree.seat" = "Disagree take seat"; "TUIRoom.code" = "Room QR code"; "TUIRoom.scan.code" = "Scan the code to enter the room"; "TUIRoom.save.into.album" = "Save into the album"; @@ -106,7 +107,8 @@ "TUIRoom.invite.turn.on.video" = "The host invites you to turn on the video"; "TUIRoom.dismiss.meeting.Title" = "If you don't want to end the meeting"; "TUIRoom.appoint.new.host" = "Please appoint a new host before leaving the meeting"; -"TUIRoom.leave.room.tip" = "If you do not want to end the room, please appoint a new moderator before leaving the room"; +"TUIRoom.appoint.owner" = "If you do not want to end the room, please appoint a new moderator before leaving the room"; +"TUIRoom.leave.room.tip" = "Are you sure you want to leave the room"; "TUIRoom.leave.meeting" = "Leave meeting"; "TUIRoom.leave.room" = "Leave Room"; "TUIRoom.dismiss.meeting" = "Dismiss metting"; @@ -159,15 +161,14 @@ "TUIRoom.fail.microphone.permission.Enable" = "Authorize Now"; "TUIRoom.fail.camera.permission.Title" = "No access to camera"; "TUIRoom.fail.camera.permission.Tips" = "Unable to use the video function, click \"Authorize Now\" to open the camera permission."; -"TUIRoom.fail.camera.permission.Later" = "Later"; -"TUIRoom.fail.camera.permission.Enable" = "Authorize Now"; +"TUIRoom.fail.permission.Later" = "Later"; +"TUIRoom.fail.permission.Enable" = "Authorize Now"; "TUIRoom.device.set" = "Meeting Settings"; "TUIRoom.mic.set" = "Join the meeting and start the audio"; "TUIRoom.camera.set" = "Join the conference and turn on the camera"; "TUIRoom.sharing.screen" = "You are sharing your screen"; "TUIRoom.stop.share.screen" = "Stop screen sharing"; "TUIRoom.screen.recording" = "Screen recording"; - "TUIRoom.toast.shareScreen.title" = "Share Screen"; "TUIRoom.toast.shareScreen.message" = "Stop TUIRoom screen sharing screen live?"; "TUIRoom.toast.shareScreen.cancel" = "Cancel"; @@ -189,3 +190,4 @@ "TUIRoom.take.seat.invitation.timeout" = "The invitation to take seat has timed out"; "TUIRoom.open.video.invitation.timeout" = "The invitation to start the video has timed out"; "TUIRoom.open.audio.invitation.timeout" = "The invitation to start the audio has timed out"; +"TUIRoom.sure.kick.out" = "Do you want to move xx out of the room?"; diff --git a/iOS/TUIRoomKit/Resources/Localized/zh-Hans.lproj/TUIRoomKitLocalized.strings b/iOS/TUIRoomKit/Resources/Localized/zh-Hans.lproj/TUIRoomKitLocalized.strings index 6d79c8eff..1ebf4b84e 100644 --- a/iOS/TUIRoomKit/Resources/Localized/zh-Hans.lproj/TUIRoomKitLocalized.strings +++ b/iOS/TUIRoomKit/Resources/Localized/zh-Hans.lproj/TUIRoomKitLocalized.strings @@ -23,8 +23,8 @@ "TUIRoom.sure.destroy.room" = "你确定要结束会议吗?"; "TUIRoom.sure.leave.room" = "你确定要离开房间吗?"; -"TUIRoom.destroy.room.cancel" = "再等等"; -"TUIRoom.ok" = "确认"; +"TUIRoom.wait" = "再等等"; +"TUIRoom.ok" = "确定"; "TUIRoom.cancel" = "取消"; "TUIRoom.invite.join" = "您可以分享房间号或链接邀请更多人加入房间"; "TUIRoom.logout.member.list" = "成员列表"; @@ -86,7 +86,7 @@ "TUIRoom.leave.seat" = "下台"; "TUIRoom.hand.down" = "手放下"; "TUIRoom.invite.to.speak" = "主持人邀请您上台发言"; -"TUIRoom.agree.to.speak" = "主持人邀请您上台发言,上台后可以打开摄像头和麦克风"; +"TUIRoom.agree.to.speak" = "主持人邀请您上台发言,上台后可以打开摄像头和取消静音"; "TUIRoom.decline" = "拒绝"; "TUIRoom.agree" = "同意"; "TUIRoom.quick.meeting" = "快速会议"; @@ -104,7 +104,8 @@ "TUIRoom.invite.turn.on.video" = "主持人邀请您开启视频画面"; "TUIRoom.dismiss.meeting.Title" = "如果您不想结束会议"; "TUIRoom.appoint.new.host" = "请在离开会议前指定新的主持人"; -"TUIRoom.leave.room.tip" = "如果您不想结束房间,请在离开房间前指定新的主持人"; +"TUIRoom.appoint.owner" = "如果您不想结束房间,请在离开房间前指定新的主持人"; +"TUIRoom.leave.room.tip"= "您确定要离开房间吗"; "TUIRoom.leave.meeting" = "离开会议"; "TUIRoom.leave.room" = "离开房间"; "TUIRoom.dismiss.meeting" = "结束会议"; @@ -186,3 +187,4 @@ "TUIRoom.take.seat.invitation.timeout" = "上麦邀请已经超时"; "TUIRoom.open.video.invitation.timeout" = "开启视频的邀请已经超时"; "TUIRoom.open.audio.invitation.timeout" = "开启音频的邀请已经超时"; +"TUIRoom.sure.kick.out" = "是否将xx移出房间?"; diff --git a/iOS/TUIRoomKit/RoomExtension/Model/Manager/RoomManager.swift b/iOS/TUIRoomKit/RoomExtension/Model/Manager/RoomManager.swift index e7fbe1b6b..eeabc720f 100644 --- a/iOS/TUIRoomKit/RoomExtension/Model/Manager/RoomManager.swift +++ b/iOS/TUIRoomKit/RoomExtension/Model/Manager/RoomManager.swift @@ -66,6 +66,7 @@ class RoomManager { func enterRoom(roomId: String) { roomObserver.registerObserver() + engineManager.store.isImAccess = true engineManager.enterRoom(roomId: roomId, enableAudio: engineManager.store.isOpenMicrophone, enableVideo: engineManager.store.isOpenCamera, isSoundOnSpeaker: true) { [weak self] in guard let self = self else { return } diff --git a/iOS/TUIRoomKit/Source/Common/CGFloat+Extension.swift b/iOS/TUIRoomKit/Source/Common/CGFloat+Extension.swift index e0d1eef11..bb1673ea2 100644 --- a/iOS/TUIRoomKit/Source/Common/CGFloat+Extension.swift +++ b/iOS/TUIRoomKit/Source/Common/CGFloat+Extension.swift @@ -37,10 +37,10 @@ public let kDeviceSafeBottomHeight : CGFloat = { } }() private var width: CGFloat { - return isLandscape ? kScreenHeight : kScreenWidth + return min(kScreenHeight, kScreenWidth) } private var height: CGFloat { - return isLandscape ? kScreenWidth : kScreenHeight + return max(kScreenWidth, kScreenHeight) } extension CGFloat { diff --git a/iOS/TUIRoomKit/Source/Common/UIImage+RTL.swift b/iOS/TUIRoomKit/Source/Common/UIImage+RTL.swift index a79eae1cc..132f7ad05 100644 --- a/iOS/TUIRoomKit/Source/Common/UIImage+RTL.swift +++ b/iOS/TUIRoomKit/Source/Common/UIImage+RTL.swift @@ -16,7 +16,7 @@ extension UIImage { bitmap.translateBy(x: self.size.width / 2, y: self.size.height / 2) bitmap.scaleBy(x: -1.0, y: -1.0) bitmap.translateBy(x: -self.size.width / 2, y: -self.size.height / 2) - bitmap.draw(cgImage, in: CGRectMake(0, 0, self.size.width, self.size.height)) + bitmap.draw(cgImage, in: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)) let image = UIGraphicsGetImageFromCurrentImageContext() return image } diff --git a/iOS/TUIRoomKit/Source/Model/EngineEventCenter.swift b/iOS/TUIRoomKit/Source/Model/EngineEventCenter.swift index b1e452a31..ac67d7367 100644 --- a/iOS/TUIRoomKit/Source/Model/EngineEventCenter.swift +++ b/iOS/TUIRoomKit/Source/Model/EngineEventCenter.swift @@ -59,6 +59,10 @@ class EngineEventCenter: NSObject { case onUserScreenCaptureStopped case onRequestReceived case onSendMessageForUserDisableChanged + case onRemoteUserEnterRoom + case onRemoteUserLeaveRoom + case onUserRoleChanged + case onSeatListChanged } enum RoomUIEvent: String { @@ -81,6 +85,7 @@ class EngineEventCenter: NSObject { case TUIRoomKitService_SetToolBarDelayHidden //设定工具栏是否3秒之后隐藏(参数:isDelay) case TUIRoomKitService_HiddenChatWindow //隐藏聊天窗口 case TUIRoomKitService_ShowExitRoomView //显示离开房间页面 + case TUIRoomKitService_RenewVideoSeatView //更新视频页面 } /// 注册UI响应相关监听事件 diff --git a/iOS/TUIRoomKit/Source/Model/EngineManager.swift b/iOS/TUIRoomKit/Source/Model/EngineManager.swift index f6426138e..7c7ddd704 100644 --- a/iOS/TUIRoomKit/Source/Model/EngineManager.swift +++ b/iOS/TUIRoomKit/Source/Model/EngineManager.swift @@ -38,7 +38,7 @@ class EngineManager: NSObject { let eventDispatcher = RoomEventDispatcher() return eventDispatcher }() - private let timeOutNumber: Double = 0 + private let timeOutNumber: Double = 10 private let rootRouter: RoomRouter = RoomRouter.shared private var isLoginEngine: Bool = false private let appGroupString: String = "com.tencent.TUIRoomTXReplayKit-Screen" @@ -98,35 +98,25 @@ class EngineManager: NSObject { } func exitRoom(onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) { - roomEngine.getTRTCCloud().stopAllRemoteView() roomEngine.exitRoom(syncWaiting: false) { [weak self] in guard let self = self else { return } self.destroyEngineManager() - EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ExitedRoom, param: ["isExited":true]) + EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ExitedRoom, param: [:]) onSuccess?() - } onError: { [weak self] code, message in - guard let self = self else { return } - self.destroyEngineManager() - EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_ExitedRoom, param: ["isExited":false]) + } onError: { code, message in onError?(code, message) } - TRTCCloud.destroySharedIntance() } func destroyRoom(onSuccess: TUISuccessBlock? = nil, onError: TUIErrorBlock? = nil) { - roomEngine.getTRTCCloud().stopAllRemoteView() roomEngine.destroyRoom { [weak self] in guard let self = self else { return } self.destroyEngineManager() - EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_DestroyedRoom, param: ["isDestroyed":true]) + EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_DestroyedRoom, param: [:]) onSuccess?() - } onError: { [weak self] code, message in - guard let self = self else { return } - self.destroyEngineManager() - EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_DestroyedRoom, param: ["isDestroyed":false]) + } onError: { code, message in onError?(code, message) } - TRTCCloud.destroySharedIntance() } func destroyEngineManager() { @@ -239,20 +229,32 @@ class EngineManager: NSObject { onCancelled: TUIRequestCancelledBlock? = nil, onTimeout: TUIRequestTimeoutBlock? = nil, onError: TUIRequestErrorBlock? = nil) { - roomEngine.takeUserOnSeatByAdmin(-1, userId: userId, timeout: timeout) { requestId, userId in + store.extendedInvitationList.append(userId) + roomEngine.takeUserOnSeatByAdmin(-1, userId: userId, timeout: timeout) { [weak self] requestId, userId in + guard let self = self else { return } + self.store.extendedInvitationList.removeAll(where: { $0 == userId }) onAccepted?(requestId, userId) - } onRejected: { requestId, userId, message in - onRejected?(requestId, userId, message) - } onCancelled: { requestId, userId in + } onRejected: { [weak self] requestId, userId, message in + guard let self = self else { return } + self.store.extendedInvitationList.removeAll(where: { $0 == userId }) + onRejected?( requestId, userId, message) + } onCancelled: { [weak self] requestId, userId in + guard let self = self else { return } + self.store.extendedInvitationList.removeAll(where: { $0 == userId }) onCancelled?(requestId, userId) - } onTimeout: { requestId, userId in + } onTimeout: { [weak self] requestId, userId in + guard let self = self else { return } + self.store.extendedInvitationList.removeAll(where: { $0 == userId }) onTimeout?(requestId, userId) - } onError: { requestId, userId, code, message in + } onError: { [weak self] requestId, userId, code, message in + guard let self = self else { return } + self.store.extendedInvitationList.removeAll(where: { $0 == userId }) onError?(requestId, userId, code, message) } } func setAudioRoute(route: TRTCAudioRoute) { + store.audioSetting.isSoundOnSpeaker = route == .modeSpeakerphone roomEngine.getTRTCCloud().setAudioRoute(route) } @@ -524,6 +526,10 @@ class EngineManager: NSObject { func changeRaiseHandNoticeState(isShown: Bool) { store.isShownRaiseHandNotice = isShown } + + func setRemoteRenderParams(userId: String, streamType: TRTCVideoStreamType, params: TRTCRenderParams) { + roomEngine.getTRTCCloud().setRemoteRenderParams(userId, streamType: streamType, params: params) + } } // MARK: - Private @@ -688,6 +694,9 @@ extension EngineManager { } else { self.store.attendeeList = localUserList onSuccess() + if self.store.roomInfo.speechMode != .applySpeakAfterTakingSeat { + EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewVideoSeatView, param: [:]) + } EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewUserList, param: [:]) } } onError: { code, message in @@ -713,6 +722,7 @@ extension EngineManager { self.store.seatList = localSeatList onSuccess() EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewSeatList, param: [:]) + EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_RenewVideoSeatView, param: [:]) } onError: { code, message in onError(code, message) debugPrint("getSeatList:code:\(code),message:\(message)") @@ -756,14 +766,16 @@ extension EngineManager: TUIExtensionProtocol { extension EngineManager { fileprivate static let TUIRoomKitFrameworkValue = 1 fileprivate static let TUIRoomKitComponentValue = 18 + fileprivate static let IMComponentValue = 19 fileprivate static let TUIRoomKitLanguageValue = 3 private func setFramework() { + let componentValue = store.isImAccess ? EngineManager.IMComponentValue : EngineManager.TUIRoomKitComponentValue let jsonStr = """ { "api":"setFramework", "params":{ "framework":\(EngineManager.TUIRoomKitFrameworkValue), - "component":\(EngineManager.TUIRoomKitComponentValue), + "component":\(componentValue), "language":\(EngineManager.TUIRoomKitLanguageValue) } } diff --git a/iOS/TUIRoomKit/Source/Model/RoomEventDispatcher.swift b/iOS/TUIRoomKit/Source/Model/RoomEventDispatcher.swift index 285fcee35..1387e0927 100644 --- a/iOS/TUIRoomKit/Source/Model/RoomEventDispatcher.swift +++ b/iOS/TUIRoomKit/Source/Model/RoomEventDispatcher.swift @@ -65,14 +65,29 @@ extension RoomEventDispatcher: TUIRoomObserver { // MARK: - 房间内用户事件回调 func onRemoteUserEnterRoom(roomId: String, userInfo: TUIUserInfo) { remoteUserEnterRoom(roomId: roomId, userInfo: userInfo) + let param = [ + "roomId" : roomId, + "userInfo" : userInfo, + ] as [String : Any] + EngineEventCenter.shared.notifyEngineEvent(event: .onRemoteUserEnterRoom, param: param) } func onRemoteUserLeaveRoom(roomId: String, userInfo: TUIUserInfo) { remoteUserLeaveRoom(roomId: roomId, userInfo: userInfo) + let param = [ + "roomId" : roomId, + "userInfo" : userInfo, + ] as [String : Any] + EngineEventCenter.shared.notifyEngineEvent(event: .onRemoteUserLeaveRoom, param: param) } func onUserRoleChanged(userId: String, userRole: TUIRole) { userRoleChanged(userId: userId, userRole: userRole) + let param = [ + "userId" : userId, + "userRole" : userRole, + ] as [String : Any] + EngineEventCenter.shared.notifyEngineEvent(event: .onUserRoleChanged, param: param) } func onUserVideoStateChanged(userId: String, streamType: TUIVideoStreamType, hasVideo: Bool, reason: TUIChangeReason) { @@ -108,6 +123,12 @@ extension RoomEventDispatcher: TUIRoomObserver { func onSeatListChanged(seatList: [TUISeatInfo], seated seatedList: [TUISeatInfo], left leftList: [TUISeatInfo]) { seatListChanged(seatList: seatList,seated: seatedList, left: leftList) + let param = [ + "seatList": seatList, + "seated": seatedList, + "left": leftList, + ] as [String : Any] + EngineEventCenter.shared.notifyEngineEvent(event: .onSeatListChanged, param: param) } func OnSendMessageForUserDisableChanged(roomId: String, userId: String, isDisable muted: Bool) { @@ -153,6 +174,7 @@ extension RoomEventDispatcher { //判断自己是否下麦 if leftList.first(where: { $0.userId == currentUser.userId }) != nil { currentUser.isOnSeat = false + store.audioSetting.isMicOpened = false if currentUser.hasScreenStream { //如果正在进行屏幕共享,要把屏幕共享关闭。 engineManager.stopScreenCapture() } diff --git a/iOS/TUIRoomKit/Source/Model/RoomStore.swift b/iOS/TUIRoomKit/Source/Model/RoomStore.swift index 60de096de..168804802 100644 --- a/iOS/TUIRoomKit/Source/Model/RoomStore.swift +++ b/iOS/TUIRoomKit/Source/Model/RoomStore.swift @@ -24,6 +24,8 @@ class RoomStore: NSObject { var isEnteredRoom: Bool = false //是否已经进入房间 var timeStampOnEnterRoom: Int = 0 //进入会议的时间戳 var isShowRoomMainViewAutomatically: Bool = true //true 调用createRoom或者enterRoom会自动进入主界面; false 需要调用 showRoomMainView 才能进入主界面。 + var extendedInvitationList : [String] = [] //已经发出邀请的用户列表 + var isImAccess: Bool = false //是否由IM进入的TUIRoomKit private let openCameraKey = "isOpenCamera" private let openMicrophoneKey = "isOpenMicrophone" private let shownRaiseHandNoticeKey = "isShownRaiseHandNotice" @@ -65,7 +67,7 @@ class RoomStore: NSObject { } func initialRoomCurrentUser() { - EngineManager.createInstance().getUserInfo(currentUser.userId) { [weak self] userInfo in + EngineManager.createInstance().getUserInfo(TUILogin.getUserID() ?? "") { [weak self] userInfo in guard let self = self else { return } guard let userInfo = userInfo else { return } self.currentUser.update(userInfo: userInfo) @@ -73,4 +75,8 @@ class RoomStore: NSObject { debugPrint("getUserInfo,code:\(code),message:\(message)") } } + + deinit { + debugPrint("self:\(self),deinit") + } } diff --git a/iOS/TUIRoomKit/Source/Model/VideoSeatItem.swift b/iOS/TUIRoomKit/Source/Model/VideoSeatItem.swift index 1bd4d213e..1dfd50c63 100644 --- a/iOS/TUIRoomKit/Source/Model/VideoSeatItem.swift +++ b/iOS/TUIRoomKit/Source/Model/VideoSeatItem.swift @@ -18,50 +18,35 @@ class VideoSeatItem: Equatable { static func == (lhs: VideoSeatItem, rhs: VideoSeatItem) -> Bool { return (lhs.userId == rhs.userId) && (lhs.type == rhs.type) } - + private var itemType: VideoSeatItemType = .original private var videoStreamType: TUIVideoStreamType = .cameraStream - private var userInfo: TUIUserInfo - var isPlaying: Bool = false + private var userInfo: UserEntity var audioVolume: Int = 0 - weak var boundCell: TUIVideoSeatCell? var isSelf: Bool { - return userInfo.userId == TUIRoomEngine.getSelfInfo().userId + return userInfo.userId == EngineManager.createInstance().store.currentUser.userId } var streamType: TUIVideoStreamType { return videoStreamType } - + var type: VideoSeatItemType { return itemType } - + var userId: String { return userInfo.userId } - - var isRoomOwner: Bool { - return userRole == .roomOwner - } - + var userName: String { return userInfo.userName } - + var avatarUrl: String { return userInfo.avatarUrl } - - var userRole: TUIRole { - set { - userInfo.userRole = newValue - } - get { - return userInfo.userRole - } - } - + var hasAudioStream: Bool { set { userInfo.hasAudioStream = newValue @@ -70,7 +55,7 @@ class VideoSeatItem: Equatable { return userInfo.hasAudioStream } } - + var hasVideoStream: Bool { set { userInfo.hasVideoStream = newValue @@ -79,7 +64,7 @@ class VideoSeatItem: Equatable { return userInfo.hasVideoStream } } - + var hasScreenStream: Bool { set { userInfo.hasScreenStream = newValue @@ -88,31 +73,26 @@ class VideoSeatItem: Equatable { return userInfo.hasScreenStream } } - + var isHasVideoStream: Bool { return hasVideoStream || hasScreenStream } - init(userId: String) { - userInfo = TUIUserInfo() - userInfo.userId = userId - } - - init(userInfo: TUIUserInfo) { + init(userInfo: UserEntity) { self.userInfo = userInfo } - - func updateUserInfo(_ userInfo: TUIUserInfo) { + + func updateUserInfo(_ userInfo: UserEntity) { self.userInfo = userInfo } - + func cloneShare() -> VideoSeatItem { let item = VideoSeatItem(userInfo: userInfo) item.videoStreamType = .screenStream item.itemType = .share return item } - + func updateStreamType(streamType: TUIVideoStreamType) { if videoStreamType != .screenStream { videoStreamType = streamType diff --git a/iOS/TUIRoomKit/Source/View/Component/ListCellItemView.swift b/iOS/TUIRoomKit/Source/View/Component/ListCellItemView.swift index ef3668c96..e0565df7a 100644 --- a/iOS/TUIRoomKit/Source/View/Component/ListCellItemView.swift +++ b/iOS/TUIRoomKit/Source/View/Component/ListCellItemView.swift @@ -114,18 +114,18 @@ class ListCellItemView: UIView { make.height.equalTo(20.scale375()) } - sliderLabel.snp.makeConstraints { make in - make.leading.equalToSuperview().offset(85.scale375()) - make.trailing.equalToSuperview().offset(-160.scale375()) - make.centerY.equalToSuperview() - } - slider.snp.makeConstraints { make in make.trailing.equalToSuperview() make.width.equalTo(152.scale375()) make.centerY.equalToSuperview() } + sliderLabel.snp.makeConstraints { make in + make.leading.equalTo(titleLabel.snp.trailing).offset(5.scale375()) + make.trailing.equalTo(slider.snp.leading).offset(-5.scale375()) + make.centerY.equalToSuperview() + } + rightSwitch.snp.makeConstraints { make in make.trailing.equalToSuperview() make.centerY.equalToSuperview() diff --git a/iOS/TUIRoomKit/Source/View/Page/RoomMainView.swift b/iOS/TUIRoomKit/Source/View/Page/RoomMainView.swift index e91a24e2b..c316aa748 100644 --- a/iOS/TUIRoomKit/Source/View/Page/RoomMainView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/RoomMainView.swift @@ -139,7 +139,7 @@ class RoomMainView: UIView { bottomView.snp.remakeConstraints { make in make.leading.equalTo(safeAreaLayoutGuide.snp.leading) make.trailing.equalTo(safeAreaLayoutGuide.snp.trailing) - make.height.equalTo(bottomView.isUnfold ? 130.scale375() : 60.scale375()) + make.height.equalTo(bottomView.isUnfold ? bottomView.unfoldHeight : bottomView.packUpHeight) if isLandscape { make.bottom.equalToSuperview().offset(-layout.bottomViewLandscapeSpace) } else { @@ -165,22 +165,8 @@ extension RoomMainView: RoomMainViewResponder { muteAudioButton.isSelected = isSelected } - func showAlert(title: String?, message: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?) { - let alertVC = UIAlertController(title: title, - message: message, - preferredStyle: .alert) - let sureTitle: String = declineBlock != nil ? .agreeText : .alertOkText - let sureAction = UIAlertAction(title: sureTitle, style: .default) { _ in - sureBlock?() - } - alertVC.addAction(sureAction) - if let declineBlock = declineBlock { - let declineAction = UIAlertAction(title: .declineText, style: .destructive) { _ in - declineBlock() - } - alertVC.addAction(declineAction) - } - RoomRouter.shared.presentAlert(alertVC) + func showAlert(title: String?, message: String?, sureTitle:String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?) { + RoomRouter.presentAlert(title: title, message: message, sureTitle: sureTitle, declineTitle: declineTitle, sureBlock: sureBlock, declineBlock: declineBlock) } func makeToast(text: String) { @@ -231,15 +217,3 @@ extension RoomMainView: RoomMainViewResponder { perform(#selector(hideToolBar),with: nil,afterDelay: delayDisappearanceTime) } } - -private extension String { - static var alertOkText: String { - localized("TUIRoom.ok") - } - static var declineText: String { - localized("TUIRoom.decline") - } - static var agreeText: String { - localized("TUIRoom.agree") - } -} diff --git a/iOS/TUIRoomKit/Source/View/Page/RoomRouter.swift b/iOS/TUIRoomKit/Source/View/Page/RoomRouter.swift index 2cbb22c2e..33a059a14 100644 --- a/iOS/TUIRoomKit/Source/View/Page/RoomRouter.swift +++ b/iOS/TUIRoomKit/Source/View/Page/RoomRouter.swift @@ -174,8 +174,22 @@ class RoomRouter: NSObject { } } - func presentAlert(_ alertController: UIAlertController) { - getCurrentWindowViewController()?.present(alertController, animated: true) + class func presentAlert(title: String?, message: String?, sureTitle:String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?) { + let alertVC = UIAlertController(title: title, + message: message, + preferredStyle: .alert) + if let declineTitle = declineTitle { + let declineAction = UIAlertAction(title: declineTitle, style: .destructive) { _ in + declineBlock?() + } + declineAction.setValue(UIColor(0x4F586B), forKey: "titleTextColor") + alertVC.addAction(declineAction) + } + let sureAction = UIAlertAction(title: sureTitle, style: .default) { _ in + sureBlock?() + } + alertVC.addAction(sureAction) + shared.getCurrentWindowViewController()?.present(alertVC, animated: true) } class func makeToast(toast: String) { diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/BottomNavigationBar/BottomView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/BottomNavigationBar/BottomView.swift index 42f07fdbe..39db65aec 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/BottomNavigationBar/BottomView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/BottomNavigationBar/BottomView.swift @@ -17,6 +17,8 @@ class BottomView: UIView { var dropButtonItem: BottomItemView? var recordButtonItem: BottomItemView? var isUnfold: Bool = false //是否展开 + let unfoldHeight = Float(130.scale375Height()) + let packUpHeight = Float(68.scale375Height()) let baseButtonMenuView: UIStackView = { let view = UIStackView() @@ -168,7 +170,7 @@ class BottomView: UIView { func activateConstraints() { backgroundView.snp.makeConstraints { make in make.bottom.leading.trailing.equalToSuperview() - make.height.equalTo(60.scale375()) + make.height.equalTo(packUpHeight) } let width = min(kScreenWidth, kScreenHeight) buttonMenuView.snp.makeConstraints { make in @@ -198,6 +200,10 @@ class BottomView: UIView { } extension BottomView: BottomViewModelResponder { + func showStopShareScreenAlert(sureBlock: (() -> ())?) { + RoomRouter.presentAlert(title: .toastTitleText, message: .toastMessageText, sureTitle: .toastStopText, declineTitle: .toastCancelText, sureBlock: sureBlock, declineBlock: nil) + } + func updateStackView(item: ButtonItemData, index: Int) { guard viewArray.count > index else { return } viewArray[index].setupViewState(item: item) @@ -211,7 +217,7 @@ extension BottomView: BottomViewModelResponder { UIView.animate(withDuration: 0.3) { [weak self] () in guard let self = self else { return } self.snp.updateConstraints { make in - make.height.equalTo(isUnfold ? 130.scale375() : 60.scale375()) + make.height.equalTo(isUnfold ? self.unfoldHeight : self.packUpHeight) } self.superview?.layoutIfNeeded() } completion: { _ in @@ -266,7 +272,7 @@ private extension String { } static var destroyRoomCancelTitle: String { - localized("TUIRoom.destroy.room.cancel") + localized("TUIRoom.wait") } static var logoutOkText: String { @@ -296,4 +302,17 @@ private extension String { static var memberText: String { localized("TUIRoom.conference.member") } + + static var toastTitleText: String { + localized("TUIRoom.toast.shareScreen.title") + } + static var toastMessageText: String { + localized("TUIRoom.toast.shareScreen.message") + } + static var toastCancelText: String { + localized("TUIRoom.toast.shareScreen.cancel") + } + static var toastStopText: String { + localized("TUIRoom.toast.shareScreen.stop") + } } diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/Dialog/ExitRoomView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/Dialog/ExitRoomView.swift index 8b8c13450..fa0725378 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/Dialog/ExitRoomView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/Dialog/ExitRoomView.swift @@ -31,14 +31,14 @@ class ExitRoomView: UIView { return view }() - let titleLabel: UILabel = { + lazy var titleLabel: UILabel = { let label = UILabel() - label.text = .leaveRoomTipText label.textColor = UIColor(0x7C85A6) label.font = UIFont(name: "PingFangSC-Regular", size: 12) label.textAlignment = .center label.numberOfLines = 0 label.lineBreakMode = .byWordWrapping + label.text = viewModel.isShownExitRoomButton() && viewModel.isShownLeaveRoomButton() ? .appointOwnerText : .leaveRoomTipText return label }() @@ -55,7 +55,6 @@ class ExitRoomView: UIView { button.setTitleColor(UIColor(0x006CFF), for: .normal) button.backgroundColor = UIColor(0x17181F) button.isEnabled = true - button.addTarget(self, action: #selector(leaveRoomAction), for: .touchUpInside) return button }() @@ -72,7 +71,6 @@ class ExitRoomView: UIView { button.setTitleColor(UIColor(0xE5395C), for: .normal) button.backgroundColor = UIColor(0x17181F) button.isEnabled = true - button.addTarget(self, action: #selector(exitRoomAction), for: .touchUpInside) return button }() @@ -92,9 +90,9 @@ class ExitRoomView: UIView { activateConstraints() bindInteraction() isViewReady = true - exitRoomButton.isHidden = currentUser.userId != roomInfo.ownerId - leaveRoomButton.isHidden = !viewModel.isShowLeaveRoomButton() - boundary2View.isHidden = currentUser.userId != roomInfo.ownerId || !viewModel.isShowLeaveRoomButton() + exitRoomButton.isHidden = !viewModel.isShownExitRoomButton() + leaveRoomButton.isHidden = !viewModel.isShownLeaveRoomButton() + boundary2View.isHidden = !viewModel.isShownExitRoomButton() || !viewModel.isShownLeaveRoomButton() } func constructViewHierarchy() { @@ -109,7 +107,7 @@ class ExitRoomView: UIView { func activateConstraints() { let titleLabelHeight = 67.scale375Height() - let leaveRoomButtonHeight = viewModel.isShowLeaveRoomButton() ? 57.scale375Height() : 0 + let leaveRoomButtonHeight = viewModel.isShownLeaveRoomButton() ? 57.scale375Height() : 0 let exitRoomButtonHeight = currentUser.userId == roomInfo.ownerId ? 57.scale375Height() : 0 let space = 20.scale375Height() let contentViewHeight = titleLabelHeight + leaveRoomButtonHeight + exitRoomButtonHeight + space @@ -149,6 +147,8 @@ class ExitRoomView: UIView { } func bindInteraction() { + leaveRoomButton.addTarget(self, action: #selector(leaveRoomAction), for: .touchUpInside) + exitRoomButton.addTarget(self, action: #selector(exitRoomAction), for: .touchUpInside) contentView.transform = CGAffineTransform(translationX: 0, y: kScreenHeight) panelControl.addTarget(self, action: #selector(clickBackgroundView), for: .touchUpInside) } @@ -195,10 +195,19 @@ class ExitRoomView: UIView { } } +extension ExitRoomView: ExitRoomViewModelResponder { + func makeToast(message: String) { + makeToast(message) + } +} + private extension String { static var leaveRoomTipText: String { localized("TUIRoom.leave.room.tip" ) } + static var appointOwnerText: String { + localized("TUIRoom.appoint.owner" ) + } static var leaveRoomText: String { localized("TUIRoom.leave.room") } diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/RaiseHandControlPanel/RaiseHandApplicationListView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/RaiseHandControlPanel/RaiseHandApplicationListView.swift index b6d8d1b73..b8b4b4233 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/RaiseHandControlPanel/RaiseHandApplicationListView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/RaiseHandControlPanel/RaiseHandApplicationListView.swift @@ -290,6 +290,7 @@ class ApplyTableCell: UITableViewCell { button.setTitle(.agreeSeatText, for: .normal) button.setTitleColor(.white, for: .normal) button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .regular) + button.titleLabel?.adjustsFontSizeToFitWidth = true button.layer.cornerRadius = 6 button.clipsToBounds = true return button @@ -300,6 +301,7 @@ class ApplyTableCell: UITableViewCell { button.backgroundColor = .red button.setTitle(.disagreeSeatText, for: .normal) button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .regular) + button.titleLabel?.adjustsFontSizeToFitWidth = true button.setTitleColor(.white, for: .normal) button.layer.cornerRadius = 6 button.clipsToBounds = true diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/TransferOwnerControlPanel/TransferMasterView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/TransferOwnerControlPanel/TransferMasterView.swift index e38d9777a..c548ebf13 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/TransferOwnerControlPanel/TransferMasterView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/TransferOwnerControlPanel/TransferMasterView.swift @@ -205,6 +205,10 @@ extension TransferMasterView: UITableViewDelegate { } extension TransferMasterView: TransferMasterViewResponder { + func makeToast(message: String) { + makeToast(message) + } + func reloadTransferMasterTableView() { guard !isSearching else { return } attendeeList = viewModel.attendeeList diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/UserControlPanel/UserListManagerView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/UserControlPanel/UserListManagerView.swift index a31cd12ff..8f5027b52 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/UserControlPanel/UserListManagerView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/UserControlPanel/UserListManagerView.swift @@ -172,6 +172,10 @@ class UserListManagerView: UIView { } extension UserListManagerView: UserListManagerViewEventResponder { + func showKickOutAlert(title: String, sureAction: (() -> ())?) { + RoomRouter.presentAlert(title: title, message: nil, sureTitle: .alertOkText, declineTitle: .cancelText, sureBlock: sureAction, declineBlock: nil) + } + func updateUI(items: [ButtonItemData]) { setupViewState() viewArray.forEach { view in @@ -203,13 +207,7 @@ extension UserListManagerView: UserListManagerViewEventResponder { } func showTransferredRoomOwnerAlert() { - let alertVC = UIAlertController(title: .haveTransferredMasterText, - message: nil, - preferredStyle: .alert) - let sureAction = UIAlertAction(title: .alertOkText, style: .cancel) { _ in - } - alertVC.addAction(sureAction) - RoomRouter.shared.presentAlert(alertVC) + RoomRouter.presentAlert(title: .haveTransferredMasterText, message: nil, sureTitle: .alertOkText, declineTitle: nil, sureBlock: nil, declineBlock: nil) } func setUserListManagerViewHidden(isHidden: Bool) { @@ -227,5 +225,8 @@ private extension String { static var haveTransferredMasterText: String { localized("TUIRoom.have.transferred.master") } + static var cancelText: String { + localized("TUIRoom.cancel") + } } diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/UserControlPanel/UserListView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/UserControlPanel/UserListView.swift index 804f6dfcb..972f5dc1b 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/UserControlPanel/UserListView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/UserControlPanel/UserListView.swift @@ -100,6 +100,12 @@ class UserListView: UIView { return button }() + let bottomView: UIView = { + let view = UIView() + view.backgroundColor = UIColor(0x17181F) + return view + }() + lazy var userListTableView: UITableView = { let tableView = UITableView(frame: .zero, style: .plain) tableView.separatorStyle = .none @@ -141,9 +147,10 @@ class UserListView: UIView { addSubview(searchBar) addSubview(inviteButton) addSubview(userListTableView) - addSubview(muteAllAudioButton) - addSubview(muteAllVideoButton) - addSubview(moreFunctionButton) + addSubview(bottomView) + bottomView.addSubview(muteAllAudioButton) + bottomView.addSubview(muteAllVideoButton) + bottomView.addSubview(moreFunctionButton) addSubview(blurView) addSubview(userListManagerView) addSubview(searchControl) @@ -168,26 +175,30 @@ class UserListView: UIView { make.trailing.equalToSuperview().offset(-16.scale375()) make.height.equalTo(36.scale375()) } + bottomView.snp.makeConstraints { make in + make.leading.trailing.bottom.equalToSuperview() + make.height.equalTo(84.scale375Height()) + } userListTableView.snp.makeConstraints { make in make.leading.equalToSuperview().offset(16.scale375()) make.trailing.equalToSuperview().offset(-16.scale375()) make.top.equalToSuperview().offset(127.scale375()) - make.bottom.equalToSuperview() + make.bottom.equalTo(bottomView.snp.top) } muteAllAudioButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().offset(-34.scale375()) + make.top.equalToSuperview().offset(10.scale375Height()) make.leading.equalToSuperview().offset(16.scale375()) make.width.equalTo(108.scale375()) make.height.equalTo(40.scale375()) } muteAllVideoButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().offset(-34.scale375()) + make.top.equalToSuperview().offset(10.scale375Height()) make.leading.equalToSuperview().offset(133.scale375()) make.width.equalTo(108.scale375()) make.height.equalTo(40.scale375()) } moreFunctionButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().offset(-34.scale375()) + make.top.equalToSuperview().offset(10.scale375Height()) make.leading.equalToSuperview().offset(250.scale375()) make.width.equalTo(108.scale375()) make.height.equalTo(40.scale375()) @@ -396,6 +407,7 @@ class UserListCell: UITableViewCell { button.setTitle(.inviteSeatText, for: .normal) button.setTitleColor(UIColor(0xFFFFFF), for: .normal) button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .regular) + button.isHidden = true return button }() @@ -453,8 +465,6 @@ class UserListCell: UITableViewCell { make.centerY.equalTo(self.avatarImageView) } inviteStageButton.snp.makeConstraints { make in - make.width.equalTo(62.scale375()) - make.height.equalTo(24.scale375Height()) make.trailing.equalToSuperview() make.centerY.equalTo(self.avatarImageView) } @@ -512,18 +522,13 @@ class UserListCell: UITableViewCell { muteAudioButton.isSelected = !item.hasAudioStream muteVideoButton.isSelected = !item.hasVideoStream //判断是否显示邀请上台的按钮(房主在举手发言房间中可以邀请其他没有上台的用户) - switch viewModel.roomInfo.speechMode { - case .freeToSpeak: - changeInviteStageButtonHidden(isHidden: true) - case .applySpeakAfterTakingSeat: - //房主可以邀请没有上麦的成员上麦 - if viewModel.currentUser.userId == viewModel.roomInfo.ownerId, attendeeModel.userId != viewModel.roomInfo.ownerId, - !attendeeModel.isOnSeat { - changeInviteStageButtonHidden(isHidden: false) - } else { - changeInviteStageButtonHidden(isHidden: true) - } - default: break + guard viewModel.roomInfo.speechMode == .applySpeakAfterTakingSeat else { return } + muteAudioButton.isHidden = !attendeeModel.isOnSeat + muteVideoButton.isHidden = !attendeeModel.isOnSeat + if viewModel.currentUser.userId == viewModel.roomInfo.ownerId { + inviteStageButton.isHidden = attendeeModel.isOnSeat + } else { + inviteStageButton.isHidden = true } } @@ -536,13 +541,6 @@ class UserListCell: UITableViewCell { viewModel.showUserManageViewAction(userId: attendeeModel.userId, userName: attendeeModel.userName) } - //是否显示邀请按钮(如果显示了邀请按钮,麦克风和摄像头按钮不会显示) - private func changeInviteStageButtonHidden(isHidden: Bool) { - inviteStageButton.isHidden = isHidden - muteAudioButton.isHidden = !isHidden - muteVideoButton.isHidden = !isHidden - } - deinit { debugPrint("deinit \(self)") } diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/ScreenCaptureMaskView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/ScreenCaptureMaskView.swift index f9c1c75e0..d4108062f 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/ScreenCaptureMaskView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/ScreenCaptureMaskView.swift @@ -15,7 +15,7 @@ enum ScreenCaptureMaskViewFrameType { class ScreenCaptureMaskView: UIView { private var dotsTimer: Timer = Timer() - + var viewModel: TUIVideoSeatViewModel? let frameType: ScreenCaptureMaskViewFrameType let contentView: UIView = { @@ -122,18 +122,10 @@ class ScreenCaptureMaskView: UIView { } @objc func stopScreenCaptureAction(sender: UIButton) { - let alertVC = UIAlertController(title: .toastTitleText, - message: .toastMessageText, - preferredStyle: .alert) - let cancelAction = UIAlertAction(title: .toastCancelText, style: .cancel) { _ in - } - let StopAction = UIAlertAction(title: .toastStopText, style: .default) { _ in - EngineEventCenter.shared.notifyEngineEvent(event: .onUserScreenCaptureStopped, param: [:]) - EngineManager.createInstance().stopScreenCapture() - } - alertVC.addAction(cancelAction) - alertVC.addAction(StopAction) - RoomRouter.shared.presentAlert(alertVC) + RoomRouter.presentAlert(title: .toastTitleText, message: .toastMessageText, sureTitle: .toastStopText, declineTitle: .toastCancelText, sureBlock: { [weak self] in + guard let self = self else { return } + self.viewModel?.stopScreenCapture() + }, declineBlock: nil) } @objc func clickMask() { diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatCell.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatCell.swift similarity index 77% rename from iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatCell.swift rename to iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatCell.swift index 97707d537..94bfc0b73 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatCell.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatCell.swift @@ -1,5 +1,5 @@ // -// TUIVideoSeatCell.swift +// VideoSeatCell.swift // TUIVideoSeat // // Created by WesleyLei on 2021/12/16. @@ -10,42 +10,41 @@ import SnapKit import TUIRoomEngine import UIKit -class TUIVideoSeatCell: UICollectionViewCell { +class VideoSeatCell: UICollectionViewCell { var seatItem: VideoSeatItem? - - private lazy var scrollRenderView: UIScrollView = { - let scrollView = UIScrollView() - scrollView.backgroundColor = UIColor(0x17181F) - scrollView.layer.cornerRadius = 16 - scrollView.layer.masksToBounds = true - scrollView.layer.borderWidth = 2 - scrollView.layer.borderColor = UIColor.clear.cgColor - - scrollView.showsVerticalScrollIndicator = false - scrollView.showsHorizontalScrollIndicator = false - scrollView.maximumZoomScale = 3 - scrollView.minimumZoomScale = 1 - scrollView.delegate = self - return scrollView - }() - + var viewModel: TUIVideoSeatViewModel? + let renderView: UIView = { let view = UIView(frame: .zero) - view.backgroundColor = .clear + view.backgroundColor = UIColor(0x17181F) + view.layer.cornerRadius = 16 + view.layer.masksToBounds = true + view.layer.borderWidth = 2 + view.layer.borderColor = UIColor.clear.cgColor return view }() - - let userInfoView: TUIVideoSeatUserStatusView = { - let view = TUIVideoSeatUserStatusView() + + let backgroundMaskView: UIView = { + let view = UIView(frame: .zero) + view.backgroundColor = UIColor(0x17181F) + view.layer.cornerRadius = 16 + view.layer.masksToBounds = true + view.layer.borderWidth = 2 + view.layer.borderColor = UIColor.clear.cgColor return view }() - + + let userInfoView: VideoSeatUserStatusView = { + let view = VideoSeatUserStatusView() + return view + }() + let avatarImageView: UIImageView = { let imageView = UIImageView(frame: .zero) imageView.layer.masksToBounds = true return imageView }() - + private var isViewReady = false override func didMoveToWindow() { super.didMoveToWindow() @@ -57,22 +56,20 @@ class TUIVideoSeatCell: UICollectionViewCell { activateConstraints() contentView.backgroundColor = .clear } - + private func constructViewHierarchy() { - scrollRenderView.addSubview(renderView) - contentView.addSubview(scrollRenderView) + contentView.addSubview(renderView) + contentView.addSubview(backgroundMaskView) contentView.addSubview(avatarImageView) contentView.addSubview(userInfoView) } - + private func activateConstraints() { - scrollRenderView.snp.makeConstraints { make in + renderView.snp.makeConstraints { make in make.edges.equalToSuperview().inset(3) } - renderView.snp.makeConstraints { make in - make.center.equalToSuperview() - make.width.equalToSuperview() - make.height.equalToSuperview() + backgroundMaskView.snp.makeConstraints { make in + make.edges.equalToSuperview().inset(3) } userInfoView.snp.makeConstraints { make in make.height.equalTo(24) @@ -81,33 +78,29 @@ class TUIVideoSeatCell: UICollectionViewCell { make.width.lessThanOrEqualTo(self).multipliedBy(0.9) } } - + @objc private func resetVolumeView() { guard let seatItem = seatItem else { return } userInfoView.updateUserVolume(hasAudio: seatItem.hasAudioStream, volume: 0) - scrollRenderView.layer.borderColor = UIColor.clear.cgColor + renderView.layer.borderColor = UIColor.clear.cgColor + backgroundMaskView.layer.borderColor = UIColor.clear.cgColor } - + deinit { NSObject.cancelPreviousPerformRequests(withTarget: self) debugPrint("deinit \(self)") } } -extension TUIVideoSeatCell: UIScrollViewDelegate { - func viewForZooming(in scrollView: UIScrollView) -> UIView? { - return renderView - } -} - // MARK: - Public -extension TUIVideoSeatCell { +extension VideoSeatCell { func updateUI(item: VideoSeatItem) { seatItem = item let placeholder = UIImage(named: "room_default_user", in: tuiRoomKitBundle(), compatibleWith: nil) avatarImageView.sd_setImage(with: URL(string: item.avatarUrl), placeholderImage: placeholder) - avatarImageView.isHidden = item.isHasVideoStream // && (item.isPlaying || item.isSelf) + avatarImageView.isHidden = item.type == .share ? item.isHasVideoStream : item.hasVideoStream + backgroundMaskView.isHidden = item.type == .share ? item.isHasVideoStream : item.hasVideoStream userInfoView.updateUserStatus(item) resetVolumeView() DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { @@ -119,26 +112,28 @@ extension TUIVideoSeatCell { } } } - + func updateUIVolume(item: VideoSeatItem) { userInfoView.updateUserVolume(hasAudio: item.hasAudioStream, volume: item.audioVolume) if item.audioVolume > 0 && item.hasAudioStream { if item.type != .share { - scrollRenderView.layer.borderColor = UIColor(0xA5FE33).cgColor + renderView.layer.borderColor = UIColor(0xA5FE33).cgColor + backgroundMaskView.layer.borderColor = UIColor(0xA5FE33).cgColor } } else { - scrollRenderView.layer.borderColor = UIColor.clear.cgColor + renderView.layer.borderColor = UIColor.clear.cgColor + backgroundMaskView.layer.borderColor = UIColor.clear.cgColor } resetVolume() } - + func resetVolume() { NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(resetVolumeView), object: nil) perform(#selector(resetVolumeView), with: nil, afterDelay: 1) } } -class TUIVideoSeatDragCell: TUIVideoSeatCell { +class TUIVideoSeatDragCell: VideoSeatCell { typealias DragCellClickBlock = () -> Void private let clickBlock: DragCellClickBlock init(frame: CGRect, clickBlock: @escaping DragCellClickBlock) { @@ -146,11 +141,11 @@ class TUIVideoSeatDragCell: TUIVideoSeatCell { super.init(frame: frame) addGesture() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func updateSize(size: CGSize) { var frame = self.frame frame.size = size @@ -168,11 +163,11 @@ extension TUIVideoSeatDragCell { let dragGesture = UIPanGestureRecognizer(target: self, action: #selector(dragViewDidDrag(gesture:))) addGestureRecognizer(dragGesture) } - + @objc private func click() { clickBlock() } - + @objc private func dragViewDidDrag(gesture: UIPanGestureRecognizer) { guard let viewSuperview = superview else { return } // 移动状态 @@ -196,7 +191,7 @@ extension TUIVideoSeatDragCell { // 重置 panGesture gesture.setTranslation(.zero, in: viewSuperview) } - + private func adsorption(centerPoint: CGPoint) -> CGPoint { guard let viewSuperview = superview else { return centerPoint } let limitMargin = 5.0 diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatLayout.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatLayout.swift similarity index 98% rename from iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatLayout.swift rename to iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatLayout.swift index 9ede94b82..bda4dd444 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatLayout.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatLayout.swift @@ -1,5 +1,5 @@ // -// TUIVideoSeatLayout.swift +// VideoSeatLayout.swift // TUIVideoSeat // // Created by 唐佳宁 on 2023/3/16. @@ -7,11 +7,11 @@ import Foundation -protocol TUIVideoSeatLayoutDelegate: AnyObject { +protocol VideoSeatLayoutDelegate: AnyObject { func updateNumberOfPages(numberOfPages: NSInteger) } -class TUIVideoSeatLayout: UICollectionViewFlowLayout { +class VideoSeatLayout: UICollectionViewFlowLayout { private var prePageCount: NSInteger = 1 private var collectionViewHeight: CGFloat { @@ -75,7 +75,7 @@ class TUIVideoSeatLayout: UICollectionViewFlowLayout { return CGSize(width: CGFloat(prePageCount) * collectionViewWidth, height: collectionViewHeight) } - weak var delegate: TUIVideoSeatLayoutDelegate? + weak var delegate: VideoSeatLayoutDelegate? // Miniscreen布局信息 func getMiniscreenFrame(item: VideoSeatItem?) -> CGRect { @@ -91,7 +91,7 @@ class TUIVideoSeatLayout: UICollectionViewFlowLayout { // MARK: - layout -extension TUIVideoSeatLayout { +extension VideoSeatLayout { // 计算cell的位置和大小,并进行存储 private func calculateEachCellFrame() { guard let collectionViewWidth: CGFloat = collectionView?.bounds.width else { return } diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatUserStatusView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatUserStatusView.swift similarity index 94% rename from iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatUserStatusView.swift rename to iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatUserStatusView.swift index 731c966f6..b2946d33b 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatUserStatusView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatUserStatusView.swift @@ -1,5 +1,5 @@ // -// TUIVideoSeatUserStatusView.swift +// VideoSeatUserStatusView.swift // TUIVideoSeat // // Created by jack on 2023/3/6. @@ -7,7 +7,7 @@ import Foundation -class TUIVideoSeatUserStatusView: UIView { +class VideoSeatUserStatusView: UIView { private var isOwner: Bool = false private var isViewReady: Bool = false override func didMoveToWindow() { @@ -77,14 +77,14 @@ class TUIVideoSeatUserStatusView: UIView { // MARK: - Public -extension TUIVideoSeatUserStatusView { +extension VideoSeatUserStatusView { func updateUserStatus(_ item: VideoSeatItem) { if !item.userName.isEmpty { userNameLabel.text = item.userName } else { userNameLabel.text = item.userId } - isOwner = item.userRole == .roomOwner + isOwner = item.userId == EngineManager.createInstance().store.roomInfo.ownerId homeOwnerImageView.isHidden = !isOwner updateOwnerImageConstraints() updateUserVolume(hasAudio: item.hasAudioStream, volume: item.audioVolume) diff --git a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatView.swift b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatView.swift similarity index 61% rename from iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatView.swift rename to iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatView.swift index 29a5b7faf..f73df0e16 100644 --- a/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/TUIVideoSeatView.swift +++ b/iOS/TUIRoomKit/Source/View/Page/Widget/VideoSeat/VideoSeatView.swift @@ -9,16 +9,16 @@ import TUIRoomEngine import UIKit #if TXLiteAVSDK_TRTC - import TXLiteAVSDK_TRTC +import TXLiteAVSDK_TRTC #elseif TXLiteAVSDK_Professional - import TXLiteAVSDK_Professional +import TXLiteAVSDK_Professional #endif class TUIVideoSeatView: UIView { - private let CellID_Normal = "TUIVideoSeatCell_Normal" + private let CellID_Normal = "VideoSeatCell_Normal" private let viewModel: TUIVideoSeatViewModel private var isViewReady: Bool = false - + private var pageControl: UIPageControl = { let control = UIPageControl() control.currentPage = 0 @@ -27,14 +27,14 @@ class TUIVideoSeatView: UIView { control.isUserInteractionEnabled = false return control }() - - init(frame: CGRect, roomEngine: TUIRoomEngine, roomId: String) { - viewModel = TUIVideoSeatViewModel(roomEngine: roomEngine, roomId: roomId) - super.init(frame: frame) + + init() { + viewModel = TUIVideoSeatViewModel() + super.init(frame: .zero) viewModel.viewResponder = self isUserInteractionEnabled = true } - + override func didMoveToWindow() { super.didMoveToWindow() guard !isViewReady else { return } @@ -43,7 +43,7 @@ class TUIVideoSeatView: UIView { bindInteraction() isViewReady = true } - + override func layoutSubviews() { super.layoutSubviews() if let item = moveMiniscreen.seatItem,!moveMiniscreen.isHidden { @@ -56,21 +56,21 @@ class TUIVideoSeatView: UIView { CGPoint(x: CGFloat(pageControl.currentPage) * attendeeCollectionView.frame.size.width, y: attendeeCollectionView.contentOffset.y), animated: false) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - - lazy var videoSeatLayout: TUIVideoSeatLayout = { - let layout = TUIVideoSeatLayout(viewModel: viewModel) + + lazy var videoSeatLayout: VideoSeatLayout = { + let layout = VideoSeatLayout(viewModel: viewModel) layout.delegate = self return layout }() - + lazy var attendeeCollectionView: UICollectionView = { let collection = UICollectionView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height), collectionViewLayout: - videoSeatLayout) - collection.register(TUIVideoSeatCell.self, forCellWithReuseIdentifier: CellID_Normal) + videoSeatLayout) + collection.register(VideoSeatCell.self, forCellWithReuseIdentifier: CellID_Normal) collection.isPagingEnabled = true collection.showsVerticalScrollIndicator = false collection.showsHorizontalScrollIndicator = false @@ -91,7 +91,7 @@ class TUIVideoSeatView: UIView { collection.delegate = self return collection }() - + lazy var moveMiniscreen: TUIVideoSeatDragCell = { let cell = TUIVideoSeatDragCell(frame: videoSeatLayout.getMiniscreenFrame(item: nil)) { [weak self] in guard let self = self else { return } @@ -104,18 +104,29 @@ class TUIVideoSeatView: UIView { lazy var screenCaptureMaskView: ScreenCaptureMaskView = { let view = ScreenCaptureMaskView(frameType: .fullScreen) + view.viewModel = self.viewModel view.isHidden = true return view }() - + + let placeholderView: UIView = { + let view = UIView(frame: .zero) + view.isHidden = true + return view + }() + func constructViewHierarchy() { backgroundColor = .clear + addSubview(placeholderView) addSubview(attendeeCollectionView) addSubview(pageControl) addSubview(screenCaptureMaskView) } - + func activateConstraints() { + placeholderView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } attendeeCollectionView.snp.makeConstraints { make in make.edges.equalToSuperview() } @@ -134,30 +145,30 @@ class TUIVideoSeatView: UIView { screenCaptureMaskView.isHidden = !EngineManager.createInstance().store.currentUser.hasScreenStream addGesture() } - + private func addGesture() { let tap = UITapGestureRecognizer(target: self, action: #selector(clickVideoSeat)) addGestureRecognizer(tap) } - + @objc private func clickVideoSeat() { viewModel.clickVideoSeat() } - + func updatePageControl() { let offsetYu = Int(attendeeCollectionView.contentOffset.x) % Int(attendeeCollectionView.mm_w) let offsetMuti = CGFloat(offsetYu) / attendeeCollectionView.mm_w pageControl.currentPage = (offsetMuti > 0.5 ? 1 : 0) + (Int(attendeeCollectionView.contentOffset.x) / Int(attendeeCollectionView.mm_w)) - + if let seatItem = moveMiniscreen.seatItem, seatItem.hasVideoStream { if pageControl.currentPage == 0 && !moveMiniscreen.isHidden { viewModel.startPlayVideo(item: seatItem, renderView: moveMiniscreen.renderView) } else { - viewModel.startPlayVideo(item: seatItem, renderView: getSeatVideoRenderView(seatItem)) + viewModel.startPlayVideo(item: seatItem, renderView: getVideoVisibleCell(seatItem)?.renderView) } } } - + deinit { debugPrint("deinit \(self)") } @@ -166,94 +177,78 @@ class TUIVideoSeatView: UIView { // MARK: - TUIVideoSeatViewResponder extension TUIVideoSeatView: TUIVideoSeatViewResponder { - private func freshCollectionView(block: () -> Void) { - CATransaction.begin() - CATransaction.setDisableActions(true) - CATransaction.setCompletionBlock { [weak self] in - guard let self = self else { return } - self.viewModel.clearSubscribeVideoStream(items: self.getNoLoadHasVideoItems()) - } - block() - CATransaction.commit() - } - func reloadData() { - freshCollectionView { - self.attendeeCollectionView.reloadData() - } + self.attendeeCollectionView.reloadData() } - + func insertItems(at indexPaths: [IndexPath]) { - freshCollectionView { - self.attendeeCollectionView.performBatchUpdates { - //如果当前的cell数量已经和数据源相同,不再进行插入操作而是直接reloadData - if self.attendeeCollectionView.numberOfItems(inSection: 0) == viewModel.listSeatItem.count { - attendeeCollectionView.reloadData() - } else { - self.attendeeCollectionView.insertItems(at: indexPaths) - } + self.attendeeCollectionView.performBatchUpdates { [weak self] in + guard let self = self else { return } + //如果当前的cell数量已经和数据源相同,不再进行插入操作而是直接reloadData + if self.attendeeCollectionView.numberOfItems(inSection: 0) == self.viewModel.listSeatItem.count { + self.attendeeCollectionView.reloadData() + } else { + self.attendeeCollectionView.insertItems(at: indexPaths) } } } func deleteItems(at indexPaths: [IndexPath]) { - freshCollectionView { - self.attendeeCollectionView.performBatchUpdates { - //如果当前cell的数量已经和数据源相同,不再进行删除操作,而是直接reloadData - if attendeeCollectionView.numberOfItems(inSection: 0) == viewModel.listSeatItem.count { - attendeeCollectionView.reloadData() - } else { - var resultArray: [IndexPath] = [] - let numberOfSections = self.attendeeCollectionView.numberOfSections - for indexPath in indexPaths { - let section = indexPath.section - let item = indexPath.item - guard section < numberOfSections && item < self.attendeeCollectionView.numberOfItems(inSection: section) - else { continue } // indexPath越界,不执行删除操作 - resultArray.append(indexPath) - } - self.attendeeCollectionView.deleteItems(at: resultArray) + self.attendeeCollectionView.performBatchUpdates { [weak self] in + guard let self = self else { return } + //如果当前cell的数量已经和数据源相同,不再进行删除操作,而是直接reloadData + if self.attendeeCollectionView.numberOfItems(inSection: 0) == self.viewModel.listSeatItem.count { + self.attendeeCollectionView.reloadData() + } else { + var resultArray: [IndexPath] = [] + let numberOfSections = self.attendeeCollectionView.numberOfSections + for indexPath in indexPaths { + let section = indexPath.section + let item = indexPath.item + guard section < numberOfSections && item < self.attendeeCollectionView.numberOfItems(inSection: section) + else { continue } // indexPath越界,不执行删除操作 + resultArray.append(indexPath) } + self.attendeeCollectionView.deleteItems(at: resultArray) } } } - - func reloadItems(at indexPaths: [IndexPath]) { - freshCollectionView { - self.attendeeCollectionView.performBatchUpdates { - self.attendeeCollectionView.reloadItems(at: indexPaths) - } - } + + func deleteVideoSeatCell(item: VideoSeatItem) { + guard let cell = getVideoVisibleCell(item) else { return } + cell.removeFromSuperview() } func updateSeatItem(_ item: VideoSeatItem) { if let seatItem = moveMiniscreen.seatItem, seatItem.userId == item.userId { moveMiniscreen.updateUI(item: seatItem) } - guard let cell = item.boundCell else { return } - if item == cell.seatItem { - cell.updateUI(item: item) - if item.hasVideoStream { - viewModel.startPlayVideo(item: item, renderView: cell.renderView) - } + guard let cell = getVideoVisibleCell(item) else { return } + cell.updateUI(item: item) + if item.hasVideoStream { + viewModel.startPlayVideo(item: item, renderView: cell.renderView) + } else { + viewModel.stopPlayVideo(item: item) } } - + func updateSeatVolume(_ item: VideoSeatItem) { - guard let cell = item.boundCell else { return } - if cell.seatItem == item { - cell.updateUIVolume(item: item) - } + guard let cell = getVideoVisibleCell(item) else { return } + cell.updateUIVolume(item: item) } - - func getSeatVideoRenderView(_ item: VideoSeatItem) -> UIView? { - guard let cell = item.boundCell else { return nil } - if cell.seatItem == item { - return cell.renderView - } - return nil + + func getVideoVisibleCell(_ item: VideoSeatItem) -> VideoSeatCell? { + let cellArray = attendeeCollectionView.visibleCells + guard let cell = cellArray.first(where: { cell in + if let seatCell = cell as? VideoSeatCell, seatCell.seatItem == item { + return true + } else { + return false + } + }) as? VideoSeatCell else { return nil } + return cell } - + func updateMiniscreen(_ item: VideoSeatItem?) { guard let item = item else { moveMiniscreen.isHidden = true @@ -262,27 +257,19 @@ extension TUIVideoSeatView: TUIVideoSeatViewResponder { if attendeeCollectionView.contentOffset.x > 0 { return } - - if let lastSeatItem = moveMiniscreen.seatItem, lastSeatItem.isHasVideoStream { - if let renderView = getSeatVideoRenderView(lastSeatItem) { - viewModel.startPlayVideo(item: lastSeatItem, renderView: renderView) - } else { - viewModel.unboundCell(item: lastSeatItem) - } - } - moveMiniscreen.updateSize(size: videoSeatLayout.getMiniscreenFrame(item: item).size) moveMiniscreen.isHidden = false + bringSubviewToFront(moveMiniscreen) moveMiniscreen.updateUI(item: item) if item.isHasVideoStream { viewModel.startPlayVideo(item: item, renderView: moveMiniscreen.renderView) } } - + func updateMiniscreenVolume(_ item: VideoSeatItem) { moveMiniscreen.updateUIVolume(item: item) } - + func getMoveMiniscreen() -> TUIVideoSeatDragCell { return moveMiniscreen } @@ -293,29 +280,6 @@ extension TUIVideoSeatView: TUIVideoSeatViewResponder { screenCaptureMaskView.superview?.bringSubviewToFront(screenCaptureMaskView) } } - - private func getNoLoadHasVideoItems() -> [VideoSeatItem] { - var hasVideoStreamItems = viewModel.listSeatItem.filter({ $0.isHasVideoStream }) - let visibleCells = Array(attendeeCollectionView.visibleCells) - for cell in visibleCells { - if let seatCell = cell as? TUIVideoSeatCell, - let seatItem = seatCell.seatItem, - let seatItemIndex = hasVideoStreamItems.firstIndex(where: { $0 == seatItem }) { - hasVideoStreamItems.remove(at: seatItemIndex) - } - } - if let seatItem = moveMiniscreen.seatItem, - let seatItemIndex = hasVideoStreamItems.firstIndex(where: { - if $0.userId == seatItem.userId && $0.type != .share { - return true - } else { - return false - } - }) { - hasVideoStreamItems.remove(at: seatItemIndex) - } - return hasVideoStreamItems - } } // MARK: - UICollectionViewDelegateFlowLayout @@ -323,16 +287,18 @@ extension TUIVideoSeatView: TUIVideoSeatViewResponder { extension TUIVideoSeatView: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { guard let seatItem = viewModel.listSeatItem[safe: indexPath.item] else { return } - guard let seatCell = cell as? TUIVideoSeatCell else { return } - if let lastSeatItem = seatCell.seatItem, lastSeatItem != seatItem, lastSeatItem.boundCell == cell { - lastSeatItem.boundCell = nil - viewModel.stopPlayVideo(item: lastSeatItem) - } - seatItem.boundCell = seatCell - seatCell.updateUI(item: seatItem) + guard let seatCell = cell as? VideoSeatCell else { return } if seatItem.isHasVideoStream { viewModel.startPlayVideo(item: seatItem, renderView: seatCell.renderView) + } else { + viewModel.stopPlayVideo(item: seatItem) } + seatCell.updateUI(item: seatItem) + } + + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard let seatItem = viewModel.listSeatItem[safe: indexPath.item] else { return } + viewModel.stopPlayVideo(item: seatItem) } } @@ -345,10 +311,9 @@ extension TUIVideoSeatView: UIScrollViewDelegate { } else { attendeeCollectionView.addSubview(moveMiniscreen) } - viewModel.clearSubscribeVideoStream(items: getNoLoadHasVideoItems()) updatePageControl() } - + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { attendeeCollectionView.addSubview(moveMiniscreen) } @@ -360,38 +325,27 @@ extension TUIVideoSeatView: UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + debugPrint("**********,collectionView,numberOfItemsInSection:\(viewModel.listSeatItem.count)") return viewModel.listSeatItem.count } - + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell( withReuseIdentifier: CellID_Normal, - for: indexPath) as! TUIVideoSeatCell + for: indexPath) as! VideoSeatCell if indexPath.item >= viewModel.listSeatItem.count { return cell } - - // 解绑cell和item的绑定 - let seatItem = viewModel.listSeatItem[indexPath.item] - if let lastSeatItem = cell.seatItem, lastSeatItem != seatItem, lastSeatItem.boundCell == cell { - lastSeatItem.boundCell = nil - viewModel.stopPlayVideo(item: lastSeatItem) - } - - seatItem.boundCell = cell - cell.updateUI(item: seatItem) - if seatItem.isHasVideoStream { - viewModel.startPlayVideo(item: seatItem, renderView: cell.renderView) - } + cell.viewModel = self.viewModel return cell } } // MARK: - UICollectionViewDataSource -extension TUIVideoSeatView: TUIVideoSeatLayoutDelegate { +extension TUIVideoSeatView: VideoSeatLayoutDelegate { func updateNumberOfPages(numberOfPages: NSInteger) { pageControl.numberOfPages = numberOfPages } diff --git a/iOS/TUIRoomKit/Source/ViewModel/BottomViewModel.swift b/iOS/TUIRoomKit/Source/ViewModel/BottomViewModel.swift index ed97bef18..94dd9e3ab 100644 --- a/iOS/TUIRoomKit/Source/ViewModel/BottomViewModel.swift +++ b/iOS/TUIRoomKit/Source/ViewModel/BottomViewModel.swift @@ -14,6 +14,7 @@ protocol BottomViewModelResponder: AnyObject { func updateStackView(item: ButtonItemData, index: Int) func makeToast(text: String) func updataBottomView(isUp:Bool) + func showStopShareScreenAlert(sureBlock: (()->())?) } class BottomViewModel: NSObject { @@ -346,20 +347,10 @@ class BottomViewModel: NSObject { BroadcastLauncher.launch() } } else { - let alertVC = UIAlertController(title: .toastTitleText, - message: .toastMessageText, - preferredStyle: .alert) - let cancelAction = UIAlertAction(title: .toastCancelText, style: .cancel) { _ in - } - let StopAction = UIAlertAction(title: .toastStopText, style: .default) { [weak self] _ in - guard let self = self else { - return - } + viewResponder?.showStopShareScreenAlert(sureBlock: { [weak self] in + guard let self = self else { return } self.engineManager.stopScreenCapture() - } - alertVC.addAction(cancelAction) - alertVC.addAction(StopAction) - RoomRouter.shared.presentAlert(alertVC) + }) } } else { viewResponder?.makeToast(text: .versionLowToastText) @@ -640,18 +631,6 @@ private extension String { static var dropText: String { localized("TUIRoom.drop") } - static var toastTitleText: String { - localized("TUIRoom.toast.shareScreen.title") - } - static var toastMessageText: String { - localized("TUIRoom.toast.shareScreen.message") - } - static var toastCancelText: String { - localized("TUIRoom.toast.shareScreen.cancel") - } - static var toastStopText: String { - localized("TUIRoom.toast.shareScreen.stop") - } static var putHandsDownText: String { localized("TUIRoom.put.hands.down") } diff --git a/iOS/TUIRoomKit/Source/ViewModel/ExitRoomViewModel.swift b/iOS/TUIRoomKit/Source/ViewModel/ExitRoomViewModel.swift index 868faea1a..50261cf45 100644 --- a/iOS/TUIRoomKit/Source/ViewModel/ExitRoomViewModel.swift +++ b/iOS/TUIRoomKit/Source/ViewModel/ExitRoomViewModel.swift @@ -8,11 +8,16 @@ import Foundation import TUIRoomEngine +protocol ExitRoomViewModelResponder: AnyObject { + func makeToast(message: String) +} + class ExitRoomViewModel { var engineManager: EngineManager var currentUser: UserEntity var isRoomOwner: Bool var isOnlyOneUserInRoom: Bool + weak var viewResponder: ExitRoomViewModelResponder? init() { engineManager = EngineManager.createInstance() @@ -21,7 +26,7 @@ class ExitRoomViewModel { isOnlyOneUserInRoom = engineManager.store.attendeeList.count == 1 } - func isShowLeaveRoomButton() -> Bool { + func isShownLeaveRoomButton() -> Bool { if currentUser.userId == engineManager.store.roomInfo.ownerId { return engineManager.store.attendeeList.count > 1 } else { @@ -29,6 +34,10 @@ class ExitRoomViewModel { } } + func isShownExitRoomButton() -> Bool { + return currentUser.userId == engineManager.store.roomInfo.ownerId + } + func leaveRoomAction() { if isRoomOwner && !isOnlyOneUserInRoom { RoomRouter.shared.presentPopUpViewController(viewType: .transferMasterViewType, height: 720.scale375Height()) @@ -39,12 +48,22 @@ class ExitRoomViewModel { func exitRoom() { if isRoomOwner { - engineManager.destroyRoom(onSuccess: nil, onError: nil) + engineManager.destroyRoom { + RoomRouter.shared.dismissAllRoomPopupViewController() + RoomRouter.shared.popToRoomEntranceViewController() + } onError: { [weak self] code, message in + guard let self = self else { return } + self.viewResponder?.makeToast(message: message) + } } else { - engineManager.exitRoom(onSuccess: nil, onError: nil) + engineManager.exitRoom { + RoomRouter.shared.dismissAllRoomPopupViewController() + RoomRouter.shared.popToRoomEntranceViewController() + } onError: { [weak self] code, message in + guard let self = self else { return } + self.viewResponder?.makeToast(message: message) + } } - RoomRouter.shared.dismissAllRoomPopupViewController() - RoomRouter.shared.popToRoomEntranceViewController() } deinit { diff --git a/iOS/TUIRoomKit/Source/ViewModel/RoomMainViewModel.swift b/iOS/TUIRoomKit/Source/ViewModel/RoomMainViewModel.swift index bd2198951..d913812ee 100644 --- a/iOS/TUIRoomKit/Source/ViewModel/RoomMainViewModel.swift +++ b/iOS/TUIRoomKit/Source/ViewModel/RoomMainViewModel.swift @@ -21,7 +21,7 @@ protocol RoomMainViewResponder: AnyObject { func setToolBarDelayHidden(isDelay: Bool) func updateMuteAudioButton(isSelected: Bool) func showExitRoomView() - func showAlert(title: String?, message: String?, sureBlock: (()->())?, declineBlock: (() -> ())?) + func showAlert(title: String?, message: String?, sureTitle:String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?) } class RoomMainViewModel: NSObject { @@ -116,7 +116,7 @@ extension RoomMainViewModel: RoomEngineEventResponder { func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) { if name == .onRoomDismissed { engineManager.destroyEngineManager() - viewResponder?.showAlert(title: .destroyAlertText, message: nil, sureBlock: { [weak self] in + viewResponder?.showAlert(title: .destroyAlertText, message: nil, sureTitle: .alertOkText, declineTitle: nil, sureBlock: { [weak self] in guard let self = self else { return } self.roomRouter.dismissAllRoomPopupViewController() self.roomRouter.popToRoomEntranceViewController() @@ -125,7 +125,7 @@ extension RoomMainViewModel: RoomEngineEventResponder { if name == .onKickedOutOfRoom { engineManager.destroyEngineManager() - viewResponder?.showAlert(title: .kickOffTitleText, message: nil, sureBlock: { [weak self] in + viewResponder?.showAlert(title: .kickOffTitleText, message: nil, sureTitle: .alertOkText, declineTitle: nil , sureBlock: { [weak self] in guard let self = self else { return } self.roomRouter.dismissAllRoomPopupViewController() self.roomRouter.popToRoomEntranceViewController() @@ -137,7 +137,7 @@ extension RoomMainViewModel: RoomEngineEventResponder { switch request.requestAction { case .openRemoteCamera: guard !isShownOpenCameraInviteAlert else { return } - viewResponder?.showAlert(title: .inviteTurnOnVideoText, message: nil, sureBlock: { [weak self] in + viewResponder?.showAlert(title: .inviteTurnOnVideoText, message: nil, sureTitle: .agreeText, declineTitle: .declineText, sureBlock: { [weak self] in guard let self = self else { return } self.isShownOpenCameraInviteAlert = false // FIXME: - 打开摄像头前需要先设置一个view @@ -160,7 +160,7 @@ extension RoomMainViewModel: RoomEngineEventResponder { isShownOpenCameraInviteAlert = true case .openRemoteMicrophone: guard !isShownOpenMicrophoneInviteAlert else { return } - viewResponder?.showAlert(title: .inviteTurnOnAudioText, message: nil, sureBlock: { [weak self] in + viewResponder?.showAlert(title: .inviteTurnOnAudioText, message: nil, sureTitle: .agreeText, declineTitle: .declineText, sureBlock: { [weak self] in guard let self = self else { return } self.isShownOpenMicrophoneInviteAlert = false if RoomCommon.checkAuthorMicStatusIsDenied() { @@ -185,7 +185,7 @@ extension RoomMainViewModel: RoomEngineEventResponder { switch roomInfo.speechMode { case .applySpeakAfterTakingSeat: guard !isShownTakeSeatInviteAlert else { return } - viewResponder?.showAlert(title: .inviteSpeakOnStageTitle, message: .inviteSpeakOnStageMessage, sureBlock: { [weak self] in + viewResponder?.showAlert(title: .inviteSpeakOnStageTitle, message: .inviteSpeakOnStageMessage, sureTitle: .agreeSeatText, declineTitle: .declineText, sureBlock: { [weak self] in guard let self = self else { return } self.isShownTakeSeatInviteAlert = false self.respondUserOnSeat(isAgree: true, requestId: request.requestId) @@ -219,8 +219,7 @@ extension RoomMainViewModel: RoomMainViewFactory { } func makeVideoSeatView() -> UIView { - let videoSeatView = TUIVideoSeatView(frame: UIScreen.main.bounds, roomEngine: engineManager.roomEngine, - roomId: roomInfo.roomId) + let videoSeatView = TUIVideoSeatView() videoSeatView.backgroundColor = UIColor(0x0F1014) return videoSeatView } @@ -274,7 +273,7 @@ extension RoomMainViewModel: RoomKitUIEventResponder { case .TUIRoomKitService_CurrentUserRoleChanged: guard let userRole = info?["userRole"] as? TUIRole else { return } guard userRole == .roomOwner else { return } - viewResponder?.showAlert(title: .haveBecomeMasterText, message: nil, sureBlock: nil, declineBlock: nil) + viewResponder?.showAlert(title: .haveBecomeMasterText, message: nil,sureTitle: .alertOkText, declineTitle: nil, sureBlock: nil, declineBlock: nil) case .TUIRoomKitService_CurrentUserMuteMessage: guard let isMute = info?["isMute"] as? Bool else { return } viewResponder?.makeToast(text: isMute ? .messageTurnedOffText : .messageTurnedOnText) @@ -329,4 +328,16 @@ private extension String { static var kickedOffLineText: String { localized("TUIRoom.kicked.off.line") } + static var alertOkText: String { + localized("TUIRoom.ok") + } + static var declineText: String { + localized("TUIRoom.decline") + } + static var agreeText: String { + localized("TUIRoom.agree") + } + static var agreeSeatText: String { + localized("TUIRoom.agree.seat") + } } diff --git a/iOS/TUIRoomKit/Source/ViewModel/TUIVideoSeatViewModel.swift b/iOS/TUIRoomKit/Source/ViewModel/TUIVideoSeatViewModel.swift index 4d90485e4..0c2b85570 100644 --- a/iOS/TUIRoomKit/Source/ViewModel/TUIVideoSeatViewModel.swift +++ b/iOS/TUIRoomKit/Source/ViewModel/TUIVideoSeatViewModel.swift @@ -9,23 +9,22 @@ import Foundation import TUIRoomEngine #if TXLiteAVSDK_TRTC - import TXLiteAVSDK_TRTC +import TXLiteAVSDK_TRTC #elseif TXLiteAVSDK_Professional - import TXLiteAVSDK_Professional +import TXLiteAVSDK_Professional #endif protocol TUIVideoSeatViewResponder: AnyObject { func reloadData() func insertItems(at indexPaths: [IndexPath]) func deleteItems(at indexPaths: [IndexPath]) - func reloadItems(at indexPaths: [IndexPath]) - - func getSeatVideoRenderView(_ item: VideoSeatItem) -> UIView? + + func getVideoVisibleCell(_ item: VideoSeatItem) -> VideoSeatCell? func getMoveMiniscreen() -> TUIVideoSeatDragCell - + func updateMiniscreen(_ item: VideoSeatItem?) func updateMiniscreenVolume(_ item: VideoSeatItem) - + func updateSeatItem(_ item: VideoSeatItem) func updateSeatVolume(_ item: VideoSeatItem) @@ -50,9 +49,7 @@ class TUIVideoSeatViewModel: NSObject { private var speakerItem: VideoSeatItem? // 小画面item private var smallItem: VideoSeatItem? - - private var userNoExistMap: [String: TUIUserInfo] = [:] - + private var isSwitchPosition: Bool = false private var speakerUpdateTimer: Int = 0 //演讲者更新的时间戳 @@ -64,40 +61,77 @@ class TUIVideoSeatViewModel: NSObject { return .cameraStream } } - + var listSeatItem: [VideoSeatItem] = [] - + private var isHasVideoStream: Bool { return videoSeatItems.firstIndex(where: { $0.isHasVideoStream }) != nil } - + private var isHasScreenStream: Bool { return videoSeatItems.firstIndex(where: { $0.hasScreenStream }) != nil } - - private var isNeedReloadStream: Bool { - return videoSeatItems.firstIndex(where: { $0.boundCell != nil }) == nil - } - + weak var viewResponder: TUIVideoSeatViewResponder? var videoSeatViewType: TUIVideoSeatViewType = .unknown - var roomInfo: TUIRoomInfo + var engineManager: EngineManager { + EngineManager.createInstance() + } + var store: RoomStore { + engineManager.store + } + var roomInfo: TUIRoomInfo { + store.roomInfo + } var currentUserId: String { - return TUIRoomEngine.getSelfInfo().userId + store.currentUser.userId } - - private weak var roomEngine: TUIRoomEngine? - init(roomEngine: TUIRoomEngine, roomId: String) { - roomInfo = TUIRoomInfo() - roomInfo.roomId = roomId + + override init() { super.init() - self.roomEngine = roomEngine - initRoomInfo() - roomEngine.addObserver(self) + initVideoSeatItems() + subscribeUIEvent() } - + + private func initVideoSeatItems() { + videoSeatItems = [] + let videoItems = store.roomInfo.speechMode == .applySpeakAfterTakingSeat ? store.seatList : store.attendeeList + guard videoItems.count > 0 else { return } + videoItems.forEach { userInfo in + let userItem = VideoSeatItem(userInfo: userInfo) + videoSeatItems.append(userItem) + } + sortSeatItems() + reloadSeatItems() + } + + private func subscribeUIEvent() { + EngineEventCenter.shared.subscribeUIEvent(key: .TUIRoomKitService_RenewVideoSeatView, responder: self) + EngineEventCenter.shared.subscribeEngine(event: .onUserAudioStateChanged, observer: self) + EngineEventCenter.shared.subscribeEngine(event: .onUserVideoStateChanged, observer: self) + EngineEventCenter.shared.subscribeEngine(event: .onUserVoiceVolumeChanged, observer: self) + EngineEventCenter.shared.subscribeEngine(event: .onUserScreenCaptureStopped, observer: self) + EngineEventCenter.shared.subscribeEngine(event: .onRemoteUserEnterRoom, observer: self) + EngineEventCenter.shared.subscribeEngine(event: .onRemoteUserLeaveRoom, observer: self) + EngineEventCenter.shared.subscribeEngine(event: .onUserRoleChanged, observer: self) + EngineEventCenter.shared.subscribeEngine(event: .onSeatListChanged, observer: self) + } + + private func unsubscribeUIEvent() { + EngineEventCenter.shared.unsubscribeUIEvent(key: .TUIRoomKitService_RenewVideoSeatView, responder: self) + EngineEventCenter.shared.unsubscribeEngine(event: .onUserAudioStateChanged, observer: self) + EngineEventCenter.shared.unsubscribeEngine(event: .onUserVideoStateChanged, observer: self) + EngineEventCenter.shared.unsubscribeEngine(event: .onUserVoiceVolumeChanged, observer: self) + EngineEventCenter.shared.unsubscribeEngine(event: .onUserScreenCaptureStopped, observer: self) + EngineEventCenter.shared.unsubscribeEngine(event: .onRemoteUserEnterRoom, observer: self) + EngineEventCenter.shared.unsubscribeEngine(event: .onRemoteUserLeaveRoom, observer: self) + EngineEventCenter.shared.unsubscribeEngine(event: .onUserRoleChanged, observer: self) + EngineEventCenter.shared.unsubscribeEngine(event: .onSeatListChanged, observer: self) + } + deinit { - roomEngine?.removeObserver(self) + unsubscribeUIEvent() + debugPrint("deinit:\(self)") } } @@ -107,14 +141,15 @@ extension TUIVideoSeatViewModel { func isHomeOwner(_ userId: String) -> Bool { return roomInfo.ownerId == userId } - + func switchPosition() { if videoSeatViewType == .largeSmallWindowType { isSwitchPosition = !isSwitchPosition reloadSeatItems() + viewResponder?.reloadData() } } - + func updateSpeakerPlayVideoState(currentPageIndex: Int) { // currentPageIndex : 0,1,2,3 if videoSeatViewType != .speechType { @@ -123,64 +158,46 @@ extension TUIVideoSeatViewModel { if currentPageIndex == 0 { viewResponder?.updateMiniscreen(speakerItem) } else if let item = videoSeatItems.first(where: { $0.userId == speakerItem?.userId }), - let renderView = viewResponder?.getSeatVideoRenderView(item) { + let renderView = viewResponder?.getVideoVisibleCell(item)?.renderView { startPlayVideo(item: item, renderView: renderView) } } - + func startPlayVideo(item: VideoSeatItem, renderView: UIView?) { guard let renderView = renderView else { return } if item.userId == currentUserId { - roomEngine?.setLocalVideoView(streamType: item.streamType, view: renderView) + engineManager.setLocalVideoView(streamType: item.streamType, view: renderView) } else { item.updateStreamType(streamType: itemStreamType) - if item.isPlaying { - roomEngine?.setRemoteVideoView(userId: item.userId, streamType: item.streamType, view: renderView) - return - } - roomEngine?.setRemoteVideoView(userId: item.userId, streamType: item.streamType, view: renderView) - roomEngine?.startPlayRemoteVideo(userId: item.userId, streamType: item.streamType, onPlaying: { [weak self] _ in + engineManager.setRemoteVideoView(userId: item.userId, streamType: item.streamType, view: renderView) + engineManager.startPlayRemoteVideo(userId: item.userId, streamType: item.streamType, onSuccess: { [weak self] in guard let self = self else { return } - item.isPlaying = true - if let seatCell = item.boundCell, item == seatCell.seatItem { - seatCell.updateUI(item: item) - } if item == self.viewResponder?.getMoveMiniscreen().seatItem { self.viewResponder?.getMoveMiniscreen().updateUI(item: item) } - }, onLoading: { _ in - }, onError: { _, _, _ in }) } + guard let seatCell = viewResponder?.getVideoVisibleCell(item) else { return } + seatCell.updateUI(item: item) } - + func stopPlayVideo(item: VideoSeatItem) { unboundCell(item: item) if item.userId != currentUserId { - item.isPlaying = false - roomEngine?.stopPlayRemoteVideo(userId: item.userId, streamType: item.streamType) + engineManager.stopPlayRemoteVideo(userId: item.userId, streamType: item.streamType) } + guard let seatCell = viewResponder?.getVideoVisibleCell(item) else { return } + seatCell.updateUI(item: item) } - - func unboundCell(item: VideoSeatItem) { + + private func unboundCell(item: VideoSeatItem) { if item.userId == currentUserId { - roomEngine?.setLocalVideoView(streamType: item.streamType, view: nil) + engineManager.setLocalVideoView(streamType: item.streamType, view: nil) } else { if item.streamType != .screenStream { - roomEngine?.setRemoteVideoView(userId: item.userId, streamType: .cameraStreamLow, view: nil) - } - roomEngine?.setRemoteVideoView(userId: item.userId, streamType: item.streamType, view: nil) - } - } - - func clearSubscribeVideoStream(items: [VideoSeatItem]) { - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - for item in items { - if self.viewResponder?.getSeatVideoRenderView(item) == nil { - self.stopPlayVideo(item: item) - } + engineManager.setRemoteVideoView(userId: item.userId, streamType: .cameraStreamLow, view: nil) } + engineManager.setRemoteVideoView(userId: item.userId, streamType: item.streamType, view: nil) } } @@ -189,133 +206,86 @@ extension TUIVideoSeatViewModel { guard RoomRouter.shared.hasChatWindow() else { return } EngineEventCenter.shared.notifyUIEvent(key: .TUIRoomKitService_HiddenChatWindow, param: [:]) } + + func stopScreenCapture() { + EngineEventCenter.shared.notifyEngineEvent(event: .onUserScreenCaptureStopped, param: [:]) + engineManager.stopScreenCapture() + } } // MARK: - Private extension TUIVideoSeatViewModel { - private func asyncUserInfo(_ seatItem: VideoSeatItem) { - roomEngine?.getUserInfo(seatItem.userId, onSuccess: { [weak self] userInfo in - guard let self = self, let userInfo = userInfo else { return } - seatItem.updateUserInfo(userInfo) - self.viewResponder?.updateSeatItem(seatItem) - self.reloadSeatItems() - }, onError: { _, msg in - debugPrint("asyncUserInfo error: \(msg)") - }) - } - - private func initRoomInfo() { - roomEngine?.fetchRoomInfo { [weak self] roomInfo in - guard let self = self, let roomInfo = roomInfo else { return } - self.roomInfo = roomInfo - switch self.roomInfo.speechMode { - case .freeToSpeak: - let localSeatList: [VideoSeatItem] = [] - self.initUserList(nextSequence: 0, localSeatList: localSeatList) - case .applySpeakAfterTakingSeat: - self.initSeatList() - default: break - } - } onError: { code, message in - debugPrint("getRoomInfo:code:\(code),message:\(message)") - } - } - - private func initSeatList() { - roomEngine?.getSeatList { [weak self] seatList in - guard let self = self else { return } - var localSeatList = [VideoSeatItem]() - for seatInfo in seatList { - let seatItem = VideoSeatItem(userId: seatInfo.userId ?? "") - self.asyncUserInfo(seatItem) - if seatItem.userId == self.roomInfo.ownerId { - seatItem.userRole = .roomOwner - } - if let userInfo = self.userNoExistMap[seatItem.userId] { - seatItem.hasVideoStream = userInfo.hasVideoStream - seatItem.hasScreenStream = userInfo.hasScreenStream - seatItem.hasAudioStream = userInfo.hasAudioStream - } - localSeatList.append(seatItem) - } - self.videoSeatItems = localSeatList - self.reloadSeatItems() - } onError: { code, message in - debugPrint("getSeatList:code:\(code),message:\(message)") - } - } - - private func initUserList(nextSequence: Int, localSeatList: [VideoSeatItem]) { - roomEngine?.getUserList(nextSequence: nextSequence) { [weak self] list, nextSequence in - guard let self = self else { return } - var localSeatList = localSeatList - list.forEach { userInfo in - let userItem = VideoSeatItem(userInfo: userInfo) - localSeatList.append(userItem) - } - if nextSequence != 0 { - self.initUserList(nextSequence: nextSequence, localSeatList: localSeatList) - } - self.videoSeatItems = localSeatList - self.reloadSeatItems() - } onError: { code, message in - debugPrint("getUserList:code:\(code),message:\(message)") - } - } - - private func addSeatInfo(_ seatInfo: TUISeatInfo) { - if let userId = seatInfo.userId, let _ = getSeatItem(userId) { - return - } - let seatItem = VideoSeatItem(userId: seatInfo.userId ?? "") - asyncUserInfo(seatItem) + private func addUserInfo(_ userId: String) { + guard let userInfo = store.attendeeList.first(where: { $0.userId == userId }) else { return } + let seatItem = VideoSeatItem(userInfo: userInfo) videoSeatItems.append(seatItem) reloadSeatItems() } - - private func addUserInfo(_ userInfo: TUIUserInfo) { - if let userItem = getSeatItem(userInfo.userId) { - userItem.updateUserInfo(userInfo) - viewResponder?.updateSeatItem(userItem) - } else { - let seatItem = VideoSeatItem(userInfo: userInfo) - videoSeatItems.append(seatItem) - reloadSeatItems() - } - } - + private func removeSeatItem(_ userId: String) { if shareItem?.userId == userId, let seatItem = shareItem { stopPlayVideo(item: seatItem) } - if speakerItem?.userId == userId, let seatItem = speakerItem { stopPlayVideo(item: seatItem) } - guard let seatItemIndex = videoSeatItems.firstIndex(where: { $0.userId == userId }) else { return } - let seatItem = videoSeatItems.remove(at: seatItemIndex) - stopPlayVideo(item: seatItem) - reloadSeatItems() + if let seatItem = videoSeatItems.first(where: { $0.userId == userId }) { + stopPlayVideo(item: seatItem) + } + videoSeatItems.removeAll(where: { $0.userId == userId }) + if let index = listSeatItem.firstIndex(where: { $0.userId == userId && $0.type != .share }), + let item = listSeatItem.first(where: { $0.userId == userId && $0.type != .share }) { + listSeatItem.remove(at: index) + if ((viewResponder?.getVideoVisibleCell(item)) != nil) { + viewResponder?.reloadData() + } else { + viewResponder?.deleteItems(at: [IndexPath(item: index, section: 0)]) + } + } + let type = videoSeatViewType + self.refreshListSeatItem() + if type != videoSeatViewType { + viewResponder?.reloadData() + } + self.resetMiniscreen() } - + private func getSeatItem(_ userId: String) -> VideoSeatItem? { return videoSeatItems.first(where: { $0.userId == userId }) } - + private func sortSeatItems() { + guard checkNeededSort() else { return } // 自己在第二位 if let currentItemIndex = videoSeatItems.firstIndex(where: { $0.userId == self.currentUserId }) { let currentItem = videoSeatItems.remove(at: currentItemIndex) videoSeatItems.insert(currentItem, at: 0) } // 房主永远在第一位 - if let roomOwnerItemIndex = videoSeatItems.firstIndex(where: { $0.userRole == .roomOwner }) { + if let roomOwnerItemIndex = videoSeatItems.firstIndex(where: { $0.userId == roomInfo.ownerId }) { let roomOwnerItem = videoSeatItems.remove(at: roomOwnerItemIndex) videoSeatItems.insert(roomOwnerItem, at: 0) } + + viewResponder?.reloadData() } - + + private func checkNeededSort() -> Bool { + var isSort = false + if let roomOwnerItemIndex = videoSeatItems.firstIndex(where: { $0.userId == roomInfo.ownerId }) { + isSort = roomOwnerItemIndex != 0 + } + if let currentItemIndex = videoSeatItems.firstIndex(where: { $0.userId == self.currentUserId }) { + if currentUserId == roomInfo.ownerId { + isSort = isSort || (currentItemIndex != 0) + } else { + isSort = isSort || (currentItemIndex != 1) + } + } + return isSort + } + private func findCurrentSpeaker(list: [VideoSeatItem]) -> VideoSeatItem? { let array = list.filter({ $0.type == .original }) var currentSpeakerItem: VideoSeatItem? @@ -326,11 +296,11 @@ extension TUIVideoSeatViewModel { } return currentSpeakerItem } - + + //判断type状态 private func refreshListSeatItem() { sortSeatItems() listSeatItem = Array(videoSeatItems) - if videoSeatItems.count == 1 { // 单人 videoSeatViewType = .singleType @@ -354,7 +324,7 @@ extension TUIVideoSeatViewModel { refreshMultiVideo() } } - + private func refreshMultiVideo() { let screenResult = videoSeatItems.filter({ $0.hasScreenStream }) let videoResult = videoSeatItems.filter({ $0.hasVideoStream }) @@ -366,7 +336,7 @@ extension TUIVideoSeatViewModel { // 只有一路视频 speechItem = item } - + if let item = speechItem, let seatItemIndex = videoSeatItems.firstIndex(where: { $0.userId == item.userId }) { // 演讲者 videoSeatViewType = .speechType @@ -398,7 +368,7 @@ extension TUIVideoSeatViewModel { videoSeatViewType = .equallyDividedType } } - + private func reloadSeatItems() { DispatchQueue.main.async { [weak self] in guard let self = self else { return } @@ -406,22 +376,26 @@ extension TUIVideoSeatViewModel { let lastListSeatItem = Array(self.listSeatItem) self.refreshListSeatItem() self.updateCollectionView(type, lastListSeatItem) - if self.videoSeatViewType == .largeSmallWindowType { - self.speakerItem = nil - self.shareItem = nil - self.viewResponder?.updateMiniscreen(self.smallItem) - } else if self.videoSeatViewType == .speechType { - self.smallItem = nil - self.viewResponder?.updateMiniscreen(self.speakerItem) - } else { - self.smallItem = nil - self.speakerItem = nil - self.shareItem = nil - self.viewResponder?.updateMiniscreen(nil) - } + self.resetMiniscreen() } } - + + private func resetMiniscreen() { + if self.videoSeatViewType == .largeSmallWindowType { + self.speakerItem = nil + self.shareItem = nil + self.viewResponder?.updateMiniscreen(self.smallItem) + } else if self.videoSeatViewType == .speechType { + self.smallItem = nil + self.viewResponder?.updateMiniscreen(self.speakerItem) + } else { + self.smallItem = nil + self.speakerItem = nil + self.shareItem = nil + self.viewResponder?.updateMiniscreen(nil) + } + } + private func updateSeatVolume(item: VideoSeatItem) { viewResponder?.updateSeatVolume(item) if let shareItem = shareItem, shareItem.userId == item.userId { @@ -429,20 +403,20 @@ extension TUIVideoSeatViewModel { shareItem.audioVolume = item.audioVolume viewResponder?.updateSeatVolume(shareItem) } - + if let speakerItem = speakerItem, speakerItem.userId == item.userId { speakerItem.hasAudioStream = item.hasAudioStream speakerItem.audioVolume = item.audioVolume viewResponder?.updateMiniscreenVolume(speakerItem) } - + if let smallItem = smallItem, smallItem.userId == item.userId { smallItem.hasAudioStream = item.hasAudioStream smallItem.audioVolume = item.audioVolume viewResponder?.updateMiniscreenVolume(smallItem) } } - + private func updateCollectionView(_ type: TUIVideoSeatViewType, _ lastList: [VideoSeatItem]) { if type != videoSeatViewType { viewResponder?.reloadData() @@ -455,23 +429,14 @@ extension TUIVideoSeatViewModel { indexPaths.append(IndexPath(item: i, section: 0)) } viewResponder?.insertItems(at: indexPaths) - } else if diffItem < 0 { - for i in (count + diffItem + 1) ... count { - indexPaths.append(IndexPath(item: i - 1, section: 0)) - } - viewResponder?.deleteItems(at: indexPaths) } - indexPaths = [] for i in 0 ... min(max(count - 1, 0), max(listSeatItem.count - 1, 0)) { if lastList.count > i && listSeatItem.count > i && lastList[i] != listSeatItem[i] { - indexPaths.append(IndexPath(item: i, section: 0)) + if let item = listSeatItem[safe: i] { + viewResponder?.updateSeatItem(item) + } } } - if indexPaths.count > 0 { - viewResponder?.reloadItems(at: indexPaths) - } else if diffItem == 0 && isNeedReloadStream { - viewResponder?.reloadData() - } } } @@ -483,20 +448,58 @@ extension TUIVideoSeatViewModel { } } -extension TUIVideoSeatViewModel: TUIRoomObserver { - func onUserAudioStateChanged(userId: String, hasAudio: Bool, reason: TUIChangeReason) { - guard let seatItem = getSeatItem(userId) else { - let userInfo = userNoExistMap[userId] ?? TUIUserInfo() - userInfo.hasAudioStream = hasAudio - userNoExistMap[userId] = userInfo - return +extension TUIVideoSeatViewModel: RoomKitUIEventResponder { + func onNotifyUIEvent(key: EngineEventCenter.RoomUIEvent, Object: Any?, info: [AnyHashable : Any]?) { + switch key { + case .TUIRoomKitService_RenewVideoSeatView: + initVideoSeatItems() + default: break } - // 同步刷新 分享和speaker的view的音量 - seatItem.hasAudioStream = hasAudio - updateSeatVolume(item: seatItem) } +} - public func onUserVoiceVolumeChanged(volumeMap: [String: NSNumber]) { +extension TUIVideoSeatViewModel: RoomEngineEventResponder { + func onEngineEvent(name: EngineEventCenter.RoomEngineEvent, param: [String : Any]?) { + switch name { + case .onUserAudioStateChanged: + guard let userId = param?["userId"] as? String else { return } + guard let hasAudio = param?["hasAudio"] as? Bool else { return } + guard let seatItem = getSeatItem(userId) else { return } + seatItem.hasAudioStream = hasAudio + updateSeatVolume(item: seatItem) + case .onUserVoiceVolumeChanged: + guard let volumeMap = param as? [String: NSNumber] else { return } + userVoiceVolumeChanged(volumeMap: volumeMap) + case .onUserVideoStateChanged: + guard let userId = param?["userId"] as? String else { return } + guard let streamType = param?["streamType"] as? TUIVideoStreamType else { return } + guard let hasVideo = param?["hasVideo"] as? Bool else { return } + userVideoStateChanged(userId: userId, streamType: streamType, hasVideo: hasVideo) + case .onUserScreenCaptureStopped: + userScreenCaptureStopped() + case .onRemoteUserEnterRoom: + guard let userInfo = param?["userInfo"] as? TUIUserInfo else { return } + guard roomInfo.speechMode != .applySpeakAfterTakingSeat else { return } + addUserInfo(userInfo.userId) + case .onRemoteUserLeaveRoom: + guard let userInfo = param?["userInfo"] as? TUIUserInfo else { return } + removeSeatItem(userInfo.userId) + case .onUserRoleChanged: + engineManager.fetchRoomInfo() { [weak self] in + guard let self = self else { return } + self.reloadSeatItems() + } + case .onSeatListChanged: + guard let left = param?["left"] as? [TUISeatInfo] else { return } + guard let seated = param?["seated"] as? [TUISeatInfo] else { return } + seatListChanged(seated: seated, left: left) + default: break + } + } +} + +extension TUIVideoSeatViewModel: TUIRoomObserver { + private func userVoiceVolumeChanged(volumeMap: [String: NSNumber]) { if volumeMap.count <= 0 { return } @@ -505,7 +508,7 @@ extension TUIVideoSeatViewModel: TUIRoomObserver { seatItem.audioVolume = volume.intValue updateSeatVolume(item: seatItem) } - + if videoSeatViewType == .speechType { guard let currentSpeakerItem = findCurrentSpeaker(list: listSeatItem), currentSpeakerItem.hasAudioStream else { return } if speakerItem?.userId == currentSpeakerItem.userId { @@ -518,28 +521,19 @@ extension TUIVideoSeatViewModel: TUIRoomObserver { speakerItem = currentSpeakerItem } } - - public func onUserVideoStateChanged(userId: String, streamType: TUIVideoStreamType, hasVideo: Bool, reason: TUIChangeReason) { + + private func userVideoStateChanged(userId: String, streamType: TUIVideoStreamType, hasVideo: Bool) { if streamType == .screenStream, userId == currentUserId { - viewResponder?.showScreenCaptureMaskView(isShow: hasVideo) - return + viewResponder?.showScreenCaptureMaskView(isShow: hasVideo) + return } if hasVideo { let renderParams = TRTCRenderParams() renderParams.fillMode = (streamType == .screenStream) ? .fit : .fill let trtcStreamType: TRTCVideoStreamType = (streamType == .screenStream) ? .sub : .big - roomEngine?.getTRTCCloud().setRemoteRenderParams(userId, streamType: trtcStreamType, params: renderParams) - } - guard var seatItem = getSeatItem(userId) else { - let userInfo = userNoExistMap[userId] ?? TUIUserInfo() - if streamType == .screenStream { - userInfo.hasScreenStream = hasVideo - } else { - userInfo.hasVideoStream = hasVideo - } - userNoExistMap[userId] = userInfo - return + engineManager.setRemoteRenderParams(userId: userId, streamType: trtcStreamType, params: renderParams) } + guard var seatItem = getSeatItem(userId) else { return } if streamType == .cameraStream { seatItem.hasVideoStream = hasVideo viewResponder?.updateSeatItem(seatItem) @@ -550,47 +544,34 @@ extension TUIVideoSeatViewModel: TUIRoomObserver { } } if hasVideo { - startPlayVideo(item: seatItem, renderView: viewResponder?.getSeatVideoRenderView(seatItem)) + startPlayVideo(item: seatItem, renderView: viewResponder?.getVideoVisibleCell(seatItem)?.renderView) } else { stopPlayVideo(item: seatItem) } - reloadSeatItems() + if streamType == .screenStream, hasVideo, videoSeatItems.filter({ $0.hasScreenStream }).count == 1 { + refreshListSeatItem() + viewResponder?.insertItems(at: [IndexPath(item: 0, section: 0)]) + resetMiniscreen() + } else { + reloadSeatItems() + } } - + // seatList: 当前麦位列表 seated: 新增上麦的用户列表 left: 下麦的用户列表 - public func onSeatListChanged(seatList: [TUISeatInfo], seated: [TUISeatInfo], left: [TUISeatInfo]) { + private func seatListChanged(seated: [TUISeatInfo], left: [TUISeatInfo]) { for leftSeat in left { if let userId = leftSeat.userId { removeSeatItem(userId) } } - for seatInfo in seatList { - addSeatInfo(seatInfo) - } - reloadSeatItems() - } - - public func onUserRoleChanged(userId: String, userRole: TUIRole) { - guard let seatItem = getSeatItem(userId) else { return } - seatItem.userRole = userRole - reloadSeatItems() - } - - public func onRemoteUserEnterRoom(roomId: String, userInfo: TUIUserInfo) { - switch roomInfo.speechMode { - case .freeToSpeak: - addUserInfo(userInfo) - case .applySpeakAfterTakingSeat: - break - default: break + for seatInfo in seated { + if let userId = seatInfo.userId { + addUserInfo(userId) + } } } - - public func onRemoteUserLeaveRoom(roomId: String, userInfo: TUIUserInfo) { - removeSeatItem(userInfo.userId) - } - public func onUserScreenCaptureStopped(reason: Int) { + private func userScreenCaptureStopped() { viewResponder?.showScreenCaptureMaskView(isShow: false) guard let seatItem = getSeatItem(currentUserId) else { return } seatItem.hasScreenStream = false diff --git a/iOS/TUIRoomKit/Source/ViewModel/TransferMasterViewModel.swift b/iOS/TUIRoomKit/Source/ViewModel/TransferMasterViewModel.swift index 92575427e..1d41f9bd2 100644 --- a/iOS/TUIRoomKit/Source/ViewModel/TransferMasterViewModel.swift +++ b/iOS/TUIRoomKit/Source/ViewModel/TransferMasterViewModel.swift @@ -10,6 +10,7 @@ import Foundation protocol TransferMasterViewResponder: NSObject { func reloadTransferMasterTableView() func searchControllerChangeActive(isActive: Bool) + func makeToast(message: String) } class TransferMasterViewModel: NSObject { @@ -37,15 +38,17 @@ class TransferMasterViewModel: NSObject { guard userId != "" else { return } engineManager.changeUserRole(userId: userId, role: .roomOwner) { [weak self] in guard let self = self else { return } - self.engineManager.exitRoom(onSuccess: nil, onError: nil) - self.roomRouter.dismissAllRoomPopupViewController() - self.roomRouter.popToRoomEntranceViewController() + self.engineManager.exitRoom { [weak self] in + guard let self = self else { return } + self.roomRouter.dismissAllRoomPopupViewController() + self.roomRouter.popToRoomEntranceViewController() + } onError: { [weak self] code, message in + guard let self = self else { return } + self.viewResponder?.makeToast(message: message) + } } onError: { [weak self] code, message in guard let self = self else { return } - self.engineManager.destroyRoom(onSuccess: nil, onError: nil) - self.roomRouter.dismissAllRoomPopupViewController() - self.roomRouter.popToRoomEntranceViewController() - debugPrint("changeUserRole:code:\(code),message:\(message)") + self.viewResponder?.makeToast(message: message) } } diff --git a/iOS/TUIRoomKit/Source/ViewModel/UserListManagerViewModel.swift b/iOS/TUIRoomKit/Source/ViewModel/UserListManagerViewModel.swift index 79606ac72..dc102452e 100644 --- a/iOS/TUIRoomKit/Source/ViewModel/UserListManagerViewModel.swift +++ b/iOS/TUIRoomKit/Source/ViewModel/UserListManagerViewModel.swift @@ -13,6 +13,7 @@ protocol UserListManagerViewEventResponder: AnyObject { func updateUI(items: [ButtonItemData]) func makeToast(text: String) func showTransferredRoomOwnerAlert() + func showKickOutAlert(title: String, sureAction: (() ->())?) func setUserListManagerViewHidden(isHidden: Bool) } @@ -25,7 +26,7 @@ class UserListManagerViewModel: NSObject { } var userId: String = "" var userName: String = "" - let timeoutNumber: Double = 0 + let timeoutNumber: Double = 10 private(set) var otherUserItems: [ButtonItemData] = []//其他用户viewItem private(set) var currentUserItems: [ButtonItemData] = [] private(set) var onSeatItems: [ButtonItemData] = []//已经上麦的用户viewItem @@ -45,7 +46,6 @@ class UserListManagerViewModel: NSObject { } private var hasOpenCameraInvite = false private var hasOpenMicrophoneInvite = false - var hasTakeSeatInvite = false override init() { super.init() @@ -315,29 +315,20 @@ class UserListManagerViewModel: NSObject { func inviteSeatAction(sender: UIButton) { sender.isSelected = !sender.isSelected - if !hasTakeSeatInvite { - engineManager.takeUserOnSeatByAdmin(userId: userId, timeout: timeoutNumber) { [weak self] _,_ in - guard let self = self else { return } - self.hasTakeSeatInvite = false + if !engineManager.store.extendedInvitationList.contains(userId) { + engineManager.takeUserOnSeatByAdmin(userId: userId, timeout: timeoutNumber) { _,_ in } onRejected: { [weak self] requestId, userId, message in guard let self = self else { return } - self.hasTakeSeatInvite = false self.viewResponder?.makeToast(text: self.userName + .refusedTakeSeatInvitationText) - } onCancelled: { [weak self] _, _ in - guard let self = self else { return } - self.hasTakeSeatInvite = false } onTimeout: { [weak self] _, _ in guard let self = self else { return } - self.hasTakeSeatInvite = false self.viewResponder?.makeToast(text: .takeSeatInvitationTimeoutText) } onError: { [weak self] _, _, _, message in guard let self = self else { return } - self.hasTakeSeatInvite = false self.viewResponder?.makeToast(text: message) } } viewResponder?.makeToast(text: .invitedTakeSeatText) - hasTakeSeatInvite = true hideUserListManagerView() } @@ -368,8 +359,12 @@ class UserListManagerViewModel: NSObject { func kickOutAction(sender: UIButton) { sender.isSelected = !sender.isSelected - engineManager.kickRemoteUserOutOfRoom(userId: userId) - hideUserListManagerView() + let kickOutTitle = localizedReplace(.kickOutText, replace: userName) + viewResponder?.showKickOutAlert(title: kickOutTitle, sureAction: { [weak self] in + guard let self = self else { return } + self.engineManager.kickRemoteUserOutOfRoom(userId: self.userId) + self.hideUserListManagerView() + }) } //根据用户的状态更新item数组 @@ -542,4 +537,7 @@ private extension String { static var invitedOpenVideoText: String { localized("TUIRoom.invited.open.video") } + static var kickOutText: String { + localized("TUIRoom.sure.kick.out") + } } diff --git a/iOS/TUIRoomKit/Source/ViewModel/UserListViewModel.swift b/iOS/TUIRoomKit/Source/ViewModel/UserListViewModel.swift index 00b08fd2b..d10322222 100644 --- a/iOS/TUIRoomKit/Source/ViewModel/UserListViewModel.swift +++ b/iOS/TUIRoomKit/Source/ViewModel/UserListViewModel.swift @@ -100,6 +100,7 @@ class UserListViewModel: NSObject { } func inviteSeatAction(sender: UIButton) { + userManagerModel.userId = userId userManagerModel.inviteSeatAction(sender: sender) } } diff --git a/iOS/TUIRoomKit/TUIRoomKit.podspec b/iOS/TUIRoomKit/TUIRoomKit.podspec index 8923d22cc..c2ff62746 100644 --- a/iOS/TUIRoomKit/TUIRoomKit.podspec +++ b/iOS/TUIRoomKit/TUIRoomKit.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |spec| spec.name = 'TUIRoomKit' - spec.version = '1.6.1' + spec.version = '1.7.0' spec.platform = :ios spec.ios.deployment_target = '13.0' spec.license = { :type => 'MIT', :file => 'LICENSE' } @@ -23,7 +23,7 @@ Pod::Spec.new do |spec| spec.default_subspec = 'TRTC' spec.subspec 'Professional' do |professional| - professional.dependency 'TUIRoomEngine/Professional', '1.6.1' + professional.dependency 'TUIRoomEngine/Professional', '1.7.0' professional.source_files = 'Source/*.swift', 'Source/Presenter/*.swift', 'Source/**/*.swift', 'Source/**/*.h', 'Source/**/*.m', 'RoomExtension/**/*.swift', 'RoomExtension/**/*.h', 'RoomExtension/**/*.m' professional.resource_bundles = { 'TUIRoomKitBundle' => ['Resources/*.xcassets', 'Resources/Localized/**/*.strings'] @@ -32,7 +32,7 @@ Pod::Spec.new do |spec| end spec.subspec 'TRTC' do |trtc| - trtc.dependency 'TUIRoomEngine/TRTC', '1.6.1' + trtc.dependency 'TUIRoomEngine/TRTC', '1.7.0' trtc.source_files = 'Source/*.swift', 'Source/Presenter/*.swift', 'Source/**/*.swift', 'Source/**/*.h', 'Source/**/*.m', 'RoomExtension/**/*.swift', 'RoomExtension/**/*.h', 'RoomExtension/**/*.m' trtc.resource_bundles = { 'TUIRoomKitBundle' => ['Resources/*.xcassets', 'Resources/Localized/**/*.strings']