diff --git a/Session/Calls/Call Management/SessionCall.swift b/Session/Calls/Call Management/SessionCall.swift index a99a69575..af64468c7 100644 --- a/Session/Calls/Call Management/SessionCall.swift +++ b/Session/Calls/Call Management/SessionCall.swift @@ -305,15 +305,16 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate { func endSessionCall() { guard !hasEnded else { return } - let sessionId: String = self.sessionId - webRTCSession.hangUp() - - Storage.shared.writeAsync { [weak self] db in - try self?.webRTCSession.endCall(db, with: sessionId) - } - - hasEnded = true + webRTCSession.endCall( + with: self.sessionId + ) + .sinkUntilComplete( + receiveCompletion: { [weak self] _ in + self?.hasEnded = true + Singleton.callManager.cleanUpPreviousCall() + } + ) } func handleCallInitializationFailed() { diff --git a/Session/Calls/Call Management/SessionCallManager.swift b/Session/Calls/Call Management/SessionCallManager.swift index cf8f9f2d9..72fdae58a 100644 --- a/Session/Calls/Call Management/SessionCallManager.swift +++ b/Session/Calls/Call Management/SessionCallManager.swift @@ -143,20 +143,8 @@ public final class SessionCallManager: NSObject, CallManagerProtocol { return } - func handleCallEnded() { - SNLog("[Calls] Call ended.") - WebRTCSession.current = nil - UserDefaults.sharedLokiProject?[.isCallOngoing] = false - UserDefaults.sharedLokiProject?[.lastCallPreOffer] = nil - - if Singleton.hasAppContext && Singleton.appContext.isNotInForeground { - (UIApplication.shared.delegate as? AppDelegate)?.stopPollers() - Log.flush() - } - } - guard let call = currentCall else { - handleCallEnded() + self.cleanUpPreviousCall() suspendDatabaseIfCallEndedInBackground() return } @@ -175,9 +163,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol { call.updateCallMessage(mode: .local, using: dependencies) } - (call as? SessionCall)?.webRTCSession.dropConnection() - self.currentCall = nil - handleCallEnded() + self.cleanUpPreviousCall() } public func currentWebRTCSessionMatches(callId: String) -> Bool { @@ -301,4 +287,19 @@ public final class SessionCallManager: NSObject, CallManagerProtocol { (Singleton.appContext.frontmostViewController as? CallVC)?.handleEndCallMessage() MiniCallView.current?.dismiss() } + + public func cleanUpPreviousCall() { + SNLog("[Calls] Clean up calls") + + WebRTCSession.current?.dropConnection() + WebRTCSession.current = nil + currentCall = nil + UserDefaults.sharedLokiProject?[.isCallOngoing] = false + UserDefaults.sharedLokiProject?[.lastCallPreOffer] = nil + + if Singleton.hasAppContext && Singleton.appContext.isNotInForeground { + (UIApplication.shared.delegate as? AppDelegate)?.stopPollers() + Log.flush() + } + } } diff --git a/Session/Calls/WebRTC/WebRTCSession.swift b/Session/Calls/WebRTC/WebRTCSession.swift index 57d9ed29b..adc81af73 100644 --- a/Session/Calls/WebRTC/WebRTCSession.swift +++ b/Session/Calls/WebRTC/WebRTCSession.swift @@ -358,39 +358,50 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate { ) } - public func endCall( - _ db: Database, - with sessionId: String - ) throws { - guard let thread: SessionThread = try SessionThread.fetchOne(db, id: sessionId) else { return } - - SNLog("[Calls] Sending end call message.") - - let preparedSendData: MessageSender.PreparedSendData = try MessageSender - .preparedSendData( - db, - message: CallMessage( - uuid: self.uuid, - kind: .endCall, - sdps: [] - ) - .with(try? thread.disappearingMessagesConfiguration - .fetchOne(db)? - .forcedWithDisappearAfterReadIfNeeded() - ), - to: try Message.Destination.from(db, threadId: thread.id, threadVariant: thread.variant), - namespace: try Message.Destination - .from(db, threadId: thread.id, threadVariant: thread.variant) - .defaultNamespace, - interactionId: nil, - using: dependencies - ) - - MessageSender - .sendImmediate(data: preparedSendData, using: dependencies) + public func endCall(with sessionId: String) -> AnyPublisher { + return dependencies.storage + .writePublisher { [dependencies = self.dependencies] db in + guard let thread: SessionThread = try SessionThread.fetchOne(db, id: sessionId) else { + throw WebRTCSessionError.noThread + } + + SNLog("[Calls] Sending end call message.") + + return try MessageSender + .preparedSendData( + db, + message: CallMessage( + uuid: self.uuid, + kind: .endCall, + sdps: [] + ) + .with(try? thread.disappearingMessagesConfiguration + .fetchOne(db)? + .forcedWithDisappearAfterReadIfNeeded() + ), + to: try Message.Destination.from(db, threadId: thread.id, threadVariant: thread.variant), + namespace: try Message.Destination + .from(db, threadId: thread.id, threadVariant: thread.variant) + .defaultNamespace, + interactionId: nil, + using: dependencies + ) + } .subscribe(on: DispatchQueue.global(qos: .userInitiated)) - .retry(5) - .sinkUntilComplete() + .flatMap { [dependencies = self.dependencies] sendData in + MessageSender + .sendImmediate(data: sendData, using: dependencies) + .retry(5) + } + .handleEvents(receiveCompletion: { result in + switch result { + case .finished: + SNLog("[Calls] End call message sent") + case .failure(let error): + SNLog("[Calls] Error sending End call message due to error: \(error)") + } + }) + .eraseToAnyPublisher() } public func dropConnection() { diff --git a/SessionMessagingKit/Calls/CallManagerProtocol.swift b/SessionMessagingKit/Calls/CallManagerProtocol.swift index a0ef8a9a9..5065256a6 100644 --- a/SessionMessagingKit/Calls/CallManagerProtocol.swift +++ b/SessionMessagingKit/Calls/CallManagerProtocol.swift @@ -38,4 +38,5 @@ public protocol CallManagerProtocol { func currentWebRTCSessionMatches(callId: String) -> Bool func dismissAllCallUI() + func cleanUpPreviousCall() } diff --git a/SessionMessagingKit/Calls/NoopSessionCallManager.swift b/SessionMessagingKit/Calls/NoopSessionCallManager.swift index 5f70680b7..3e152206e 100644 --- a/SessionMessagingKit/Calls/NoopSessionCallManager.swift +++ b/SessionMessagingKit/Calls/NoopSessionCallManager.swift @@ -23,4 +23,5 @@ internal struct NoopSessionCallManager: CallManagerProtocol { func currentWebRTCSessionMatches(callId: String) -> Bool { return false } func dismissAllCallUI() {} + func cleanUpPreviousCall() {} } diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift index 228f9ba92..ad5dd19ca 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+Calls.swift @@ -38,7 +38,7 @@ extension MessageReceiver { // MARK: - Specific Handling private static func handleNewCallMessage(_ db: Database, message: CallMessage, using dependencies: Dependencies) throws { - SNLog("[Calls] Received pre-offer message.") + SNLog("[Calls] Received pre-offer message with uuid: \(message.uuid).") // Determine whether the app is active based on the prefs rather than the UIApplication state to avoid // requiring main-thread execution @@ -59,6 +59,7 @@ extension MessageReceiver { else { return } guard let timestamp = message.sentTimestamp, TimestampUtils.isWithinOneMinute(timestampMs: timestamp) else { // Add missed call message for call offer messages from more than one minute + SNLog("[Calls] Got an expired call offer message with uuid: \(message.uuid). Sent at \(message.sentTimestamp), now is \(Date().timeIntervalSince1970 * 1000)") if let interaction: Interaction = try MessageReceiver.insertCallInfoMessage(db, for: message, state: .missed, using: dependencies) { let thread: SessionThread = try SessionThread.upsert( db,