Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  SettingsStore.swift   Sprache: JAVA

 

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
    }
}

Messung
CHZG
909510095

¤ Dauer der Verarbeitung: 0.5 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104850
#Domains=416975