diff --git a/Sources/Fluid/ContentView.swift b/Sources/Fluid/ContentView.swift index 2410fd45..721e399c 100644 --- a/Sources/Fluid/ContentView.swift +++ b/Sources/Fluid/ContentView.swift @@ -1119,7 +1119,14 @@ struct ContentView: View { shortcuts.append(shortcut) } } - self.primaryDictationShortcuts = shortcuts + self.updatePrimaryDictationShortcuts(shortcuts) + } + + private func updatePrimaryDictationShortcuts(_ shortcuts: [HotkeyShortcut]) { + SettingsStore.shared.primaryDictationShortcuts = shortcuts + let storedShortcuts = SettingsStore.shared.primaryDictationShortcuts + self.primaryDictationShortcuts = storedShortcuts + self.hotkeyManager?.updatePrimaryShortcuts(storedShortcuts) } private func setShortcutTargetEnabled(_ enabled: Bool, for target: ShortcutRecordingTarget) { @@ -1464,7 +1471,10 @@ struct ContentView: View { inputDevices: self.$inputDevices, outputDevices: self.$outputDevices, accessibilityEnabled: self.$accessibilityEnabled, - primaryDictationShortcuts: self.$primaryDictationShortcuts, + primaryDictationShortcuts: Binding( + get: { self.primaryDictationShortcuts }, + set: { self.updatePrimaryDictationShortcuts($0) } + ), activeShortcutRecordingTarget: self.$activeShortcutRecordingTarget, shortcutRecordingMessage: self.$shortcutRecordingMessage, commandModeShortcut: self.$commandModeHotkeyShortcut, diff --git a/Sources/Fluid/Persistence/SettingsStore.swift b/Sources/Fluid/Persistence/SettingsStore.swift index e786893f..52dcbaed 100644 --- a/Sources/Fluid/Persistence/SettingsStore.swift +++ b/Sources/Fluid/Persistence/SettingsStore.swift @@ -1669,7 +1669,7 @@ final class SettingsStore: ObservableObject { } set { objectWillChange.send() - let shortcuts = Self.normalizedPrimaryDictationShortcuts([newValue], fallback: Self.defaultPrimaryDictationShortcut) + let shortcuts = Self.normalizedPrimaryDictationShortcuts([newValue]) self.storePrimaryDictationShortcuts(shortcuts) self.storeLegacyHotkeyShortcut(shortcuts[0]) } @@ -1680,7 +1680,7 @@ final class SettingsStore: ObservableObject { .map(\.displayString) .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } - return displays.isEmpty ? Self.defaultPrimaryDictationShortcut.displayString : displays.joined(separator: " / ") + return displays.joined(separator: " / ") } var primaryDictationShortcuts: [HotkeyShortcut] { @@ -1689,15 +1689,17 @@ final class SettingsStore: ObservableObject { if let data = defaults.data(forKey: Keys.primaryDictationShortcutsKey), let shortcuts = try? JSONDecoder().decode([HotkeyShortcut].self, from: data) { - return Self.normalizedPrimaryDictationShortcuts(shortcuts, fallback: fallback) + return Self.normalizedPrimaryDictationShortcuts(shortcuts) } return [fallback] } set { objectWillChange.send() - let shortcuts = Self.normalizedPrimaryDictationShortcuts(newValue, fallback: self.legacyHotkeyShortcut) + let shortcuts = Self.normalizedPrimaryDictationShortcuts(newValue) self.storePrimaryDictationShortcuts(shortcuts) - self.storeLegacyHotkeyShortcut(shortcuts[0]) + if let firstShortcut = shortcuts.first { + self.storeLegacyHotkeyShortcut(firstShortcut) + } } } @@ -1714,17 +1716,11 @@ final class SettingsStore: ObservableObject { return Self.defaultPrimaryDictationShortcut } - private static func normalizedPrimaryDictationShortcuts( - _ shortcuts: [HotkeyShortcut], - fallback: HotkeyShortcut - ) -> [HotkeyShortcut] { + private static func normalizedPrimaryDictationShortcuts(_ shortcuts: [HotkeyShortcut]) -> [HotkeyShortcut] { var unique: [HotkeyShortcut] = [] for shortcut in shortcuts where !unique.contains(shortcut) { unique.append(shortcut) } - if unique.isEmpty { - unique.append(fallback) - } return unique } diff --git a/Sources/Fluid/UI/SettingsView.swift b/Sources/Fluid/UI/SettingsView.swift index 115ebc21..49706115 100644 --- a/Sources/Fluid/UI/SettingsView.swift +++ b/Sources/Fluid/UI/SettingsView.swift @@ -2151,14 +2151,12 @@ struct SettingsView: View { .disabled(!isRecording && self.isRecordingAnyShortcut) Button("Remove") { - guard self.primaryDictationShortcuts.count > 1, - self.primaryDictationShortcuts.indices.contains(index) - else { return } + guard self.primaryDictationShortcuts.indices.contains(index) else { return } self.primaryDictationShortcuts.remove(at: index) } .buttonStyle(.bordered) .controlSize(.small) - .disabled(self.primaryDictationShortcuts.count <= 1 || self.isRecordingAnyShortcut) + .disabled(self.isRecordingAnyShortcut) if isRecording, let recordingMessage = self.shortcutRecordingMessage, diff --git a/Tests/FluidDictationIntegrationTests/HotkeyShortcutTests.swift b/Tests/FluidDictationIntegrationTests/HotkeyShortcutTests.swift index 65bd2b5e..cbfc1f8e 100644 --- a/Tests/FluidDictationIntegrationTests/HotkeyShortcutTests.swift +++ b/Tests/FluidDictationIntegrationTests/HotkeyShortcutTests.swift @@ -159,6 +159,18 @@ final class HotkeyShortcutTests: XCTestCase { } } + func testPrimaryDictationShortcutsCanBeExplicitlyCleared() throws { + try self.withRestoredDefaults(keys: [self.legacyHotkeyShortcutKey, self.primaryDictationShortcutsKey]) { + let legacyShortcut = HotkeyShortcut(keyCode: 12, modifierFlags: [.option]) + SettingsStore.shared.hotkeyShortcut = legacyShortcut + + SettingsStore.shared.primaryDictationShortcuts = [] + + XCTAssertEqual(SettingsStore.shared.primaryDictationShortcuts, []) + XCTAssertEqual(SettingsStore.shared.primaryDictationShortcutDisplayString, "") + } + } + func testPasteLastTranscriptionShortcutDefaultsToUnboundAndDisabled() throws { try self.withRestoredDefaults(keys: [ self.pasteLastTranscriptionShortcutKey,