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

View File

@@ -0,0 +1,131 @@
//
// ConferenceInvitationService.swift
// TUIRoomKit
//
// Created by jeremiawang on 2024/8/8.
//
import Foundation
import RTCRoomEngine
import Combine
import Factory
class ConferenceInvitationService: NSObject {
@WeakLazyInjected(\.conferenceStore) var store: ConferenceStore?
typealias InviteUsersResult = ([String: NSNumber])
typealias InvitationfetchResult = ([TUIInvitation], String)
private let timeout: Double = 60
private let invitationManager = TUIRoomEngine.sharedInstance().getExtension(extensionType: .conferenceInvitationManager) as? TUIConferenceInvitationManager
override init() {
super.init()
invitationManager?.addObserver(self)
}
deinit {
invitationManager?.removeObserver(self)
}
func inviteUsers(roomId: String, userIdList: [String]) -> AnyPublisher<InviteUsersResult, RoomError> {
return Future<InviteUsersResult, RoomError> { [weak self] promise in
guard let self = self else { return }
self.invitationManager?.inviteUsers(roomId, userIdList: userIdList, timeout: timeout, extensionInfo: "") {dic in
promise(.success((dic)))
} onError: { error, message in
promise(.failure(RoomError(error: error, message: message)))
}
}
.eraseToAnyPublisher()
}
func accept(roomId: String) -> AnyPublisher<String, RoomError> {
return Future<String, RoomError> { [weak self] promise in
guard let self = self else { return }
self.invitationManager?.accept(roomId) {
promise(.success(roomId))
} onError: { error, message in
promise(.failure(RoomError(error: error, message: message)))
}
}
.eraseToAnyPublisher()
}
func reject(roomId: String, reason: TUIInvitationRejectedReason) -> AnyPublisher<String, RoomError> {
return Future<String, RoomError> { [weak self] promise in
guard let self = self else { return }
self.invitationManager?.reject(roomId, reason: reason) {
promise(.success(roomId))
} onError: { error, message in
promise(.failure(RoomError(error: error, message: message)))
}
}
.eraseToAnyPublisher()
}
func getInvitationList(roomId: String, cursor: String, count: Int = 20) -> AnyPublisher<InvitationfetchResult, RoomError> {
return Future<InvitationfetchResult, RoomError> { [weak self] promise in
guard let self = self else { return }
self.invitationManager?.getInvitationList(roomId, cursor: cursor, count: count) {invitations, cursor in
promise(.success((invitations, cursor)))
} onError: { error, message in
promise(.failure(RoomError(error: error, message: message)))
}
}
.eraseToAnyPublisher()
}
}
extension ConferenceInvitationService: TUIConferenceInvitationObserver {
func onReceiveInvitation(roomInfo: TUIRoomInfo, invitation: TUIInvitation, extensionInfo: String) {
}
func onInvitationHandledByOtherDevice(roomInfo: TUIRoomInfo, accepted: Bool) {
guard let store = self.store else { return }
store.dispatch(action: InvitationViewActions.dismissInvitationView())
}
func onInvitationCancelled(roomInfo: TUIRoomInfo, invitation: TUIInvitation) {
}
func onInvitationAccepted(roomInfo: TUIRoomInfo, invitation: TUIInvitation) {
}
func onInvitationRejected(roomInfo: TUIRoomInfo, invitation: TUIInvitation, reason: TUIInvitationRejectedReason) {
}
func onInvitationTimeout(roomInfo: TUIRoomInfo, invitation: TUIInvitation) {
guard let store = self.store else { return }
store.dispatch(action: InvitationViewActions.dismissInvitationView())
}
func onInvitationRevokedByAdmin(roomInfo: TUIRoomInfo, invitation: TUIInvitation, admin: TUIUserInfo) {
}
func onInvitationAdded(roomId: String, invitation: TUIInvitation) {
guard let store = self.store else { return }
let currentRoomId = store.selectCurrent(RoomSelectors.getRoomId)
guard currentRoomId == roomId else { return }
store.dispatch(action: ConferenceInvitationActions.addInvitation(payload: invitation))
}
func onInvitationRemoved(roomId: String, invitation: TUIInvitation) {
guard let store = self.store else { return }
let currentRoomId = store.selectCurrent(RoomSelectors.getRoomId)
guard currentRoomId == roomId else { return }
store.dispatch(action: ConferenceInvitationActions.removeInvitation(payload: invitation.invitee.userId))
}
func onInvitationStatusChanged(roomId: String, invitation: TUIInvitation) {
guard let store = self.store else { return }
let currentRoomId = store.selectCurrent(RoomSelectors.getRoomId)
guard currentRoomId == roomId else { return }
store.dispatch(action: ConferenceInvitationActions.changeInvitationStatus(payload: invitation))
}
}

