Ariver
2026-06-29 1d03f5b6b433b062a098bf7899a349d550aafac7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import Foundation
 
public enum PreferenceKey: String, CaseIterable, Equatable, Sendable {
    case schemaVersion = "schemaVersion"
    case showMinimizedWindows = "quickSwitch.showMinimizedWindows"
    case showFullscreenWindows = "quickSwitch.showFullscreenWindows"
    case quickSwitchWaterfallViewMode = "quickSwitch.waterfallViewMode"
    case theme = "appearance.theme"
    case language = "appearance.language"
    case spaceDisplayStrategy = "spaces.displayStrategy"
 
    public var storageKey: String {
        "com.ar.Aligner.preferences.\(rawValue)"
    }
}
 
public enum ThemePreference: String, CaseIterable, Equatable, Sendable {
    case system
    case light
    case dark
}
 
public enum LanguagePreference: String, CaseIterable, Equatable, Sendable {
    case system
    case english
    case simplifiedChinese
}
 
public enum SpaceDisplayStrategy: String, CaseIterable, Equatable, Sendable {
    case includeVisibleAndFullscreen
    case currentSpaceOnly
}
 
public enum QuickSwitchWaterfallViewMode: String, CaseIterable, Equatable, Sendable {
    case verticalColumns
    case horizontalMasonry
}
 
public struct AlignerPreferences: Equatable, Sendable {
    public let showMinimizedWindows: Bool
    public let showFullscreenWindows: Bool
    public let quickSwitchWaterfallViewMode: QuickSwitchWaterfallViewMode
    public let theme: ThemePreference
    public let language: LanguagePreference
    public let spaceDisplayStrategy: SpaceDisplayStrategy
 
    public init(
        showMinimizedWindows: Bool,
        showFullscreenWindows: Bool,
        quickSwitchWaterfallViewMode: QuickSwitchWaterfallViewMode,
        theme: ThemePreference,
        language: LanguagePreference,
        spaceDisplayStrategy: SpaceDisplayStrategy
    ) {
        self.showMinimizedWindows = showMinimizedWindows
        self.showFullscreenWindows = showFullscreenWindows
        self.quickSwitchWaterfallViewMode = quickSwitchWaterfallViewMode
        self.theme = theme
        self.language = language
        self.spaceDisplayStrategy = spaceDisplayStrategy
    }
}
 
public enum PreferenceSchema {
    public static let version = 2
 
    public static let defaults = AlignerPreferences(
        showMinimizedWindows: true,
        showFullscreenWindows: true,
        quickSwitchWaterfallViewMode: .verticalColumns,
        theme: .system,
        language: .system,
        spaceDisplayStrategy: .includeVisibleAndFullscreen
    )
 
    public static var registrationDictionary: [String: Any] {
        [
            PreferenceKey.schemaVersion.storageKey: version,
            PreferenceKey.showMinimizedWindows.storageKey: defaults.showMinimizedWindows,
            PreferenceKey.showFullscreenWindows.storageKey: defaults.showFullscreenWindows,
            PreferenceKey.quickSwitchWaterfallViewMode.storageKey: defaults.quickSwitchWaterfallViewMode.rawValue,
            PreferenceKey.theme.storageKey: defaults.theme.rawValue,
            PreferenceKey.language.storageKey: defaults.language.rawValue,
            PreferenceKey.spaceDisplayStrategy.storageKey: defaults.spaceDisplayStrategy.rawValue
        ]
    }
}
 
public protocol PreferenceStoreProtocol {
    func registerDefaults()
    func storedSchemaVersion() -> Int
    func needsMigration() -> Bool
    func read() -> AlignerPreferences
    func write(_ preferences: AlignerPreferences)
    func reset()
}
 
public final class UserDefaultsPreferenceStore: PreferenceStoreProtocol {
    private let userDefaults: UserDefaults
 
    public init(userDefaults: UserDefaults = .standard) {
        self.userDefaults = userDefaults
    }
 
    public func registerDefaults() {
        userDefaults.register(defaults: PreferenceSchema.registrationDictionary)
    }
 
    public func storedSchemaVersion() -> Int {
        registerDefaults()
        return userDefaults.integer(forKey: PreferenceKey.schemaVersion.storageKey)
    }
 
    public func needsMigration() -> Bool {
        storedSchemaVersion() < PreferenceSchema.version
    }
 
    public func read() -> AlignerPreferences {
        registerDefaults()
 
        return AlignerPreferences(
            showMinimizedWindows: userDefaults.bool(forKey: PreferenceKey.showMinimizedWindows.storageKey),
            showFullscreenWindows: userDefaults.bool(forKey: PreferenceKey.showFullscreenWindows.storageKey),
            quickSwitchWaterfallViewMode: rawValue(
                for: .quickSwitchWaterfallViewMode,
                default: PreferenceSchema.defaults.quickSwitchWaterfallViewMode
            ),
            theme: rawValue(for: .theme, default: PreferenceSchema.defaults.theme),
            language: rawValue(for: .language, default: PreferenceSchema.defaults.language),
            spaceDisplayStrategy: rawValue(
                for: .spaceDisplayStrategy,
                default: PreferenceSchema.defaults.spaceDisplayStrategy
            )
        )
    }
 
    public func write(_ preferences: AlignerPreferences) {
        userDefaults.set(PreferenceSchema.version, forKey: PreferenceKey.schemaVersion.storageKey)
        userDefaults.set(preferences.showMinimizedWindows, forKey: PreferenceKey.showMinimizedWindows.storageKey)
        userDefaults.set(preferences.showFullscreenWindows, forKey: PreferenceKey.showFullscreenWindows.storageKey)
        userDefaults.set(
            preferences.quickSwitchWaterfallViewMode.rawValue,
            forKey: PreferenceKey.quickSwitchWaterfallViewMode.storageKey
        )
        userDefaults.set(preferences.theme.rawValue, forKey: PreferenceKey.theme.storageKey)
        userDefaults.set(preferences.language.rawValue, forKey: PreferenceKey.language.storageKey)
        userDefaults.set(preferences.spaceDisplayStrategy.rawValue, forKey: PreferenceKey.spaceDisplayStrategy.storageKey)
    }
 
    public func reset() {
        for key in PreferenceKey.allCases {
            userDefaults.removeObject(forKey: key.storageKey)
        }
        registerDefaults()
    }
 
    private func rawValue<T: RawRepresentable>(for key: PreferenceKey, default defaultValue: T) -> T where T.RawValue == String {
        guard let value = userDefaults.string(forKey: key.storageKey) else {
            return defaultValue
        }
 
        return T(rawValue: value) ?? defaultValue
    }
}