mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
Move any visible account settings into AccountSettings. (#6308)
* Remove the now unused setting to disable in-app notifications. * Move visible account settings into AccountSettings. Have ignored enableNotifications for now as that will require reworking the NotificationManager. * Allow different sounds to be selected for each account. * Move defaultNotificationTone into AccountSettings.
This commit is contained in:
@@ -43,6 +43,9 @@ nonisolated struct NotificationTone: Hashable, Comparable, Codable {
|
||||
case appLibrary
|
||||
}
|
||||
|
||||
/// The default bundled message tone.
|
||||
static let `default`: NotificationTone = .createBundledSound(label: L10n.screenNotificationSettingsSoundElementDefault, filename: "message.caf")
|
||||
|
||||
/// Creates a tone backed by a file in the system sounds directory.
|
||||
static func createSystemSound(label: String?, filename: String, systemSoundsSubdirectory: [String] = []) -> NotificationTone {
|
||||
NotificationTone(labelOverride: label, storageLocationRoot: .system, relativePath: systemSoundsSubdirectory + [filename])
|
||||
|
||||
@@ -31,26 +31,40 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
||||
|
||||
private let userSettings: UserSettings
|
||||
|
||||
/// The default Element X bundled message tone.
|
||||
static let defaultElementXMessageTone: NotificationTone = .createBundledSound(label: L10n.screenNotificationSettingsSoundElementDefault,
|
||||
filename: "message.caf")
|
||||
|
||||
/// All default tones (system + Element X), sorted by name.
|
||||
static let allDefaultAlerts: [NotificationTone] = (defaultSystemAlerts + defaultElementXAlerts).sorted()
|
||||
static let allDefaultAlerts: [NotificationTone] = (defaultSystemAlerts + defaultBundledAlerts).sorted()
|
||||
|
||||
/// Filename of the active tone file used by the notification service.
|
||||
static let selectedToneFilename = "currentAlert.caf"
|
||||
/// The name the notification service uses to find the tone's file in the app bundle or Library/Sounds.
|
||||
///
|
||||
/// Note: Names with a subdirectory aren't documented for `UNNotificationSound(named:)` but do work.
|
||||
static func soundName(for tone: NotificationTone) -> String {
|
||||
switch tone.storageLocationRoot {
|
||||
case .appBundle:
|
||||
tone.filename
|
||||
case .appLibrary:
|
||||
"\(URL.customTonesDirectory.lastPathComponent)/\(tone.filename)"
|
||||
case .system:
|
||||
"\(URL.systemTonesDirectory.lastPathComponent)/\(tone.filename)"
|
||||
}
|
||||
}
|
||||
|
||||
/// Directory where user-imported custom tones are stored.
|
||||
nonisolated static let libraryLocation = URL.libraryDirectory.appending(components: "Sounds", "AvailableSounds", directoryHint: .isDirectory)
|
||||
/// Copies a system tone into Library/Sounds, as the system sounds directory isn't searched for notification sounds.
|
||||
static func copySystemToneIfNeeded(_ tone: NotificationTone) throws {
|
||||
guard tone.storageLocationRoot == .system else { return }
|
||||
|
||||
let copyLocation = URL.systemTonesDirectory.appending(component: tone.filename)
|
||||
guard (try? copyLocation.checkResourceIsReachable()) != true else { return }
|
||||
|
||||
try FileManager.default.createDirectory(at: URL.systemTonesDirectory, withIntermediateDirectories: true)
|
||||
try FileManager.default.copyItem(at: toneLocation(for: tone), to: copyLocation)
|
||||
}
|
||||
|
||||
/// Creates the manager and ensures required library directories exist.
|
||||
init(userSettings: UserSettings) {
|
||||
self.userSettings = userSettings
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: NotificationToneManager.libraryLocation, withIntermediateDirectories: true)
|
||||
try FileManager.default.createDirectory(at: Self.selectedToneLocation.deletingLastPathComponent(), withIntermediateDirectories: true)
|
||||
try FileManager.default.createDirectory(at: URL.customTonesDirectory, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
// Don't crash on a recoverable file system error. The underlying problem (directory creation
|
||||
// failing on launch, e.g. a background launch before the container is writable) is acknowledged
|
||||
@@ -60,29 +74,17 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
||||
}
|
||||
|
||||
/// Sets the given tone as the active notification alert tone.
|
||||
///
|
||||
/// Copies the tone's audio file to `selectedToneLocation` and persists the selection in app settings.
|
||||
func setSelectedTone(_ alertTone: NotificationTone) throws -> URL {
|
||||
do {
|
||||
try? FileManager.default.removeItem(at: Self.selectedToneLocation)
|
||||
let toneLocation = Self.toneLocation(for: alertTone)
|
||||
try FileManager.default.copyItem(at: toneLocation, to: Self.selectedToneLocation)
|
||||
userSettings.app.selectedNotificationTone = alertTone
|
||||
return Self.selectedToneLocation
|
||||
} catch {
|
||||
if (try? Self.selectedToneLocation.checkResourceIsReachable()) != true {
|
||||
// make sure the selected tone is reset if there's no custom tone present
|
||||
userSettings.app.selectedNotificationTone = nil
|
||||
}
|
||||
throw error
|
||||
}
|
||||
try Self.copySystemToneIfNeeded(alertTone)
|
||||
userSettings.account.selectedNotificationTone = alertTone
|
||||
return Self.toneLocation(for: alertTone)
|
||||
}
|
||||
|
||||
/// Returns all user-imported CAF tones from the library directory, sorted by name.
|
||||
func customTones() -> [NotificationTone] {
|
||||
let availableFiles = try? FileManager
|
||||
.default
|
||||
.contentsOfDirectory(at: NotificationToneManager.libraryLocation, includingPropertiesForKeys: nil)
|
||||
.contentsOfDirectory(at: URL.customTonesDirectory, includingPropertiesForKeys: nil)
|
||||
|
||||
return (availableFiles ?? [])
|
||||
.compactMap {
|
||||
@@ -100,7 +102,7 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
||||
@discardableResult
|
||||
func addNewToneToLibrary(from sourceURL: URL) throws -> URL {
|
||||
let baseName = sourceURL.deletingPathExtension().lastPathComponent
|
||||
let outputURL = NotificationToneManager.libraryLocation.appending(component: baseName).appendingPathExtension("caf")
|
||||
let outputURL = URL.customTonesDirectory.appending(component: baseName).appendingPathExtension("caf")
|
||||
|
||||
guard (try? outputURL.checkResourceIsReachable()) != true else {
|
||||
throw ManagerError.fileAlreadyExists
|
||||
@@ -119,7 +121,7 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
||||
/// - Throws: `DeletionError.notACustomTone` if the tone is not stored in the library directory.
|
||||
func deleteCustomTone(_ alertTone: NotificationTone) throws {
|
||||
let toneLocation = Self.toneLocation(for: alertTone)
|
||||
guard toneLocation.deletingLastPathComponent() == NotificationToneManager.libraryLocation else {
|
||||
guard toneLocation.deletingLastPathComponent() == URL.customTonesDirectory else {
|
||||
throw ManagerError.notACustomTone
|
||||
}
|
||||
|
||||
@@ -172,9 +174,6 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
||||
MXLog.info("Converted \(sourceURL.path(percentEncoded: false)) to caf")
|
||||
}
|
||||
|
||||
/// File URL of the active tone copied/linked for use by the system.
|
||||
private static let selectedToneLocation = NotificationToneManager.libraryLocation.deletingLastPathComponent().appending(component: selectedToneFilename)
|
||||
|
||||
/// Pre-defined iOS system tones available for selection, sorted by name.
|
||||
private static let defaultSystemAlerts: [NotificationTone] = [
|
||||
.createSystemSound(label: L10n.screenNotificationSettingsSoundSystemTriToneIos,
|
||||
@@ -256,9 +255,9 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
||||
}
|
||||
.sorted()
|
||||
|
||||
/// Element X bundled tones available for selection, sorted by name.
|
||||
private static let defaultElementXAlerts: [NotificationTone] = [
|
||||
defaultElementXMessageTone,
|
||||
/// Bundled tones available for selection, sorted by name.
|
||||
private static let defaultBundledAlerts: [NotificationTone] = [
|
||||
.default,
|
||||
.createBundledSound(label: L10n.screenNotificationSettingsSoundElementFade,
|
||||
filename: "sound_01.caf")
|
||||
].sorted()
|
||||
@@ -280,7 +279,7 @@ nonisolated struct NotificationToneManager: NotificationToneManagerProtocol {
|
||||
case .appBundle:
|
||||
root = Self.bundledLocation
|
||||
case .appLibrary:
|
||||
root = Self.libraryLocation
|
||||
root = URL.customTonesDirectory
|
||||
}
|
||||
|
||||
return tone.relativePath.reduce(root) {
|
||||
|
||||
@@ -235,9 +235,6 @@ final class NotificationManager: NSObject, NotificationManagerProtocol {
|
||||
extension NotificationManager: UNUserNotificationCenterDelegate {
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
||||
willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
|
||||
guard appSettings.enableInAppNotifications else {
|
||||
return []
|
||||
}
|
||||
guard let delegate else {
|
||||
return [.badge, .sound, .list, .banner]
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ final class PresenceService {
|
||||
}
|
||||
|
||||
private var desiredPresence: ClientProxyPresence {
|
||||
switch (isForegroundActive, userSettings.app.sharePresence) {
|
||||
switch (isForegroundActive, userSettings.account.sharePresence) {
|
||||
case (true, true):
|
||||
.online
|
||||
case (true, false):
|
||||
@@ -68,7 +68,7 @@ final class PresenceService {
|
||||
}
|
||||
|
||||
private func observeSharePresence() {
|
||||
userSettings.app.sharePresencePublisher
|
||||
userSettings.account.sharePresencePublisher
|
||||
.removeDuplicates()
|
||||
.sink { [weak self] _ in
|
||||
self?.reportCurrentState()
|
||||
|
||||
@@ -128,7 +128,7 @@ class TimelineController: TimelineControllerProtocol {
|
||||
}
|
||||
|
||||
func sendReadReceipt(for itemID: TimelineItemIdentifier) async {
|
||||
let receiptType: MatrixRustSDK.ReceiptType = userSettings.app.sharePresence ? .read : .readPrivate
|
||||
let receiptType: MatrixRustSDK.ReceiptType = userSettings.account.sharePresence ? .read : .readPrivate
|
||||
|
||||
guard let eventID = itemID.eventID else {
|
||||
return
|
||||
|
||||
@@ -57,7 +57,8 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
return .failure(.unknownAccount)
|
||||
}
|
||||
|
||||
let userSettings = appSettings.userSettings(for: credentials.userID)
|
||||
let userSettings = appHooks.userSessionHook.configure(appSettings.userSettings(for: credentials.userID),
|
||||
forHomeserver: credentials.restorationToken.session.homeserverUrl)
|
||||
|
||||
switch await restorePreviousLogin(credentials, userSettings: userSettings) {
|
||||
case .success(let clientProxy):
|
||||
@@ -81,7 +82,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
||||
do {
|
||||
let session = try client.session()
|
||||
let userID = try client.userId()
|
||||
let userSettings = appSettings.userSettings(for: userID)
|
||||
let userSettings = appHooks.userSessionHook.configure(appSettings.userSettings(for: userID), forHomeserver: client.homeserver())
|
||||
let clientProxy = try await setupProxyForClient(client, userSettings: userSettings)
|
||||
|
||||
keychainController.setRestorationToken(RestorationToken(session: session,
|
||||
|
||||
Reference in New Issue
Block a user