View File

@@ -0,0 +1,191 @@
//
// ConferenceListService.swift
// TUIRoomKit
//
// Created by CY zhao on 2024/6/27.
//
import Foundation
import RTCRoomEngine
import Combine
import Factory
class ConferenceListService: NSObject {
@WeakLazyInjected(\.conferenceStore) var store: ConferenceStore?
private let listManager = TUIRoomEngine.sharedInstance().getExtension(extensionType: .conferenceListManager) as? TUIConferenceListManager
typealias ConferencesFetchResult = ([ConferenceInfo], String)
typealias AttendeesFetchResult = ([UserInfo], String, UInt)
override init() {
super.init()
listManager?.addObserver(self)
}
deinit {
listManager?.removeObserver(self)
}
func scheduleConference(conferenceInfo: TUIConferenceInfo) -> AnyPublisher<TUIConferenceInfo, RoomError> {
return Future<TUIConferenceInfo, RoomError> { [weak self] promise in
guard let self = self else { return }
self.listManager?.scheduleConference(conferenceInfo) {
promise(.success(conferenceInfo))
} onError: { error, message in
promise(.failure(RoomError(error: error, message: message)))
}
}
.eraseToAnyPublisher()
}
func cancelConference(conferenceId: String) -> AnyPublisher<Void, RoomError> {
return Future<Void, RoomError> { [weak self] promise in
guard let self = self else { return }
self.listManager?.cancelConference(conferenceId) {
promise(.success(()))
} onError: { error, message in
promise(.failure(RoomError(error: error , message: message)))
}
}
.eraseToAnyPublisher()
}
func updateConferenceInfo(conferenceInfo: TUIConferenceInfo, modifyFlag: TUIConferenceModifyFlag) -> AnyPublisher<Void, RoomError> {
return Future<Void, RoomError> { [weak self] promise in
guard let self = self else { return }
self.listManager?.updateConferenceInfo(conferenceInfo: conferenceInfo, modifyFlag: modifyFlag) {
promise(.success(()))
} onError: { error, message in
promise(.failure(RoomError(error: error , message: message)))
}
}
.eraseToAnyPublisher()
}
func fetchConferenceList(status: TUIConferenceStatus, cursor: String, count: Int = 20) -> AnyPublisher<ConferencesFetchResult, RoomError> {
return Future<ConferencesFetchResult, RoomError> { [weak self] promise in
guard let self = self else { return }
self.listManager?.fetchScheduledConferenceList(status: status,
cursor: cursor,
count: count) { conferenceList, cursor in
let list = conferenceList.map { ConferenceInfo(with: $0) }
promise(.success((list, cursor)))
} onError: { error, message in
promise(.failure(RoomError(error: error, message: message)))
}
}
.eraseToAnyPublisher()
}
func fetchAttendeeList(conferenceId: String, cursor: String, count: Int = 20) -> AnyPublisher<AttendeesFetchResult, RoomError> {
return Future<AttendeesFetchResult, RoomError> { [weak self] promise in
guard let self = self else { return }
self.listManager?.fetchAttendeeList(roomId: conferenceId, cursor: cursor, count: count) { attendeeList, cursor, totalCount in
let userInfoList = attendeeList.map { UserInfo(userInfo: $0) }
promise(.success((userInfoList, cursor, totalCount)))
} onError: { error, message in
promise(.failure(RoomError(error: error, message: message)))
}
}
.eraseToAnyPublisher()
}
func addAttendeesByAdmin(conferenceId: String, userIdList: [String]) -> AnyPublisher<Void, RoomError> {
return Future<Void, RoomError> { [weak self] promise in
guard let self = self else { return }
self.listManager?.addAttendeesByAdmin(roomId: conferenceId, userIdList: userIdList) {
promise(.success(()))
} onError: { error, message in
promise(.failure(RoomError(error: error , message: message)))
}
}
.eraseToAnyPublisher()
}
func removeAttendeesByAdmin(conferenceId: String, userIdList: [String]) -> AnyPublisher<Void, RoomError> {
return Future<Void, RoomError> { [weak self] promise in
guard let self = self else { return }
self.listManager?.removeAttendeesByAdmin(roomId: conferenceId, userIdList: userIdList) {
promise(.success(()))
} onError: { error, message in
promise(.failure(RoomError(error: error , message: message)))
}
}
.eraseToAnyPublisher()
}
func fetchConferenceInfo(roomId: String) -> AnyPublisher<ConferenceInfo, RoomError> {
return Future<ConferenceInfo, RoomError> { [weak self] promise in
guard let self = self else { return }
EngineManager.shared.fetchRoomInfo(roomId: roomId) { [weak self] roomInfo in
guard let self = self, let roomInfo = roomInfo else { return }
let currentList = self.store?.selectCurrent(ConferenceListSelectors.getConferenceList)
guard var updateConference = currentList?.first(where: { $0.basicInfo.roomId == roomInfo.roomId }) else { return }
updateConference.basicInfo = RoomInfo(with: roomInfo)
promise(.success(updateConference))
} onError: { error, message in
promise(.failure(RoomError(error: error , message: message)))
}
}
.eraseToAnyPublisher()
}
}
extension ConferenceListService: TUIConferenceListManagerObserver {
func onConferenceScheduled(conferenceInfo: TUIConferenceInfo) {
guard let store = self.store else { return }
let conference = ConferenceInfo(with: conferenceInfo)
let currentList = store.selectCurrent(ConferenceListSelectors.getConferenceList)
let contain = currentList.contains { $0.basicInfo.roomId == conference.basicInfo.roomId }
if !contain {
store.dispatch(action: ConferenceListActions.insertConference(payload: conference))
}
}
func onConferenceCancelled(roomId: String, reason: TUIConferenceCancelReason, operateUser: TUIUserInfo) {
guard let store = self.store else { return }
let currentList = store.selectCurrent(ConferenceListSelectors.getConferenceList).map { $0.basicInfo.roomId }
if currentList.contains(roomId) {
store.dispatch(action: ConferenceListActions.removeConference(payload: roomId))
}
}
func onConferenceInfoChanged(conferenceInfo: TUIConferenceInfo, modifyFlag: TUIConferenceModifyFlag) {
guard let store = self.store else { return }
let currentList = store.selectCurrent(ConferenceListSelectors.getConferenceList)
if let index = currentList.firstIndex(where: { $0.basicInfo.roomId == conferenceInfo.basicRoomInfo.roomId }) {
var updateConference = currentList[index]
if modifyFlag.contains(.roomName) {
updateConference.basicInfo.name = conferenceInfo.basicRoomInfo.name
}
if modifyFlag.contains(.scheduleStartTime) || modifyFlag.contains(.scheduleEndTime) {
updateConference.scheduleStartTime = conferenceInfo.scheduleStartTime
updateConference.scheduleEndTime = conferenceInfo.scheduleEndTime
updateConference.durationTime = conferenceInfo.scheduleEndTime - conferenceInfo.scheduleStartTime
}
store.dispatch(action: ConferenceListActions.onConferenceUpdated(payload: updateConference))
}
}
func onConferenceStatusChanged(roomId: String, status: TUIConferenceStatus) {
guard let store = self.store else { return }
let currentList = store.selectCurrent(ConferenceListSelectors.getConferenceList)
if let index = currentList.firstIndex(where: { $0.basicInfo.roomId == roomId }) {
var updateConference = currentList[index]
updateConference.status = status
store.dispatch(action: ConferenceListActions.onConferenceUpdated(payload: updateConference))
}
}
func onConferenceWillStart(conferenceInfo: TUIConferenceInfo) {
}
func onScheduleAttendeesChanged(roomId: String, leftUsers: [TUIUserInfo], joinedUsers: [TUIUserInfo]) {
}
}

