import CocoaLumberjackSwift
import Foundation
import Intents
import PromiseKit
import ThreemaProtocols
public final class SettingsStore: SettingsStoreInternalProtocol, SettingsStoreProtocol, ObservableObject {
// MARK: Public Attributes
public lazy var wallpaperStore: WallpaperStoreProtocol = WallpaperStore.shared
// MARK: Private Attributes
private let serverConnector: ServerConnectorProtocol
private let myIdentityStore: MyIdentityStoreProtocol
private let contactStore: ContactStoreProtocol
private let userSettings: UserSettingsProtocol
private let taskManager: TaskManagerProtocol?
// MARK: - Lifecycle
convenience init() {
self.init(
serverConnector: ServerConnector.shared(),
myIdentityStore: MyIdentityStore.shared(),
contactStore: ContactStore.shared(),
userSettings: UserSettings.shared(),
taskManager: nil
)
}
init(
serverConnector: ServerConnectorProtocol,
myIdentityStore: MyIdentityStoreProtocol,
contactStore: ContactStoreProtocol,
userSettings: UserSettingsProtocol,
taskManager: TaskManagerProtocol?
) {
self.serverConnector = serverConnector
self.myIdentityStore = myIdentityStore
self.contactStore = contactStore
self.userSettings = userSettings
self.taskManager = taskManager
// Appearance
self.displayOrderFirstName = userSettings.displayOrderFirstName
self.sortOrderFirstName = userSettings.sortOrderFirstName
self.hideStaleContacts = userSettings.hideStaleContacts
self.previewLimit = userSettings.previewLimit
self.showGalleryPreview = userSettings.showGalleryPreview
self.showProfilePictures = userSettings.showProfilePictures
self.interfaceStyle = userSettings.interfaceStyle
// Privacy Settings
self.syncContacts = userSettings.syncContacts
self.blacklist = Set<String>(userSettings.blacklist.array as? [String] ?? [])
self.syncExclusionList = userSettings.syncExclusionList
self.blockUnknown = userSettings.blockUnknown
self.allowOutgoingDonations = userSettings.allowOutgoingDonations
self.sendReadReceipts = userSettings.sendReadReceipts
self.sendTypingIndicator = userSettings.sendTypingIndicator
self.choosePOI = userSettings.enablePoi
self.hidePrivateChats = userSettings.hidePrivateChats
self.allowScreenshot = userSettings.allowScreenshot
// Notifications
self.inAppSounds = userSettings.inAppSounds
self.inAppVibrate = userSettings.inAppVibrate
self.inAppPreview = userSettings.inAppPreview
self.notificationType = NotificationType.type(for: userSettings.notificationType)
self.pushShowPreview = userSettings.pushDecrypt
self.pushSound = userSettings.pushSound
self.pushGroupSound = userSettings.pushGroupSound
self.enableMasterDnd = userSettings.enableMasterDnd
self.masterDndWorkingDays = Set<Int>(userSettings.masterDndWorkingDays.array as? [Int] ?? [])
self.masterDndStartTime = userSettings.masterDndStartTime
self.masterDndEndTime = userSettings.masterDndEndTime
// Chat
self.useBigEmojis = !userSettings.disableBigEmojis
self.sendMessageFeedback = userSettings.sendMessageFeedback
self.wallpaperType = userSettings.wallpaperType
// Media
self.imageSize = userSettings.imageSize
self.videoQuality = userSettings.videoQuality
self.autoSaveMedia = userSettings.autoSaveMedia
// Threema Calls
self.enableThreemaCall = userSettings.enableThreemaCall
self.alwaysRelayCalls = userSettings.alwaysRelayCalls
self.includeCallsInRecents = userSettings.includeCallsInRecents
self.enableVideoCall = userSettings.enableVideoCall
self.threemaVideoCallQualitySetting = userSettings.threemaVideoCallQualitySetting
self.voIPSound = userSettings.voIPSound
self.enableThreemaGroupCalls = userSettings.enableThreemaGroupCalls
// Advanced
self.enableIPv6 = userSettings.enableIPv6
self.validationLogging = userSettings.validationLogging
self.sentryAppDevice = userSettings.sentryAppDevice
self.ipcCommunicationEnabled = userSettings.ipcCommunicationEnabled
// Multi-Device
self.isMultiDeviceRegistered = userSettings.enableMultiDevice
NotificationCenter.default.addObserver(
self,
selector: #selector(incomingUpdate),
name: NSNotification.Name(rawValue: kNotificationIncomingSettingsSynchronization),
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(updateLocalValuesAsyncObjC),
name: NSNotification.Name(rawValue: kNotificationSettingStoreSynchronization),
object: nil
)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: - Published Attributes
@Published public var isSyncing = false
@Published public var syncFailed = false
@Published public var displayOrderFirstName: Bool {
didSet {
guard userSettings.displayOrderFirstName != displayOrderFirstName else {
return
}
updateUserSettingsAsync()
}
}
@Published public var sortOrderFirstName: Bool {
didSet {
guard userSettings.sortOrderFirstName != sortOrderFirstName else {
return
}
updateUserSettingsAsync()
}
}
@Published public var hideStaleContacts: Bool {
didSet {
guard userSettings.hideStaleContacts != hideStaleContacts else {
return
}
updateUserSettingsAsync()
}
}
@Published public var previewLimit: Float {
didSet {
guard userSettings.previewLimit != previewLimit else {
return
}
updateUserSettingsAsync()
}
}
@Published public var showGalleryPreview: Bool {
didSet {
guard userSettings.showGalleryPreview != showGalleryPreview else {
return
}
updateUserSettingsAsync()
}
}
@Published public var showProfilePictures: Bool {
didSet {
guard userSettings.showProfilePictures != showProfilePictures else {
return
}
updateUserSettingsAsync()
}
}
@Published public var interfaceStyle: Int {
didSet {
guard userSettings.interfaceStyle != interfaceStyle else {
return
}
updateUserSettingsAsync()
}
}
// MARK: Privacy Settings
@Published public var syncContacts: Bool {
didSet {
guard userSettings.syncContacts != syncContacts else {
return
}
syncAndSave()
}
}
@Published public var blacklist: Set<String> {
didSet {
guard Set<String>(userSettings.blacklist.array as? [String] ?? []) != blacklist else {
return
}
syncAndSave()
}
}
@Published public var syncExclusionList: [String] {
didSet {
guard userSettings.syncExclusionList != syncExclusionList else {
return
}
syncAndSave()
}
}
@Published public var blockUnknown: Bool {
didSet {
guard userSettings.blockUnknown != blockUnknown else {
return
}
syncAndSave()
}
}
@Published public var allowOutgoingDonations: Bool {
didSet {
userSettings.allowOutgoingDonations = allowOutgoingDonations
// Remove donated INInteractions when being disabled
if !allowOutgoingDonations || RemoteSecretProvider.isRemoteSecretEnabled {
removeINInteractions()
}
}
}
@Published public var sendReadReceipts: Bool {
didSet {
guard userSettings.sendReadReceipts != sendReadReceipts else {
return
}
syncAndSave()
}
}
@Published public var sendTypingIndicator: Bool {
didSet {
guard userSettings.sendTypingIndicator != sendTypingIndicator else {
return
}
syncAndSave()
}
}
@Published public var choosePOI: Bool {
didSet {
guard userSettings.enablePoi != choosePOI else {
return
}
updateUserSettingsAsync()
}
}
@Published public var hidePrivateChats: Bool {
didSet {
guard userSettings.hidePrivateChats != hidePrivateChats else {
return
}
updateUserSettingsAsync {
NotificationCenter.default.post(
name: Notification.Name(kNotificationChangedHidePrivateChat),
object: nil,
userInfo: nil
)
}
}
}
@Published public var allowScreenshot: Bool {
didSet {
guard userSettings.allowScreenshot != allowScreenshot else {
return
}
syncAndSave()
}
}
// MARK: Notifications
@Published public var inAppSounds: Bool {
didSet {
guard userSettings.inAppSounds != inAppSounds else {
return
}
updateUserSettingsAsync()
}
}
@Published public var inAppVibrate: Bool {
didSet {
guard userSettings.inAppVibrate != inAppVibrate else {
return
}
updateUserSettingsAsync()
}
}
@Published public var inAppPreview: Bool {
didSet {
guard userSettings.inAppPreview != inAppPreview else {
return
}
updateUserSettingsAsync()
}
}
@Published public var notificationType: NotificationType {
didSet {
guard userSettings.notificationType != NSNumber(integerLiteral: notificationType.userSettingsValue) else {
return
}
updateUserSettingsAsync {
// We only remove the donated Interactions, if the outgoing are disabled.
switch self.notificationType {
case .restrictive, .balanced:
if !self.allowOutgoingDonations || RemoteSecretProvider.isRemoteSecretEnabled {
self.removeINInteractions()
}
case .complete:
return
}
}
}
}
@Published public var pushShowPreview: Bool {
didSet {
guard userSettings.pushDecrypt != pushShowPreview else {
return
}
updateUserSettingsAsync()
}
}
@Published public var pushSound: String {
didSet {
guard userSettings.pushSound != pushSound else {
return
}
updateUserSettingsAsync()
}
}
@Published public var pushGroupSound: String {
didSet {
guard userSettings.pushGroupSound != pushGroupSound else {
return
}
updateUserSettingsAsync()
}
}
@Published public var enableMasterDnd: Bool {
didSet {
guard userSettings.enableMasterDnd != enableMasterDnd else {
return
}
// We automatically selects first 5 days if no selection was set
if enableMasterDnd, masterDndWorkingDays.isEmpty {
masterDndWorkingDays = preFillWorkingDays()
}
updateUserSettingsAsync()
}
}
@Published public var masterDndWorkingDays: Set<Int> {
didSet {
guard Set<Int>(userSettings.masterDndWorkingDays.array as? [Int] ?? []) != masterDndWorkingDays else {
return
}
updateUserSettingsAsync()
}
}
@Published public var masterDndStartTime: String? {
didSet {
guard userSettings.masterDndStartTime != masterDndStartTime else {
return
}
updateUserSettingsAsync()
}
}
@Published public var masterDndEndTime: String? {
didSet {
guard userSettings.masterDndEndTime != masterDndEndTime else {
return
}
updateUserSettingsAsync()
}
}
// MARK: Chats
@Published public var useBigEmojis: Bool {
didSet {
guard userSettings.disableBigEmojis == useBigEmojis else {
return
}
updateUserSettingsAsync()
}
}
@Published public var sendMessageFeedback: Bool {
didSet {
guard userSettings.sendMessageFeedback != sendMessageFeedback else {
return
}
updateUserSettingsAsync()
}
}
@Published public var wallpaperType: WallpaperType {
didSet {
guard userSettings.wallpaperType != wallpaperType else {
return
}
updateUserSettings()
switch wallpaperType {
case .empty:
wallpaperStore.saveDefaultWallpaper(nil)
case .threema:
wallpaperStore.saveDefaultWallpaper(nil)
case .custom:
break
}
}
}
// MARK: - Media
@Published public var imageSize: String {
didSet {
guard userSettings.imageSize != imageSize else {
return
}
updateUserSettingsAsync()
}
}
@Published public var videoQuality: String {
didSet {
guard userSettings.videoQuality != videoQuality else {
return
}
updateUserSettingsAsync()
}
}
@Published public var autoSaveMedia: Bool {
didSet {
guard userSettings.autoSaveMedia != autoSaveMedia else {
return
}
updateUserSettingsAsync()
}
}
// MARK: Threema Calls
@Published public var enableThreemaCall: Bool {
didSet {
guard userSettings.enableThreemaCall != enableThreemaCall else {
return
}
syncAndSave()
}
}
@Published public var alwaysRelayCalls: Bool {
didSet {
guard userSettings.alwaysRelayCalls != alwaysRelayCalls else {
return
}
syncAndSave()
}
}
@Published public var includeCallsInRecents: Bool {
didSet {
guard userSettings.includeCallsInRecents != includeCallsInRecents else {
return
}
updateUserSettingsAsync()
}
}
@Published public var enableVideoCall: Bool {
didSet {
guard userSettings.enableVideoCall != enableVideoCall else {
return
}
updateUserSettingsAsync()
}
}
@Published public var threemaVideoCallQualitySetting: ThreemaVideoCallQualitySetting {
didSet {
guard userSettings.threemaVideoCallQualitySetting != threemaVideoCallQualitySetting else {
return
}
NotificationCenter.default.post(
name: Notification.Name(kThreemaVideoCallsQualitySettingChanged),
object: nil
)
updateUserSettingsAsync()
}
}
@Published public var voIPSound: String {
didSet {
guard userSettings.voIPSound != voIPSound else {
return
}
updateUserSettingsAsync()
}
}
@Published public var enableThreemaGroupCalls: Bool {
didSet {
guard userSettings.enableThreemaGroupCalls != enableThreemaGroupCalls else {
return
}
syncAndSave()
}
}
// MARK: Multi-Device
/// Is multi-device enabled an the this device registered at the mediator?
@Published public var isMultiDeviceRegistered: Bool {
didSet {
guard userSettings.enableMultiDevice != isMultiDeviceRegistered else {
return
}
updateUserSettingsAsync()
}
}
// MARK: - Advanced
@Published public var enableIPv6: Bool {
didSet {
guard userSettings.enableIPv6 != enableIPv6 else {
return
}
updateUserSettingsAsync()
}
}
@Published public var validationLogging: Bool {
didSet {
guard userSettings.validationLogging != validationLogging else {
return
}
updateUserSettingsAsync()
}
}
@Published public var sentryAppDevice: String? {
didSet {
guard userSettings.sentryAppDevice != sentryAppDevice else {
return
}
updateUserSettingsAsync()
}
}
@Published public var ipcCommunicationEnabled: Bool {
didSet {
userSettings.ipcCommunicationEnabled = ipcCommunicationEnabled
}
}
// MARK: - Public Functions
public func flushMessageQueue() {
DDLogWarn("Manually remove current task from queue")
TaskManager.removeCurrentTask()
let tm = TaskManager()
tm.spool()
NotificationPresenterWrapper.shared.present(type: .flushMessageQueueSuccess)
}
/// Saves changed `Sync_Settings` to UserSettings and updates the SettingsStore
/// - Parameter syncSettings: Delta updates of user settings
@MainActor
func updateSettingsStore(with syncSettings: D2dSync_Settings) {
/// **IMPORTANT:**
/// Always update `UserSettings` before the local published variable
if syncSettings.hasBlockedIdentities {
let newValue = NSOrderedSet(array: syncSettings.blockedIdentities.identities)
userSettings.blacklist = newValue
blacklist = Set(syncSettings.blockedIdentities.identities)
}
if syncSettings.hasO2OCallConnectionPolicy {
let newValue = syncSettings.o2OCallConnectionPolicy == .requireRelayedConnection
userSettings.alwaysRelayCalls = newValue
alwaysRelayCalls = newValue
}
if syncSettings.hasO2OCallPolicy {
let newValue = syncSettings.o2OCallPolicy == .allowO2OCall
userSettings.enableThreemaCall = newValue
enableThreemaCall = newValue
}
if syncSettings.hasContactSyncPolicy {
let newValue = syncSettings.contactSyncPolicy == .sync
userSettings.syncContacts = newValue
syncContacts = newValue
}
if syncSettings.hasTypingIndicatorPolicy {
let newValue = syncSettings.typingIndicatorPolicy == .sendTypingIndicator
userSettings.sendTypingIndicator = newValue
sendTypingIndicator = newValue
}
if syncSettings.hasExcludeFromSyncIdentities {
userSettings.syncExclusionList = syncSettings.excludeFromSyncIdentities.identities
syncExclusionList = syncSettings.excludeFromSyncIdentities.identities
}
if syncSettings.hasUnknownContactPolicy {
let newValue = syncSettings.unknownContactPolicy == .blockUnknown
userSettings.blockUnknown = newValue
blockUnknown = newValue
}
if syncSettings.hasReadReceiptPolicy {
let newValue = syncSettings.readReceiptPolicy == .sendReadReceipt
userSettings.sendReadReceipts = newValue
sendReadReceipts = newValue
}
if syncSettings.hasGroupCallPolicy {
let newValue = syncSettings.groupCallPolicy == .allowGroupCall
userSettings.enableThreemaGroupCalls = newValue
enableThreemaGroupCalls = newValue
}
if syncSettings.hasScreenshotPolicy {
let newValue = syncSettings.screenshotPolicy == .allowScreenshot
userSettings.allowScreenshot = newValue
allowScreenshot = newValue
}
}
/// Saves made changes
/// 1. Multi-Device disabled: Just updates UserSettings
/// 2. Multi-Device enabled: Syncs all settings and updates UserSettings if sync succeeds, rejects if sync fails and
/// does not update UserSettings, shows alert
public func syncAndSave() {
guard userSettings.enableMultiDevice else {
// Save locally
Task { @MainActor in
updateUserSettings()
}
return
}
guard serverConnector.connectionState == .loggedIn else {
DDLogWarn("[SettingsStore] Not logged in.")
NotificationPresenterWrapper.shared.dismissAllPresentedNotifications()
isSyncing(false, failed: true)
return
}
guard let taskManager else {
DDLogError("[SettingsStore] TaskManager not set, reverting changes.")
discardUnsyncedChanges()
return
}
isSyncing(true, failed: false)
NotificationPresenterWrapper().presentIndefinitely(type: .settingsSyncPending)
var hasChanges = false
var syncSettings = D2dSync_Settings()
let actualBlacklist = userSettings.blacklist ?? NSOrderedSet(array: [String]())
if !actualBlacklist.isEqual(to: NSOrderedSet(array: Array(blacklist))) {
syncSettings.blockedIdentities.identities = Array(blacklist)
hasChanges = true
}
if userSettings.alwaysRelayCalls != alwaysRelayCalls {
syncSettings.o2OCallConnectionPolicy = alwaysRelayCalls ? .requireRelayedConnection : .allowDirectConnection
hasChanges = true
}
if userSettings.enableThreemaCall != enableThreemaCall {
syncSettings.o2OCallPolicy = enableThreemaCall ? .allowO2OCall : .denyO2OCall
hasChanges = true
}
if userSettings.syncContacts != syncContacts {
syncSettings.updateContactSyncPolicy(syncContacts: syncContacts)
hasChanges = true
}
if userSettings.sendTypingIndicator != sendTypingIndicator {
syncSettings
.typingIndicatorPolicy = sendTypingIndicator ? .sendTypingIndicator : .dontSendTypingIndicator
hasChanges = true
}
if userSettings.syncExclusionList != syncExclusionList {
syncSettings.excludeFromSyncIdentities.identities = syncExclusionList
hasChanges = true
}
if userSettings.blockUnknown != blockUnknown {
syncSettings.unknownContactPolicy = blockUnknown ? .blockUnknown : .allowUnknown
hasChanges = true
}
if userSettings.sendReadReceipts != sendReadReceipts {
syncSettings.readReceiptPolicy = sendReadReceipts ? .sendReadReceipt : .dontSendReadReceipt
hasChanges = true
}
if userSettings.enableThreemaGroupCalls != enableThreemaGroupCalls {
syncSettings.groupCallPolicy = enableThreemaGroupCalls ? .allowGroupCall : .denyGroupCall
hasChanges = true
}
if userSettings.allowScreenshot != allowScreenshot {
syncSettings.screenshotPolicy = allowScreenshot ? .allowScreenshot : .denyScreenshot
hasChanges = true
}
guard hasChanges else {
isSyncing(false, failed: false)
NotificationPresenterWrapper().present(type: .settingsSyncSuccess)
return
}
let task = TaskDefinitionSettingsSync(syncSettings: syncSettings)
taskManager.add(taskDefinition: task) { _, error in
guard error == nil else {
NotificationPresenterWrapper.shared.dismissAllPresentedNotifications()
self.isSyncing(false, failed: true)
self.discardUnsyncedChanges()
return
}
self.isSyncing(false, failed: false)
NotificationPresenterWrapper().present(type: .settingsSyncSuccess)
// Inform other SettingsStores
NotificationCenter.default.post(
name: NSNotification.Name(rawValue: kNotificationIncomingSettingsSynchronization),
object: nil
)
}
}
public func discardUnsyncedChanges() {
// Since the changes have not been saved yet, we simply load the user settings again
Task { @MainActor in
updateLocalValues()
}
}
/// Removes all INInteractions donated to the OS
/// - Parameter showNotification: Show a success or error notification pill
public func removeINInteractions(showNotification: Bool = false) {
INInteraction.deleteAll { error in
guard error == nil else {
DDLogError("[PrivacySettingsViewController] Could not delete INInteractions.")
if showNotification {
NotificationPresenterWrapper.shared.present(type: .interactionDeleteError)
}
return
}
if showNotification {
NotificationPresenterWrapper.shared.present(type: .interactionDeleteSuccess)
}
}
}
/// Removes all INInteractions donated to the OS
/// - Parameter showNotification: Show a success or error notification pill
public static func removeINInteractions(for managedObjectID: NSManagedObjectID) {
Task {
do {
try await INInteraction.delete(with: managedObjectID.uriRepresentation().absoluteString)
}
catch {
DDLogError("Donations for group identifier could not be deleted.")
}
}
}
/// Sync setting one to one and group call, a temporary method as long MDM sync isn't implemented
public func syncSettingCalls() {
guard userSettings.enableMultiDevice,
let taskManager else {
return
}
var syncSettings = D2dSync_Settings()
syncSettings.o2OCallPolicy = userSettings.enableThreemaCall ? .allowO2OCall : .denyO2OCall
syncSettings.groupCallPolicy = userSettings.enableThreemaGroupCalls ? .allowGroupCall : .denyGroupCall
let task = TaskDefinitionSettingsSync(syncSettings: syncSettings)
taskManager.add(taskDefinition: task) { _, error in
if let error {
DDLogError("Sync setting group call failed: \(error)")
}
}
}
// MARK: - Private Functions
@objc private func incomingUpdate() {
Task { @MainActor in
updateLocalValues()
NotificationPresenterWrapper().present(type: .settingsSyncSuccess)
}
}
private func isSyncing(_ syncing: Bool, failed: Bool) {
Task(priority: .userInitiated) { @MainActor in
isSyncing = syncing
syncFailed = failed
}
}
private func updateUserSettingsAsync(completion: (() -> Void)? = nil) {
Task { @MainActor in
updateUserSettings()
completion?()
}
}
private func updateUserSettings() {
// Appearance
compareAndAssign(&userSettings.displayOrderFirstName, displayOrderFirstName)
compareAndAssign(&userSettings.sortOrderFirstName, sortOrderFirstName)
compareAndAssign(&userSettings.hideStaleContacts, hideStaleContacts)
compareAndAssign(&userSettings.previewLimit, previewLimit)
compareAndAssign(&userSettings.showGalleryPreview, showGalleryPreview)
compareAndAssign(&userSettings.showProfilePictures, showProfilePictures)
compareAndAssign(&userSettings.interfaceStyle, interfaceStyle)
// Privacy Settings
compareAndAssign(&userSettings.syncContacts, syncContacts)
if Set<String>(userSettings.blacklist.array as? [String] ?? []) != blacklist {
compareAndAssign(&userSettings.blacklist, NSOrderedSet(array: Array(blacklist)))
}
compareAndAssign(&userSettings.blockUnknown, blockUnknown)
if userSettings.syncExclusionList != syncExclusionList {
userSettings.syncExclusionList = syncExclusionList
}
compareAndAssign(&userSettings.sendReadReceipts, sendReadReceipts)
compareAndAssign(&userSettings.sendTypingIndicator, sendTypingIndicator)
compareAndAssign(&userSettings.enablePoi, choosePOI)
compareAndAssign(&userSettings.hidePrivateChats, hidePrivateChats)
compareAndAssign(&userSettings.allowScreenshot, allowScreenshot)
// Notifications
compareAndAssign(&userSettings.inAppSounds, inAppSounds)
compareAndAssign(&userSettings.inAppVibrate, inAppVibrate)
compareAndAssign(&userSettings.inAppPreview, inAppPreview)
compareAndAssign(&userSettings.notificationType, NSNumber(integerLiteral: notificationType.userSettingsValue))
compareAndAssign(&userSettings.pushDecrypt, pushShowPreview)
compareAndAssign(&userSettings.pushSound, pushSound)
compareAndAssign(&userSettings.pushGroupSound, pushGroupSound)
compareAndAssign(&userSettings.enableMasterDnd, enableMasterDnd)
if Set<Int>(userSettings.masterDndWorkingDays.array as? [Int] ?? []) != masterDndWorkingDays {
userSettings.masterDndWorkingDays = NSOrderedSet(set: masterDndWorkingDays)
}
compareAndAssign(&userSettings.masterDndStartTime, masterDndStartTime)
compareAndAssign(&userSettings.masterDndEndTime, masterDndEndTime)
// Chat
compareAndAssign(&userSettings.disableBigEmojis, !useBigEmojis)
compareAndAssign(&userSettings.sendMessageFeedback, sendMessageFeedback)
compareAndAssign(&userSettings.wallpaperType, wallpaperType)
// Media
compareAndAssign(&userSettings.imageSize, imageSize)
compareAndAssign(&userSettings.videoQuality, videoQuality)
compareAndAssign(&userSettings.autoSaveMedia, autoSaveMedia)
// Threema Calls
compareAndAssign(&userSettings.enableThreemaCall, enableThreemaCall)
compareAndAssign(&userSettings.alwaysRelayCalls, alwaysRelayCalls)
compareAndAssign(&userSettings.includeCallsInRecents, includeCallsInRecents)
compareAndAssign(&userSettings.enableVideoCall, enableVideoCall)
compareAndAssign(&userSettings.threemaVideoCallQualitySetting, threemaVideoCallQualitySetting)
compareAndAssign(&userSettings.voIPSound, voIPSound)
compareAndAssign(&userSettings.enableThreemaGroupCalls, enableThreemaGroupCalls)
// Advanced
compareAndAssign(&userSettings.enableIPv6, enableIPv6)
compareAndAssign(&userSettings.validationLogging, validationLogging)
compareAndAssign(&userSettings.sentryAppDevice, sentryAppDevice)
// Multi-Device
compareAndAssign(&userSettings.enableMultiDevice, isMultiDeviceRegistered)
// Inform other SettingsStores
NotificationCenter.default.post(
name: NSNotification.Name(rawValue: kNotificationSettingStoreSynchronization),
object: nil
)
}
@objc private func updateLocalValuesAsyncObjC() {
Task { @MainActor in
updateLocalValues()
}
}
@MainActor
private func updateLocalValues() {
// Appearance Settings
compareAndAssign(&displayOrderFirstName, userSettings.displayOrderFirstName)
compareAndAssign(&sortOrderFirstName, userSettings.sortOrderFirstName)
compareAndAssign(&hideStaleContacts, userSettings.hideStaleContacts)
compareAndAssign(&previewLimit, userSettings.previewLimit)
compareAndAssign(&showGalleryPreview, userSettings.showGalleryPreview)
compareAndAssign(&showProfilePictures, userSettings.showProfilePictures)
compareAndAssign(&interfaceStyle, userSettings.interfaceStyle)
// Privacy Settings
compareAndAssign(&syncContacts, userSettings.syncContacts)
compareAndAssign(&blacklist, Set<String>(userSettings.blacklist.array as? [String] ?? []))
compareAndAssign(&blockUnknown, userSettings.blockUnknown)
compareAndAssign(&syncExclusionList, userSettings.syncExclusionList)
compareAndAssign(&sendReadReceipts, userSettings.sendReadReceipts)
compareAndAssign(&sendTypingIndicator, userSettings.sendTypingIndicator)
compareAndAssign(&choosePOI, userSettings.enablePoi)
compareAndAssign(&hidePrivateChats, userSettings.hidePrivateChats)
compareAndAssign(&allowScreenshot, userSettings.allowScreenshot)
// Notifications
compareAndAssign(&inAppSounds, userSettings.inAppSounds)
compareAndAssign(&inAppVibrate, userSettings.inAppVibrate)
compareAndAssign(&inAppPreview, userSettings.inAppPreview)
compareAndAssign(¬ificationType, NotificationType.type(for: userSettings.notificationType))
compareAndAssign(&pushShowPreview, userSettings.pushDecrypt)
compareAndAssign(&pushSound, userSettings.pushSound)
compareAndAssign(&pushGroupSound, userSettings.pushGroupSound)
compareAndAssign(&enableMasterDnd, userSettings.enableMasterDnd)
compareAndAssign(&masterDndWorkingDays, Set<Int>(userSettings.masterDndWorkingDays.array as? [Int] ?? []))
compareAndAssign(&masterDndStartTime, userSettings.masterDndStartTime)
compareAndAssign(&masterDndEndTime, userSettings.masterDndEndTime)
// Chat
compareAndAssign(&useBigEmojis, !userSettings.disableBigEmojis)
compareAndAssign(&sendMessageFeedback, userSettings.sendMessageFeedback)
compareAndAssign(&wallpaperType, userSettings.wallpaperType)
// Media
compareAndAssign(&userSettings.imageSize, userSettings.imageSize)
compareAndAssign(&userSettings.videoQuality, userSettings.videoQuality)
compareAndAssign(&userSettings.autoSaveMedia, userSettings.autoSaveMedia)
// Threema Calls
compareAndAssign(&enableThreemaCall, userSettings.enableThreemaCall)
compareAndAssign(&alwaysRelayCalls, userSettings.alwaysRelayCalls)
compareAndAssign(&includeCallsInRecents, userSettings.includeCallsInRecents)
compareAndAssign(&enableVideoCall, userSettings.enableVideoCall)
compareAndAssign(&threemaVideoCallQualitySetting, userSettings.threemaVideoCallQualitySetting)
compareAndAssign(&voIPSound, userSettings.voIPSound)
compareAndAssign(&enableThreemaGroupCalls, userSettings.enableThreemaGroupCalls)
// Multi-Device
compareAndAssign(&isMultiDeviceRegistered, userSettings.enableMultiDevice)
// Advanced
compareAndAssign(&enableIPv6, userSettings.enableIPv6)
compareAndAssign(&validationLogging, userSettings.validationLogging)
compareAndAssign(&sentryAppDevice, userSettings.sentryAppDevice)
}
private func compareAndAssign<T: Equatable>(_ valueToUpdate: inout T, _ comparing: T) {
if valueToUpdate != comparing {
valueToUpdate = comparing
}
}
private func preFillWorkingDays() -> Set<Int> {
var days = Set<Int>()
for index in 2...6 {
days.insert(index)
}
return days
}
}
¤ Dauer der Verarbeitung: 0.11 Sekunden
¤
*© Formatika GbR, Deutschland
|
|