mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
feat(healthchat): ring calls via gateway VoIP push
CallKit rejects the NSE hand-off without the filtering entitlement.
This commit is contained in:
@@ -15,6 +15,27 @@ import Foundation
|
||||
import Macros
|
||||
import SwiftUI
|
||||
|
||||
/// Common settings between app and NSE
|
||||
nonisolated protocol CommonSettingsProtocol: AnyObject, Sendable {
|
||||
var lastNotificationBootTime: TimeInterval? { get set }
|
||||
var selectedNotificationTone: NotificationTone? { get set }
|
||||
|
||||
var logLevel: LogLevel { get }
|
||||
var traceLogPacks: Set<TraceLogPack> { get }
|
||||
var bugReportRageshakeURL: RemotePreference<RageshakeConfiguration> { get }
|
||||
var contentScannerURL: RemotePreference<URL?> { get }
|
||||
var forceDisableE2EE: RemotePreference<Bool> { get }
|
||||
var mapTilerConfiguration: RemotePreference<MapTilerConfiguration> { get }
|
||||
|
||||
var enableOnlySignedDeviceIsolationMode: Bool { get }
|
||||
var threadsEnabled: Bool { get }
|
||||
var hideQuietNotificationAlerts: Bool { get }
|
||||
|
||||
var voipPusherAppID: String { get }
|
||||
var pushKitDeviceToken: String? { get }
|
||||
var voipPushGatewayNotifyURL: String? { get }
|
||||
}
|
||||
|
||||
nonisolated enum AppBuildType {
|
||||
case debug
|
||||
case nightly
|
||||
@@ -51,10 +72,15 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
store.reset()
|
||||
}
|
||||
|
||||
func resetSessionSpecificSettings() {
|
||||
MXLog.warning("Resetting the user session specific AppSettings.")
|
||||
resetHasRunIdentityConfirmationOnboarding()
|
||||
}
|
||||
|
||||
// MARK: - Hooks
|
||||
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
func override(accountProviders: [AccountProvider],
|
||||
func override(accountProviders: [String],
|
||||
allowOtherAccountProviders: Bool,
|
||||
hideBrandChrome: Bool,
|
||||
pushGatewayBaseURL: URL,
|
||||
@@ -115,15 +141,11 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
@UserPreference(defaultValue: true)
|
||||
var hasSeenNewSoundBanner: Bool
|
||||
|
||||
/// Defaults to `true` so new users never see it. Developer options arms it for now, an upgrade migration will at launch.
|
||||
@UserPreference(defaultValue: true)
|
||||
var hasSeenMultiAccountAnnouncement: Bool
|
||||
|
||||
/// The initial set of account providers shown to the user in the authentication flow.
|
||||
///
|
||||
/// Account provider is the friendly term for the server name. It should not contain an `https` prefix and should
|
||||
/// match the last part of the user ID. For example `example.com` and not `https://matrix.example.com`.
|
||||
private(set) var accountProviders: [AccountProvider] = [.managed(serverName: "matrix.org", baseURL: "https://matrix-client.matrix.org")]
|
||||
private(set) var accountProviders = ["matrix.org"]
|
||||
/// Whether or not the user is allowed to manually enter their own account provider or must select from one of `defaultAccountProviders`.
|
||||
private(set) var allowOtherAccountProviders = true
|
||||
/// Whether the components surrounding the app brand/logo should be hidden or not
|
||||
@@ -131,8 +153,6 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
|
||||
/// The task identifier used for background app refresh. Also used in main target's the Info.plist
|
||||
let backgroundAppRefreshTaskIdentifier = "io.element.elementx.background.refresh"
|
||||
/// The task identifier used for the search index backfill. Also used in main target's the Info.plist
|
||||
let searchBackfillTaskIdentifier = "io.element.elementx.background.search-backfill"
|
||||
|
||||
/// A URL where users can go read more about the app.
|
||||
private(set) var websiteURL: URL = "https://element.io"
|
||||
@@ -170,12 +190,8 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
@UserPreference(key: "previousServers", defaultValue: [])
|
||||
var previousServers: [String]
|
||||
|
||||
var defaultAccountProvider: AccountProvider {
|
||||
if allowOtherAccountProviders {
|
||||
previousServers.first.map { .generic($0) } ?? accountProviders[0]
|
||||
} else {
|
||||
accountProviders[0]
|
||||
}
|
||||
var defaultServer: String {
|
||||
previousServers.first ?? accountProviders[0]
|
||||
}
|
||||
|
||||
// MARK: - Security
|
||||
@@ -232,6 +248,34 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
pushGatewayBaseURL.appending(path: "_matrix/push/v1/notify")
|
||||
}
|
||||
|
||||
// HealthChat: the NSE lacks the notification filtering entitlement, so it cannot hand ringing
|
||||
// calls to CallKit directly. Instead it asks the push gateway for a PushKit (VoIP) push, the
|
||||
// way the legacy element-ios app did. The app persists what the NSE needs for that request
|
||||
// here, because the NSE neither receives PushKit credentials nor runs the app settings hook.
|
||||
|
||||
/// The push gateway app ID that delivers PushKit pushes (`push_type: voip` in Sygnal).
|
||||
var voipPusherAppID: String {
|
||||
#if DEBUG
|
||||
InfoPlistReader.main.baseBundleIdentifier + ".ios.voip.dev"
|
||||
#else
|
||||
InfoPlistReader.main.baseBundleIdentifier + ".ios.voip.prod"
|
||||
#endif
|
||||
}
|
||||
|
||||
/// The device's PushKit token, base64 encoded like the APNs pushkey.
|
||||
@UserPreference
|
||||
var pushKitDeviceToken: String?
|
||||
|
||||
/// `pushGatewayNotifyEndpoint` as configured in the app, for the NSE.
|
||||
@UserPreference
|
||||
var voipPushGatewayNotifyURL: String?
|
||||
|
||||
@UserPreference(defaultValue: true)
|
||||
var enableNotifications: Bool
|
||||
|
||||
@UserPreference(defaultValue: true)
|
||||
var enableInAppNotifications: Bool
|
||||
|
||||
@UserPreference(defaultValue: false)
|
||||
var hideQuietNotificationAlerts: Bool
|
||||
|
||||
@@ -243,9 +287,9 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
@UserPreference
|
||||
var lastNotificationBootTime: TimeInterval?
|
||||
|
||||
/// The app icon badge value the app last computed from the SDK's unread notification counts.
|
||||
@UserPreference(defaultValue: 0)
|
||||
var lastKnownBadgeCount: Int
|
||||
/// The sound played when delivering noisy notifications. If nil, use the ElementX default
|
||||
@UserPreference
|
||||
var selectedNotificationTone: NotificationTone?
|
||||
|
||||
// MARK: - Logging
|
||||
|
||||
@@ -304,6 +348,9 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
@UserPreference(defaultValue: false)
|
||||
var hasRunNotificationPermissionsOnboarding: Bool
|
||||
|
||||
@UserPreference(defaultValue: false)
|
||||
var hasRunIdentityConfirmationOnboarding: Bool
|
||||
|
||||
@UserPreference(defaultValue: false)
|
||||
var hasRequestedLocationAlwaysLocationAuthorization: Bool
|
||||
|
||||
@@ -321,6 +368,14 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
@UserPreference(defaultValue: false)
|
||||
var liveLocationDisclaimerDisplayed: Bool
|
||||
|
||||
// MARK: - Home Screen
|
||||
|
||||
@UserPreference(defaultValue: RoomListActivityVisibility.current)
|
||||
var roomListActivityVisibility: RoomListActivityVisibility
|
||||
|
||||
@UserPreference(defaultValue: false)
|
||||
var roomListNotificationCountEnabled: Bool
|
||||
|
||||
// MARK: - Room Screen
|
||||
|
||||
@UserPreference(defaultValue: AppBuildType.current == .debug)
|
||||
@@ -367,6 +422,11 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
/// The MapTiler configuration used to build map URLs, which defaults to the bundled one.
|
||||
private(set) var mapTilerConfiguration = RemotePreference(AppSettings.bundledMapTilerConfiguration)
|
||||
|
||||
// MARK: - Presence
|
||||
|
||||
@UserPreference(defaultValue: true)
|
||||
var sharePresence: Bool
|
||||
|
||||
// MARK: - Feature Flags
|
||||
|
||||
/// Others
|
||||
@@ -392,7 +452,7 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
@UserPreference(defaultValue: false)
|
||||
var messageMultiSelectEnabled: Bool
|
||||
|
||||
@UserPreference(key: "globalSearchEnabledV2", defaultValue: true)
|
||||
@UserPreference(defaultValue: ProcessInfo().isiOSAppOnMac)
|
||||
var globalSearchEnabled: Bool
|
||||
|
||||
@UserPreference(defaultValue: false)
|
||||
@@ -412,32 +472,22 @@ final nonisolated class AppSettings: @unchecked Sendable {
|
||||
@UserPreference(defaultValue: false)
|
||||
var linkNewDeviceEnabled: Bool
|
||||
|
||||
@UserPreference(defaultValue: false)
|
||||
var automaticBackPaginationEnabled: Bool
|
||||
|
||||
@UserPreference(key: "clientPausingAndResumingEnabledV2", defaultValue: false, volatile: true)
|
||||
var clientPausingAndResumingEnabled: Bool
|
||||
|
||||
@UserPreference(defaultValue: AppBuildType.current != .release)
|
||||
var developerOptionsEnabled: Bool
|
||||
|
||||
/// Runs calls through the native matrix-rust-rtc stack instead of the Element Call web view.
|
||||
@UserPreference(defaultValue: false)
|
||||
var nativeCallEnabled: Bool
|
||||
|
||||
@UserPreference(defaultValue: false)
|
||||
var multiAccountEnabled: Bool
|
||||
|
||||
/// Lets the add account flow start once `multiAccountEnabled` shows its entry points. Not a user preference, so testers
|
||||
/// can't add a second account while signing out of one isn't safe yet. Only set it to `true` locally, for development.
|
||||
let addAccountFlowEnabled = false
|
||||
|
||||
init(store: UserDefaultsProtocol) {
|
||||
self.store = store
|
||||
}
|
||||
|
||||
func userSettings(for userID: String) -> UserSettings {
|
||||
UserSettings(appSettings: self, accountSettings: .init(userID: userID, store: store))
|
||||
}
|
||||
|
||||
static func volatile() -> AppSettings {
|
||||
AppSettings(store: VolatileUserDefaults())
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated extension AppSettings: CommonSettingsProtocol { }
|
||||
|
||||
Reference in New Issue
Block a user