View File

@@ -0,0 +1,23 @@
//
// ErrorService.swift
// TUIRoomKit
//
// Created by CY zhao on 2024/6/13.
//
import Foundation
import RTCRoomEngine
struct RoomError: Error {
let error: TUIError
let message: String
var actions: [Action] = []
init(error: TUIError, message: String = "", showToast: Bool = true) {
self.error = error
self.message = message
if showToast {
actions.append(ViewActions.showToast(payload: ToastInfo(message: message)))
}
}
}

View File

@@ -0,0 +1,85 @@
//
// InvitationObserverService.swift
// TUIRoomKit
//
// Created by jeremiawang on 2024/8/23.
//
import Foundation
import RTCRoomEngine
import Factory
class InvitationObserverService: NSObject {
static let shared = InvitationObserverService()
private var invitationWindow: UIWindow?
private override init() {
}
func showInvitationWindow(roomInfo: TUIRoomInfo, invitation: TUIInvitation) {
DispatchQueue.main.async {
let invitationViewController = ConferenceInvitationViewController(roomInfo: roomInfo, invitation: invitation)
self.invitationWindow = UIWindow()
self.invitationWindow?.windowLevel = .alert + 1
self.invitationWindow?.rootViewController = invitationViewController
self.invitationWindow?.isHidden = false
self.invitationWindow?.t_makeKeyAndVisible()
}
}
func dismissInvitationWindow() {
DispatchQueue.main.async {
self.invitationWindow?.isHidden = true
self.invitationWindow = nil
}
}
}
extension InvitationObserverService: TUIConferenceInvitationObserver {
func onReceiveInvitation(roomInfo: TUIRoomInfo, invitation: TUIInvitation, extensionInfo: String) {
let store = Container.shared.conferenceStore()
store.dispatch(action: ConferenceInvitationActions.onReceiveInvitation(payload: (roomInfo, invitation)))
}
func onInvitationHandledByOtherDevice(roomInfo: TUIRoomInfo, accepted: Bool) {
}
func onInvitationCancelled(roomInfo: TUIRoomInfo, invitation: TUIInvitation) {
}
func onInvitationAccepted(roomInfo: TUIRoomInfo, invitation: TUIInvitation) {
}
func onInvitationRejected(roomInfo: TUIRoomInfo, invitation: TUIInvitation, reason: TUIInvitationRejectedReason) {
}
func onInvitationTimeout(roomInfo: TUIRoomInfo, invitation: TUIInvitation) {
}
func onInvitationRevokedByAdmin(roomInfo: TUIRoomInfo, invitation: TUIInvitation, admin: TUIUserInfo) {
}
func onInvitationAdded(roomId: String, invitation: TUIInvitation) {
}
func onInvitationRemoved(roomId: String, invitation: TUIInvitation) {
}
func onInvitationStatusChanged(roomId: String, invitation: TUIInvitation) {
}
}
extension UIWindow {
public func t_makeKeyAndVisible() {
if #available(iOS 13.0, *) {
for windowScene in UIApplication.shared.connectedScenes {
if windowScene.activationState == UIScene.ActivationState.foregroundActive ||
windowScene.activationState == UIScene.ActivationState.background {
self.windowScene = windowScene as? UIWindowScene
break
}
}
}
self.makeKeyAndVisible()
}
}

