diff --git a/DemoApp/Sources/Components/AppEnvironment.swift b/DemoApp/Sources/Components/AppEnvironment.swift index ea66f24eb..27beb85e7 100644 --- a/DemoApp/Sources/Components/AppEnvironment.swift +++ b/DemoApp/Sources/Components/AppEnvironment.swift @@ -212,7 +212,7 @@ extension AppEnvironment { #if targetEnvironment(simulator) return .simple #else - return .simple + return .detailed #endif case .release: return .simple diff --git a/DemoApp/Sources/Views/CallView/CallingView/DetailedCallingView.swift b/DemoApp/Sources/Views/CallView/CallingView/DetailedCallingView.swift index b333f33d3..0e295b29d 100644 --- a/DemoApp/Sources/Views/CallView/CallingView/DetailedCallingView.swift +++ b/DemoApp/Sources/Views/CallView/CallingView/DetailedCallingView.swift @@ -164,7 +164,7 @@ struct DetailedCallingView: View { callType: callType, callId: text, members: members, - ring: callFlow == .ringEvents, + ring: false,//callFlow == .ringEvents, video: viewModel.callSettings.videoOn ) } @@ -189,6 +189,18 @@ struct DetailedCallingView: View { self.callAction = currentUser?.type == .regular ? callAction : .joinCall self.callFlow = currentUser?.type == .regular ? callFlow : .joinImmediately } + .onChange(of: viewModel.callingState) { state in + if state == .inCall && !hasRang { + hasRang = true + viewModel.ring( + callType: callType, + callId: text, + members: members, + video: true, + showOutgoingScreen: true + ) + } + } } @ViewBuilder @@ -244,3 +256,6 @@ struct DetailedCallingView: View { ) } } + +//TODO: temp for testing. +var hasRang = false diff --git a/Sources/StreamVideo/Call.swift b/Sources/StreamVideo/Call.swift index a2763ea43..ef79f775e 100644 --- a/Sources/StreamVideo/Call.swift +++ b/Sources/StreamVideo/Call.swift @@ -362,6 +362,16 @@ public class Call: @unchecked Sendable, WSEventsSubscriber { } return response.call } + + @discardableResult + public func ring(request: RingCallRequest) async throws -> RingCallResponse { + let response = try await coordinatorClient.ringCall( + type: callType, + id: callId, + ringCallRequest: request + ) + return response + } /// Updates an existing call with the specified parameters. /// - Parameters: diff --git a/Sources/StreamVideoSwiftUI/CallViewModel.swift b/Sources/StreamVideoSwiftUI/CallViewModel.swift index 03da5f698..0d68cdd7f 100644 --- a/Sources/StreamVideoSwiftUI/CallViewModel.swift +++ b/Sources/StreamVideoSwiftUI/CallViewModel.swift @@ -193,6 +193,7 @@ open class CallViewModel: ObservableObject { private(set) var localCallSettingsChange = false private var hasAcceptedCall = false + private var skipCallStateUpdates = false public var participants: [CallParticipant] { let updateParticipants = call?.state.participants ?? [] @@ -431,6 +432,46 @@ open class CallViewModel: ObservableObject { customData: customData ) } + + public func ring( + callType: String, + callId: String, + members: [Member], + video: Bool? = nil, + showOutgoingScreen: Bool = false + ) { + outgoingCallMembers = members + if showOutgoingScreen { + skipCallStateUpdates = true + setCallingState(.outgoing) + } + if self.call == nil || (call?.id != callId && call?.callType != callType) { + let callSettings = localCallSettingsChange ? callSettings : nil + let call = streamVideo.call( + callType: callType, + callId: callId, + callSettings: callSettings + ) + self.call = call + } + guard let call else { return } + Task(disposableBag: disposableBag, priority: .userInitiated) { [weak self] in + guard let self else { return } + do { + try await call.ring( + request: .init(membersIds: members.map(\.id).filter { $0 != self.streamVideo.user.id }, video: video) + ) + if let autoCancelTimeout = call.state.settings?.ring.autoCancelTimeoutMs { + let timeoutSeconds = TimeInterval(autoCancelTimeout / 1000) + startTimer(timeout: timeoutSeconds) + } + } catch { + self.error = error + setCallingState(.idle) + self.call = nil + } + } + } /// Enters into a lobby before joining a call. /// - Parameters: @@ -593,7 +634,9 @@ open class CallViewModel: ObservableObject { lineNumber: line ) if let call, (callingState != .inCall || self.call?.cId != call.cId) { - setCallingState(.inCall) + if !skipCallStateUpdates { + setCallingState(.inCall) + } self.call = call } else if call == nil, callingState != .idle { setCallingState(.idle) @@ -887,6 +930,7 @@ open class CallViewModel: ObservableObject { setActiveCall(call) } case .outgoing where call?.cId == event.callCid: + skipCallStateUpdates = false enterCall( call: call, callType: event.type, @@ -942,6 +986,7 @@ open class CallViewModel: ObservableObject { } private func updateCallStateIfNeeded() { + guard !skipCallStateUpdates else { return } if callingState == .outgoing { if !callParticipants.isEmpty { setCallingState(.inCall)