mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
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
This commit is contained in:
@@ -37,12 +37,14 @@ final class AppSettings {
|
|||||||
case viewSourceEnabled
|
case viewSourceEnabled
|
||||||
case richTextEditorEnabled
|
case richTextEditorEnabled
|
||||||
|
|
||||||
|
case elementCallBaseURL
|
||||||
|
case elementCallEncryptionEnabled
|
||||||
|
|
||||||
// Feature flags
|
// Feature flags
|
||||||
case shouldCollapseRoomStateEvents
|
case shouldCollapseRoomStateEvents
|
||||||
case userSuggestionsEnabled
|
case userSuggestionsEnabled
|
||||||
case readReceiptsEnabled
|
case readReceiptsEnabled
|
||||||
case swiftUITimelineEnabled
|
case swiftUITimelineEnabled
|
||||||
case elementCallEnabled
|
|
||||||
case chatBackupEnabled
|
case chatBackupEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,7 +217,11 @@ final class AppSettings {
|
|||||||
|
|
||||||
// MARK: - Element Call
|
// MARK: - Element Call
|
||||||
|
|
||||||
let elementCallBaseURL: URL = "https://call.element.dev"
|
@UserPreference(key: UserDefaultsKeys.elementCallBaseURL, defaultValue: "https://call.element.io", storageType: .userDefaults(store))
|
||||||
|
var elementCallBaseURL: URL
|
||||||
|
|
||||||
|
@UserPreference(key: UserDefaultsKeys.elementCallEncryptionEnabled, defaultValue: true, storageType: .userDefaults(store))
|
||||||
|
var elementCallUseEncryption
|
||||||
|
|
||||||
// MARK: - Notifications
|
// MARK: - Notifications
|
||||||
|
|
||||||
@@ -267,9 +273,6 @@ final class AppSettings {
|
|||||||
@UserPreference(key: UserDefaultsKeys.swiftUITimelineEnabled, defaultValue: false, storageType: .volatile)
|
@UserPreference(key: UserDefaultsKeys.swiftUITimelineEnabled, defaultValue: false, storageType: .volatile)
|
||||||
var swiftUITimelineEnabled
|
var swiftUITimelineEnabled
|
||||||
|
|
||||||
@UserPreference(key: UserDefaultsKeys.elementCallEnabled, defaultValue: true, storageType: .userDefaults(store))
|
|
||||||
var elementCallEnabled
|
|
||||||
|
|
||||||
@UserPreference(key: UserDefaultsKeys.chatBackupEnabled, defaultValue: false, storageType: .userDefaults(store))
|
@UserPreference(key: UserDefaultsKeys.chatBackupEnabled, defaultValue: false, storageType: .userDefaults(store))
|
||||||
var chatBackupEnabled
|
var chatBackupEnabled
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -510,7 +510,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
private func presentCallScreen(roomProxy: RoomProxyProtocol) {
|
private func presentCallScreen(roomProxy: RoomProxyProtocol) {
|
||||||
let callScreenCoordinator = CallScreenCoordinator(parameters: .init(roomProxy: roomProxy,
|
let callScreenCoordinator = CallScreenCoordinator(parameters: .init(roomProxy: roomProxy,
|
||||||
callBaseURL: appSettings.elementCallBaseURL,
|
callBaseURL: appSettings.elementCallBaseURL,
|
||||||
clientID: InfoPlistReader.main.bundleIdentifier))
|
clientID: InfoPlistReader.main.bundleIdentifier,
|
||||||
|
useEncryption: appSettings.elementCallUseEncryption))
|
||||||
|
|
||||||
callScreenCoordinator.actions
|
callScreenCoordinator.actions
|
||||||
.sink { [weak self] action in
|
.sink { [weak self] action in
|
||||||
|
|||||||
@@ -723,23 +723,23 @@ class ElementCallWidgetDriverMock: ElementCallWidgetDriverProtocol {
|
|||||||
|
|
||||||
//MARK: - start
|
//MARK: - start
|
||||||
|
|
||||||
var startBaseURLClientIDCallsCount = 0
|
var startBaseURLClientIDUseEncryptionCallsCount = 0
|
||||||
var startBaseURLClientIDCalled: Bool {
|
var startBaseURLClientIDUseEncryptionCalled: Bool {
|
||||||
return startBaseURLClientIDCallsCount > 0
|
return startBaseURLClientIDUseEncryptionCallsCount > 0
|
||||||
}
|
}
|
||||||
var startBaseURLClientIDReceivedArguments: (baseURL: URL, clientID: String)?
|
var startBaseURLClientIDUseEncryptionReceivedArguments: (baseURL: URL, clientID: String, useEncryption: Bool)?
|
||||||
var startBaseURLClientIDReceivedInvocations: [(baseURL: URL, clientID: String)] = []
|
var startBaseURLClientIDUseEncryptionReceivedInvocations: [(baseURL: URL, clientID: String, useEncryption: Bool)] = []
|
||||||
var startBaseURLClientIDReturnValue: Result<URL, ElementCallWidgetDriverError>!
|
var startBaseURLClientIDUseEncryptionReturnValue: Result<URL, ElementCallWidgetDriverError>!
|
||||||
var startBaseURLClientIDClosure: ((URL, String) async -> Result<URL, ElementCallWidgetDriverError>)?
|
var startBaseURLClientIDUseEncryptionClosure: ((URL, String, Bool) async -> Result<URL, ElementCallWidgetDriverError>)?
|
||||||
|
|
||||||
func start(baseURL: URL, clientID: String) async -> Result<URL, ElementCallWidgetDriverError> {
|
func start(baseURL: URL, clientID: String, useEncryption: Bool) async -> Result<URL, ElementCallWidgetDriverError> {
|
||||||
startBaseURLClientIDCallsCount += 1
|
startBaseURLClientIDUseEncryptionCallsCount += 1
|
||||||
startBaseURLClientIDReceivedArguments = (baseURL: baseURL, clientID: clientID)
|
startBaseURLClientIDUseEncryptionReceivedArguments = (baseURL: baseURL, clientID: clientID, useEncryption: useEncryption)
|
||||||
startBaseURLClientIDReceivedInvocations.append((baseURL: baseURL, clientID: clientID))
|
startBaseURLClientIDUseEncryptionReceivedInvocations.append((baseURL: baseURL, clientID: clientID, useEncryption: useEncryption))
|
||||||
if let startBaseURLClientIDClosure = startBaseURLClientIDClosure {
|
if let startBaseURLClientIDUseEncryptionClosure = startBaseURLClientIDUseEncryptionClosure {
|
||||||
return await startBaseURLClientIDClosure(baseURL, clientID)
|
return await startBaseURLClientIDUseEncryptionClosure(baseURL, clientID, useEncryption)
|
||||||
} else {
|
} else {
|
||||||
return startBaseURLClientIDReturnValue
|
return startBaseURLClientIDUseEncryptionReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//MARK: - sendMessage
|
//MARK: - sendMessage
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ struct CallScreenCoordinatorParameters {
|
|||||||
let callBaseURL: URL
|
let callBaseURL: URL
|
||||||
/// A way to identify the current client against Element Call
|
/// A way to identify the current client against Element Call
|
||||||
let clientID: String
|
let clientID: String
|
||||||
|
/// Whether encryption should be used within the call
|
||||||
|
let useEncryption: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CallScreenCoordinatorAction {
|
enum CallScreenCoordinatorAction {
|
||||||
@@ -44,7 +46,8 @@ final class CallScreenCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
viewModel = CallScreenViewModel(roomProxy: parameters.roomProxy,
|
viewModel = CallScreenViewModel(roomProxy: parameters.roomProxy,
|
||||||
callBaseURL: parameters.callBaseURL,
|
callBaseURL: parameters.callBaseURL,
|
||||||
clientID: parameters.clientID)
|
clientID: parameters.clientID,
|
||||||
|
useEncryption: parameters.useEncryption)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
|||||||
@@ -46,7 +46,10 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
|||||||
/// - roomProxy: The room in which the call should be created
|
/// - roomProxy: The room in which the call should be created
|
||||||
/// - callBaseURL: Which Element Call instance should be used
|
/// - callBaseURL: Which Element Call instance should be used
|
||||||
/// - clientID: Something to identify the current client on the Element Call side
|
/// - clientID: Something to identify the current client on the Element Call side
|
||||||
init(roomProxy: RoomProxyProtocol, callBaseURL: URL, clientID: String) {
|
init(roomProxy: RoomProxyProtocol,
|
||||||
|
callBaseURL: URL,
|
||||||
|
clientID: String,
|
||||||
|
useEncryption: Bool) {
|
||||||
self.roomProxy = roomProxy
|
self.roomProxy = roomProxy
|
||||||
self.callBaseURL = callBaseURL
|
self.callBaseURL = callBaseURL
|
||||||
self.clientID = clientID
|
self.clientID = clientID
|
||||||
@@ -99,7 +102,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
|||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
switch await widgetDriver.start(baseURL: callBaseURL, clientID: clientID) {
|
switch await widgetDriver.start(baseURL: callBaseURL, clientID: clientID, useEncryption: useEncryption) {
|
||||||
case .success(let url):
|
case .success(let url):
|
||||||
state.url = url
|
state.url = url
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ struct CallScreen: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
WebView(url: context.viewState.url, viewModelContext: context)
|
WebView(url: context.viewState.url, viewModelContext: context)
|
||||||
.navigationTitle("Call")
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
|
||||||
.ignoresSafeArea(edges: .bottom)
|
.ignoresSafeArea(edges: .bottom)
|
||||||
.presentationDragIndicator(.visible)
|
.presentationDragIndicator(.visible)
|
||||||
.environment(\.colorScheme, .dark)
|
.environment(\.colorScheme, .dark)
|
||||||
@@ -164,13 +162,14 @@ struct CallScreen_Previews: PreviewProvider {
|
|||||||
let widgetDriver = ElementCallWidgetDriverMock()
|
let widgetDriver = ElementCallWidgetDriverMock()
|
||||||
widgetDriver.underlyingMessagePublisher = .init()
|
widgetDriver.underlyingMessagePublisher = .init()
|
||||||
widgetDriver.underlyingActions = PassthroughSubject<ElementCallWidgetDriverAction, Never>().eraseToAnyPublisher()
|
widgetDriver.underlyingActions = PassthroughSubject<ElementCallWidgetDriverAction, Never>().eraseToAnyPublisher()
|
||||||
widgetDriver.startBaseURLClientIDReturnValue = .success(URL.userDirectory)
|
widgetDriver.startBaseURLClientIDUseEncryptionReturnValue = .success(URL.userDirectory)
|
||||||
|
|
||||||
roomProxy.elementCallWidgetDriverReturnValue = widgetDriver
|
roomProxy.elementCallWidgetDriverReturnValue = widgetDriver
|
||||||
|
|
||||||
return CallScreenViewModel(roomProxy: roomProxy,
|
return CallScreenViewModel(roomProxy: roomProxy,
|
||||||
callBaseURL: "https://call.element.io",
|
callBaseURL: "https://call.element.io",
|
||||||
clientID: "io.element.elementx")
|
clientID: "io.element.elementx",
|
||||||
|
useEncryption: false)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ struct RoomScreenViewState: BindableState {
|
|||||||
|
|
||||||
var ownUserID: String
|
var ownUserID: String
|
||||||
|
|
||||||
var showCallButton = false
|
|
||||||
var isCallOngoing = false
|
var isCallOngoing = false
|
||||||
|
|
||||||
var bindings: RoomScreenViewStateBindings
|
var bindings: RoomScreenViewStateBindings
|
||||||
|
|||||||
@@ -264,11 +264,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
appSettings.$readReceiptsEnabled
|
appSettings.$readReceiptsEnabled
|
||||||
.weakAssign(to: \.state.readReceiptsEnabled, on: self)
|
.weakAssign(to: \.state.readReceiptsEnabled, on: self)
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
appSettings.$elementCallEnabled
|
|
||||||
.weakAssign(to: \.state.showCallButton, on: self)
|
|
||||||
.store(in: &cancellables)
|
|
||||||
|
|
||||||
roomProxy.members
|
roomProxy.members
|
||||||
.map { members in
|
.map { members in
|
||||||
members.reduce(into: [String: RoomMemberState]()) { dictionary, member in
|
members.reduce(into: [String: RoomMemberState]()) { dictionary, member in
|
||||||
|
|||||||
@@ -162,21 +162,19 @@ struct RoomScreen: View {
|
|||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var callButton: some View {
|
private var callButton: some View {
|
||||||
if context.viewState.showCallButton {
|
if context.viewState.isCallOngoing {
|
||||||
if context.viewState.isCallOngoing {
|
Button {
|
||||||
Button {
|
context.send(viewAction: .presentCall)
|
||||||
context.send(viewAction: .presentCall)
|
} label: {
|
||||||
} label: {
|
Label(L10n.actionJoin, icon: \.videoCallSolid)
|
||||||
Label(L10n.actionJoin, icon: \.videoCallSolid)
|
.labelStyle(.titleAndIcon)
|
||||||
.labelStyle(.titleAndIcon)
|
}
|
||||||
}
|
.buttonStyle(ElementCallButtonStyle())
|
||||||
.buttonStyle(ElementCallButtonStyle())
|
} else {
|
||||||
} else {
|
Button {
|
||||||
Button {
|
context.send(viewAction: .presentCall)
|
||||||
context.send(viewAction: .presentCall)
|
} label: {
|
||||||
} label: {
|
CompoundIcon(\.videoCallSolid)
|
||||||
CompoundIcon(\.videoCallSolid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -49,8 +49,10 @@ protocol DeveloperOptionsProtocol: AnyObject {
|
|||||||
var userSuggestionsEnabled: Bool { get set }
|
var userSuggestionsEnabled: Bool { get set }
|
||||||
var readReceiptsEnabled: Bool { get set }
|
var readReceiptsEnabled: Bool { get set }
|
||||||
var swiftUITimelineEnabled: Bool { get set }
|
var swiftUITimelineEnabled: Bool { get set }
|
||||||
var elementCallEnabled: Bool { get set }
|
|
||||||
var chatBackupEnabled: Bool { get set }
|
var chatBackupEnabled: Bool { get set }
|
||||||
|
|
||||||
|
var elementCallBaseURL: URL { get set }
|
||||||
|
var elementCallUseEncryption: Bool { get set }
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppSettings: DeveloperOptionsProtocol { }
|
extension AppSettings: DeveloperOptionsProtocol { }
|
||||||
|
|||||||
+20
-4
@@ -19,6 +19,7 @@ import SwiftUI
|
|||||||
struct DeveloperOptionsScreen: View {
|
struct DeveloperOptionsScreen: View {
|
||||||
@ObservedObject var context: DeveloperOptionsScreenViewModel.Context
|
@ObservedObject var context: DeveloperOptionsScreenViewModel.Context
|
||||||
@State private var showConfetti = false
|
@State private var showConfetti = false
|
||||||
|
@State private var elementCallBaseURLString = ""
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
@@ -52,10 +53,6 @@ struct DeveloperOptionsScreen: View {
|
|||||||
Text("SwiftUI Timeline")
|
Text("SwiftUI Timeline")
|
||||||
Text("Resets on reboot")
|
Text("Resets on reboot")
|
||||||
}
|
}
|
||||||
|
|
||||||
Toggle(isOn: $context.elementCallEnabled) {
|
|
||||||
Text("Element Call")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Section("Room creation") {
|
Section("Room creation") {
|
||||||
@@ -63,6 +60,25 @@ struct DeveloperOptionsScreen: View {
|
|||||||
Text("User suggestions")
|
Text("User suggestions")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section("Element Call") {
|
||||||
|
TextField(context.elementCallBaseURL.absoluteString, text: $elementCallBaseURLString)
|
||||||
|
.submitLabel(.done)
|
||||||
|
.onSubmit {
|
||||||
|
guard let url = URL(string: elementCallBaseURLString) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
context.elementCallBaseURL = url
|
||||||
|
}
|
||||||
|
.autocorrectionDisabled(true)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
.foregroundColor(URL(string: elementCallBaseURLString) == nil ? .red : .primary)
|
||||||
|
|
||||||
|
Toggle(isOn: $context.elementCallUseEncryption) {
|
||||||
|
Text("Use encryption")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Button {
|
Button {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class ElementCallWidgetDriver: WidgetCapabilitiesProvider, ElementCallWidgetDriv
|
|||||||
self.room = room
|
self.room = room
|
||||||
}
|
}
|
||||||
|
|
||||||
func start(baseURL: URL, clientID: String) async -> Result<URL, ElementCallWidgetDriverError> {
|
func start(baseURL: URL, clientID: String, useEncryption: Bool) async -> Result<URL, ElementCallWidgetDriverError> {
|
||||||
guard let room = room as? Room else {
|
guard let room = room as? Room else {
|
||||||
return .failure(.roomInvalid)
|
return .failure(.roomInvalid)
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ class ElementCallWidgetDriver: WidgetCapabilitiesProvider, ElementCallWidgetDriv
|
|||||||
confineToRoom: true,
|
confineToRoom: true,
|
||||||
font: nil,
|
font: nil,
|
||||||
analyticsId: nil,
|
analyticsId: nil,
|
||||||
encryption: .unencrypted)) else {
|
encryption: useEncryption ? .perParticipantKeys : .unencrypted)) else {
|
||||||
return .failure(.failedBuildingWidgetSettings)
|
return .failure(.failedBuildingWidgetSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ protocol ElementCallWidgetDriverProtocol {
|
|||||||
var messagePublisher: PassthroughSubject<String, Never> { get }
|
var messagePublisher: PassthroughSubject<String, Never> { get }
|
||||||
var actions: AnyPublisher<ElementCallWidgetDriverAction, Never> { get }
|
var actions: AnyPublisher<ElementCallWidgetDriverAction, Never> { get }
|
||||||
|
|
||||||
func start(baseURL: URL, clientID: String) async -> Result<URL, ElementCallWidgetDriverError>
|
func start(baseURL: URL, clientID: String, useEncryption: Bool) async -> Result<URL, ElementCallWidgetDriverError>
|
||||||
|
|
||||||
func sendMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError>
|
func sendMessage(_ message: String) async -> Result<Bool, ElementCallWidgetDriverError>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user