mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 11:58:32 +00:00
Fixes #3042 - Cancel ElementCall ringing as soon as the call ends
This commit is contained in:
committed by
Stefan Ceriu
parent
d16ba9f3ef
commit
239afeb662
@@ -42,6 +42,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
didSet {
|
||||
userSessionObserver?.cancel()
|
||||
if userSession != nil {
|
||||
configureElementCallService()
|
||||
configureNotificationManager()
|
||||
observeUserSessionChanges()
|
||||
startSync()
|
||||
@@ -637,6 +638,14 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func configureElementCallService() {
|
||||
guard let userSession else {
|
||||
fatalError("User session not setup")
|
||||
}
|
||||
|
||||
elementCallService.setClientProxy(userSession.clientProxy)
|
||||
}
|
||||
|
||||
private func configureNotificationManager() {
|
||||
notificationManager.setUserSession(userSession)
|
||||
|
||||
@@ -4826,6 +4826,47 @@ class ElementCallServiceMock: ElementCallServiceProtocol {
|
||||
}
|
||||
var underlyingActions: AnyPublisher<ElementCallServiceAction, Never>!
|
||||
|
||||
//MARK: - setClientProxy
|
||||
|
||||
var setClientProxyUnderlyingCallsCount = 0
|
||||
var setClientProxyCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return setClientProxyUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = setClientProxyUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
setClientProxyUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
setClientProxyUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var setClientProxyCalled: Bool {
|
||||
return setClientProxyCallsCount > 0
|
||||
}
|
||||
var setClientProxyReceivedClientProxy: ClientProxyProtocol?
|
||||
var setClientProxyReceivedInvocations: [ClientProxyProtocol] = []
|
||||
var setClientProxyClosure: ((ClientProxyProtocol) -> Void)?
|
||||
|
||||
func setClientProxy(_ clientProxy: ClientProxyProtocol) {
|
||||
setClientProxyCallsCount += 1
|
||||
setClientProxyReceivedClientProxy = clientProxy
|
||||
DispatchQueue.main.async {
|
||||
self.setClientProxyReceivedInvocations.append(clientProxy)
|
||||
}
|
||||
setClientProxyClosure?(clientProxy)
|
||||
}
|
||||
//MARK: - setupCallSession
|
||||
|
||||
var setupCallSessionRoomIDRoomDisplayNameUnderlyingCallsCount = 0
|
||||
@@ -8300,6 +8341,41 @@ class RoomProxyMock: RoomProxyProtocol {
|
||||
subscribeForUpdatesCallsCount += 1
|
||||
await subscribeForUpdatesClosure?()
|
||||
}
|
||||
//MARK: - subscribeToRoomInfoUpdates
|
||||
|
||||
var subscribeToRoomInfoUpdatesUnderlyingCallsCount = 0
|
||||
var subscribeToRoomInfoUpdatesCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return subscribeToRoomInfoUpdatesUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = subscribeToRoomInfoUpdatesUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
subscribeToRoomInfoUpdatesUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
subscribeToRoomInfoUpdatesUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var subscribeToRoomInfoUpdatesCalled: Bool {
|
||||
return subscribeToRoomInfoUpdatesCallsCount > 0
|
||||
}
|
||||
var subscribeToRoomInfoUpdatesClosure: (() -> Void)?
|
||||
|
||||
func subscribeToRoomInfoUpdates() {
|
||||
subscribeToRoomInfoUpdatesCallsCount += 1
|
||||
subscribeToRoomInfoUpdatesClosure?()
|
||||
}
|
||||
//MARK: - timelineFocusedOnEvent
|
||||
|
||||
var timelineFocusedOnEventEventIDNumberOfEventsUnderlyingCallsCount = 0
|
||||
|
||||
@@ -44,7 +44,17 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
return CXProvider(configuration: configuration)
|
||||
}()
|
||||
|
||||
private var incomingCallID: CallID?
|
||||
private weak var clientProxy: ClientProxyProtocol?
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var incomingCallID: CallID? {
|
||||
didSet {
|
||||
Task {
|
||||
await observeIncomingCallRoomStateUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var endUnansweredCallTask: Task<Void, Never>?
|
||||
|
||||
private var ongoingCallID: CallID?
|
||||
@@ -65,6 +75,10 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
callProvider.setDelegate(self, queue: nil)
|
||||
}
|
||||
|
||||
func setClientProxy(_ clientProxy: any ClientProxyProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
}
|
||||
|
||||
func setupCallSession(roomID: String, roomDisplayName: String) async {
|
||||
// Drop any ongoing calls when starting a new one
|
||||
if ongoingCallID != nil {
|
||||
@@ -221,4 +235,46 @@ class ElementCallService: NSObject, ElementCallServiceProtocol, PKPushRegistryDe
|
||||
|
||||
ongoingCallID = nil
|
||||
}
|
||||
|
||||
func observeIncomingCallRoomStateUpdates() async {
|
||||
cancellables.removeAll()
|
||||
|
||||
guard let clientProxy, let incomingCallID else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let roomProxy = await clientProxy.roomForIdentifier(incomingCallID.roomID) else {
|
||||
return
|
||||
}
|
||||
|
||||
roomProxy.subscribeToRoomInfoUpdates()
|
||||
|
||||
// There's no incoming event for call cancellations so try to infer
|
||||
// it from what we have. If the call is running before subscribing then wait
|
||||
// for it to change to `false` otherwise wait for it to turn `true` before
|
||||
// changing to `false`
|
||||
let isCallOngoing = roomProxy.hasOngoingCall
|
||||
|
||||
roomProxy
|
||||
.actionsPublisher
|
||||
.map { action in
|
||||
switch action {
|
||||
case .roomInfoUpdate:
|
||||
return roomProxy.hasOngoingCall
|
||||
}
|
||||
}
|
||||
.removeDuplicates()
|
||||
.dropFirst(isCallOngoing ? 0 : 1)
|
||||
.sink { [weak self] hasOngoingCall in
|
||||
guard let self else { return }
|
||||
|
||||
if !hasOngoingCall {
|
||||
MXLog.info("Call has been cancelled")
|
||||
cancellables.removeAll()
|
||||
endUnansweredCallTask?.cancel()
|
||||
callProvider.reportCall(with: incomingCallID.callKitID, endedAt: nil, reason: .remoteEnded)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright 2024 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum ElementCallServiceNotificationKey: String {
|
||||
case roomID
|
||||
case roomDisplayName
|
||||
}
|
||||
|
||||
let ElementCallServiceNotificationDiscardDelta = 10.0
|
||||
@@ -22,17 +22,12 @@ enum ElementCallServiceAction {
|
||||
case setCallMuted(_ muted: Bool, roomID: String)
|
||||
}
|
||||
|
||||
enum ElementCallServiceNotificationKey: String {
|
||||
case roomID
|
||||
case roomDisplayName
|
||||
}
|
||||
|
||||
let ElementCallServiceNotificationDiscardDelta = 10.0
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol ElementCallServiceProtocol {
|
||||
var actions: AnyPublisher<ElementCallServiceAction, Never> { get }
|
||||
|
||||
func setClientProxy(_ clientProxy: ClientProxyProtocol)
|
||||
|
||||
func setupCallSession(roomID: String, roomDisplayName: String) async
|
||||
|
||||
func tearDownCallSession()
|
||||
|
||||
@@ -155,6 +155,17 @@ class RoomProxy: RoomProxyProtocol {
|
||||
subscribeToTypingNotifications()
|
||||
}
|
||||
|
||||
func subscribeToRoomInfoUpdates() {
|
||||
guard roomInfoObservationToken == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] in
|
||||
MXLog.info("Received room info update")
|
||||
self?.actionsSubject.send(.roomInfoUpdate)
|
||||
})
|
||||
}
|
||||
|
||||
func timelineFocusedOnEvent(eventID: String, numberOfEvents: UInt16) async -> Result<TimelineProxyProtocol, RoomProxyError> {
|
||||
do {
|
||||
let timeline = try await room.timelineFocusedOnEvent(eventId: eventID, numContextEvents: numberOfEvents, internalIdPrefix: UUID().uuidString)
|
||||
@@ -596,13 +607,6 @@ class RoomProxy: RoomProxyProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func subscribeToRoomInfoUpdates() {
|
||||
roomInfoObservationToken = room.subscribeToRoomInfoUpdates(listener: RoomInfoUpdateListener { [weak self] in
|
||||
MXLog.info("Received room info update")
|
||||
self?.actionsSubject.send(.roomInfoUpdate)
|
||||
})
|
||||
}
|
||||
|
||||
private func subscribeToTypingNotifications() {
|
||||
typingNotificationObservationToken = room.subscribeToTypingNotifications(listener: RoomTypingNotificationUpdateListener { [weak self] typingUserIDs in
|
||||
guard let self else { return }
|
||||
|
||||
@@ -66,6 +66,8 @@ protocol RoomProxyProtocol {
|
||||
|
||||
func subscribeForUpdates() async
|
||||
|
||||
func subscribeToRoomInfoUpdates()
|
||||
|
||||
func timelineFocusedOnEvent(eventID: String, numberOfEvents: UInt16) async -> Result<TimelineProxyProtocol, RoomProxyError>
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
Reference in New Issue
Block a user