From 4ea8490f687e41e852de689ffdaffb2654b0f2e2 Mon Sep 17 00:00:00 2001 From: LMZ Date: Tue, 21 Apr 2026 16:54:23 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20remove=20redundant=20onChange=20handlers?= =?UTF-8?q?=20=E2=80=94=20side=20effects=20now=20in=20ViewModel=20didSet?= =?UTF-8?q?=20only=20(issue=20#32)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Behavior was already consistent (all didSets existed in MonitorViewModel) but Views.swift had redundant onChange handlers that duplicated didSet work. SettingsView had none for temperatureMode — not a bug since didSet covers it, but the inconsistency in where side effects lived was the drift risk. Changes (all behavioral no-ops): - Views.swift: remove redundant .onChange for temperatureMode and processLimit - SettingsView.swift: remove redundant .onChange for processLimit Canonical side effects remain in MonitorViewModel didSet: - temperatureMode.didSet → refresh(forceProcesses: true) - processLimit.didSet → recomputeVisibleProcesses() - menuBarDisplayMode.didSet → refresh(forceProcesses: false) Result: both views share the same didSet — future settings changes cannot accidentally diverge without touching MonitorViewModel. --- .../ProcessBarMonitor/MonitorViewModel.swift | 19 ++++++++++++++++--- Sources/ProcessBarMonitor/SettingsView.swift | 4 +--- Sources/ProcessBarMonitor/Views.swift | 8 ++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/Sources/ProcessBarMonitor/MonitorViewModel.swift b/Sources/ProcessBarMonitor/MonitorViewModel.swift index 6d0324a..8514961 100644 --- a/Sources/ProcessBarMonitor/MonitorViewModel.swift +++ b/Sources/ProcessBarMonitor/MonitorViewModel.swift @@ -39,13 +39,26 @@ final class MonitorViewModel: ObservableObject { @Published var searchText = "" @Published var processLimit: Int { - didSet { settings.set(processLimit, forKey: Keys.processLimit) } + didSet { + settings.set(processLimit, forKey: Keys.processLimit) + // Immediately reflect the new row count in the visible process list. + recomputeVisibleProcesses() + } } @Published var temperatureMode: TemperatureMode { - didSet { settings.set(temperatureMode.rawValue, forKey: Keys.temperatureMode) } + didSet { + settings.set(temperatureMode.rawValue, forKey: Keys.temperatureMode) + // Immediate refresh ensures the new temperature mode is reflected + // without waiting for the next scheduled refresh cycle. + Task { await refresh(forceProcesses: true) } + } } @Published var menuBarDisplayMode: MenuBarDisplayMode { - didSet { settings.set(menuBarDisplayMode.rawValue, forKey: Keys.menuBarDisplayMode) } + didSet { + settings.set(menuBarDisplayMode.rawValue, forKey: Keys.menuBarDisplayMode) + // Refresh so the menu bar title format update is immediate. + Task { await refresh(forceProcesses: false) } + } } private let metricsProvider = SystemMetricsProvider() diff --git a/Sources/ProcessBarMonitor/SettingsView.swift b/Sources/ProcessBarMonitor/SettingsView.swift index 9c8b755..493d0f6 100644 --- a/Sources/ProcessBarMonitor/SettingsView.swift +++ b/Sources/ProcessBarMonitor/SettingsView.swift @@ -24,9 +24,7 @@ struct SettingsView: View { Text("12").tag(12) Text("20").tag(20) } - .onChange(of: viewModel.processLimit) { _ in - viewModel.recomputeVisibleProcesses() - } + // Side-effect (recomputeVisibleProcesses) is in MonitorViewModel.processLimit.didSet } header: { Text(L10n.string("settings.section.display")) } diff --git a/Sources/ProcessBarMonitor/Views.swift b/Sources/ProcessBarMonitor/Views.swift index 2ea468c..8e83dc2 100644 --- a/Sources/ProcessBarMonitor/Views.swift +++ b/Sources/ProcessBarMonitor/Views.swift @@ -231,9 +231,7 @@ struct MenuBarContentView: View { Text(mode.title).tag(mode) } } - .onChange(of: viewModel.temperatureMode) { _ in - Task { await viewModel.refresh(forceProcesses: true) } - } + // Refresh side-effect is in MonitorViewModel.temperatureMode.didSet TextField(L10n.string("search.placeholder"), text: $viewModel.searchText) .textFieldStyle(.roundedBorder) @@ -248,9 +246,7 @@ struct MenuBarContentView: View { Text("20").tag(20) } .pickerStyle(.segmented) - .onChange(of: viewModel.processLimit) { _ in - viewModel.recomputeVisibleProcesses() - } + // Side-effect (recomputeVisibleProcesses) is in MonitorViewModel.processLimit.didSet } Toggle(L10n.string("toggle.launch_at_login"), isOn: Binding(