Files
wehub-resource-sync 1d1286fadb
Build / Build and test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:18:38 +08:00

131 lines
3.8 KiB
Swift

import Foundation
import MacToolsPluginKit
// MARK: - FanControlPresetStore
/// Manages fan presets (built-in + user-created) with persistence via PluginStorage.
@MainActor
final class FanControlPresetStore: ObservableObject {
// MARK: - Storage Keys
private enum Key {
static let customPresets = "custom-presets"
static let activePresetID = "active-preset-id"
}
// MARK: - Built-in Presets
static let builtInPresets: [FanPreset] = [
FanPreset(
id: FanPresetBuiltInID.auto,
name: "",
strategy: .auto,
isBuiltIn: true
),
FanPreset(
id: FanPresetBuiltInID.fullSpeed,
name: "",
strategy: .fullSpeed,
isBuiltIn: true
),
]
// MARK: - State
private let storage: PluginStorage
private let localization: PluginLocalization
@Published private(set) var customPresets: [FanPreset] = []
@Published private(set) var activePresetID: String = FanPresetBuiltInID.auto
var allPresets: [FanPreset] {
Self.builtInPresets + customPresets
}
var activePreset: FanPreset {
allPresets.first(where: { $0.id == activePresetID })
?? Self.builtInPresets[0]
}
// MARK: - Init
init(
storage: PluginStorage,
localization: PluginLocalization = PluginLocalization(bundle: .main)
) {
self.storage = storage
self.localization = localization
load()
}
// MARK: - Persistence
private func load() {
if let data = storage.data(forKey: Key.customPresets),
let decoded = try? JSONDecoder().decode([FanPreset].self, from: data) {
customPresets = decoded
}
if let saved = storage.string(forKey: Key.activePresetID) {
activePresetID = saved
}
// Validate active ID still exists
if !allPresets.contains(where: { $0.id == activePresetID }) {
activePresetID = FanPresetBuiltInID.auto
}
}
private func saveCustomPresets() {
if let data = try? JSONEncoder().encode(customPresets) {
storage.set(data, forKey: Key.customPresets)
}
}
private func saveActivePresetID() {
storage.set(activePresetID, forKey: Key.activePresetID)
}
// MARK: - CRUD
func setActivePreset(id: String) {
guard allPresets.contains(where: { $0.id == id }) else { return }
activePresetID = id
saveActivePresetID()
}
func addCustomPreset() -> FanPreset {
let index = customPresets.count + 1
let preset = FanPreset(
id: UUID().uuidString,
name: localization.format("preset.custom.defaultName", defaultValue: "自定义预设 %d", index),
strategy: .fixed(rpm: FanRPMLimits.absoluteMin),
isBuiltIn: false
)
customPresets.append(preset)
saveCustomPresets()
return preset
}
func updateCustomPresetRPM(id: String, rpm: Int) {
guard let idx = customPresets.firstIndex(where: { $0.id == id }) else { return }
let clamped = max(FanRPMLimits.absoluteMin, min(FanRPMLimits.absoluteMax, rpm))
customPresets[idx].strategy = .fixed(rpm: clamped)
saveCustomPresets()
}
func renameCustomPreset(id: String, newName: String) {
guard let idx = customPresets.firstIndex(where: { $0.id == id }) else { return }
let trimmed = newName.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmed.isEmpty else { return }
customPresets[idx].name = trimmed
saveCustomPresets()
}
func deleteCustomPreset(id: String) {
customPresets.removeAll(where: { $0.id == id })
if activePresetID == id {
activePresetID = FanPresetBuiltInID.auto
saveActivePresetID()
}
saveCustomPresets()
}
}