View File

@@ -0,0 +1,37 @@
//
// RoomService.swift
// TUIRoomKit
//
// Created by CY zhao on 2024/7/2.
//
import Foundation
import RTCRoomEngine
import ImSDK_Plus
import Combine
class RoomService {
private let engineManager = EngineManager.shared
func enterRoom(roomId: String,
options: TUIEnterRoomOptions? = nil,
enableAudio: Bool,
enableVideo: Bool,
isSoundOnSpeaker: Bool) -> AnyPublisher<Void, RoomError> {
return Future<Void, RoomError> { [weak self] promise in
guard let self = self else { return }
//TODO: use RTCRoomEngine directly
self.engineManager.enterRoom(roomId: roomId,
options: options,
enableAudio: enableAudio,
enableVideo: enableVideo,
isSoundOnSpeaker: isSoundOnSpeaker) { roomInfo in
promise(.success(()))
} onError: { error, message in
let error = RoomError(error: error, message: message)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}
}

View File

@@ -0,0 +1,22 @@
//
// ServiceCenter.swift
// TUIRoomKit
//
// Created by CY zhao on 2024/6/12.
//
import Foundation
import Factory
import RTCRoomEngine
import Combine
class ServiceCenter: NSObject {
@WeakLazyInjected(\.conferenceStore) var store: ConferenceStore?
@WeakLazyInjected(\.navigation) var navigator: Route?
let userService = UserService()
let conferenceListService = ConferenceListService()
let roomService = RoomService()
let conferenceInvitationService = ConferenceInvitationService()
}

View File

@@ -0,0 +1,34 @@
//
// UserService.swift
// TUIRoomKit
//
// Created by CY zhao on 2024/6/13.
//
import Foundation
import RTCRoomEngine
import ImSDK_Plus
import Combine
class UserService {
private let engine = TUIRoomEngine.sharedInstance()
private let imManager = V2TIMManager.sharedInstance()
func fetchUserInfo(_ userId: String) -> AnyPublisher<UserInfo, RoomError> {
return Future<UserInfo, RoomError> { [weak self] promise in
guard let self = self else { return }
self.engine.getUserInfo(userId) { userInfo in
if let user = userInfo {
promise(.success(UserInfo(userInfo: user)))
} else {
let error = RoomError(error: TUIError.userNotExist)
promise(.failure(error))
}
} onError: { err, message in
let error = RoomError(error: err, message: message, showToast: false)
promise(.failure(error))
}
}
.eraseToAnyPublisher()
}
}