Files
letro-ios/ElementX/Sources/Services/ElementCall/ElementCallWidgetDriver.swift
Stefan Ceriu ed0eed76e5 Expose Element Call settings in the developer options. Start using th… (#2039)
* Expose Element Call settings in the developer options. Start using the encryption widget parameter.

* Remove the Element Call feature flag
2023-11-07 16:12:03 +02:00

159 lines
5.9 KiB
Swift

//
// Copyright 2022 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 Combine
import Foundation
import MatrixRustSDK
private struct ElementCallWidgetMessage: Codable {
enum Direction: String, Codable {
case fromWidget
case toWidget
}
enum Action: String, Codable {
case hangup = "im.vector.hangup"
}
let direction: Direction
let action: Action
enum CodingKeys: String, CodingKey {
case direction = "api"
case action
}
}
class ElementCallWidgetDriver: WidgetCapabilitiesProvider, ElementCallWidgetDriverProtocol {
private let room: RoomProtocol
private var widgetDriver: WidgetDriverAndHandle?
let messagePublisher = PassthroughSubject<String, Never>()
private let actionsSubject: PassthroughSubject<ElementCallWidgetDriverAction, Never> = .init()
var actions: AnyPublisher<ElementCallWidgetDriverAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(room: RoomProtocol) {
self.room = room
}
func start(baseURL: URL, clientID: String, useEncryption: Bool) async -> Result<URL, ElementCallWidgetDriverError> {
guard let room = room as? Room else {
return .failure(.roomInvalid)
}
guard let widgetSettings = try? newVirtualElementCallWidget(props: .init(elementCallUrl: baseURL.absoluteString,
widgetId: UUID().uuidString,
parentUrl: nil,
hideHeader: nil,
preload: nil,
fontScale: nil,
appPrompt: false,
skipLobby: true,
confineToRoom: true,
font: nil,
analyticsId: nil,
encryption: useEncryption ? .perParticipantKeys : .unencrypted)) else {
return .failure(.failedBuildingWidgetSettings)
}
guard let urlString = try? await generateWebviewUrl(widgetSettings: widgetSettings, room: room,
props: .init(clientId: clientID,
languageTag: nil,
theme: nil)) else {
return .failure(.failedBuildingCallURL)
}
guard let url = URL(string: urlString) else {
return .failure(.failedParsingCallURL)
}
guard let widgetDriver = try? makeWidgetDriver(settings: widgetSettings) else {
return .failure(.failedBuildingWidgetDriver)
}
self.widgetDriver = widgetDriver
Task.detached { [weak self, widgetDriver, messagePublisher] in
MXLog.debug("Started message receiving loop")
defer {
MXLog.debug("Stopped message receiving loop")
}
while true {
guard let receivedMessage = await widgetDriver.handle.recv() else {
return
}
messagePublisher.send(receivedMessage)
MXLog.debug("Received message: \(receivedMessage)")
self?.handleMessageIfNeeded(receivedMessage)
}
}
Task.detached { [widgetDriver] in
MXLog.debug("Started widget driver")
defer {
MXLog.debug("Stopped widget driver")
}
await widgetDriver.driver.run(room: room, capabilitiesProvider: self)
}
return .success(url)
}
func sendMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError> {
guard let widgetDriver else {
return .failure(.driverNotSetup)
}
let result = await widgetDriver.handle.send(msg: message)
MXLog.debug("Sent message: \(message) with result: \(result)")
handleMessageIfNeeded(message)
return .success(result)
}
// MARK: - WidgetCapabilitiesProvider
func acquireCapabilities(capabilities: WidgetCapabilities) -> WidgetCapabilities {
capabilities
}
// MARK: - Private
func handleMessageIfNeeded(_ message: String) {
guard let data = message.data(using: .utf8),
let widgetMessage = try? JSONDecoder().decode(ElementCallWidgetMessage.self, from: data) else {
return
}
if widgetMessage.direction == .fromWidget {
switch widgetMessage.action {
case .hangup:
actionsSubject.send(.callEnded)
}
}
}
}