From 9d0fc241eee6bdc446033015b644bf14a21588d0 Mon Sep 17 00:00:00 2001 From: Beingpax Date: Wed, 31 Dec 2025 18:48:45 +0545 Subject: [PATCH] Refactor expandable sections to use enum and set pattern for cleaner state management --- .../Settings/ExpandableToggleSection.swift | 38 ++++++++++++++----- .../ExperimentalFeaturesSection.swift | 5 ++- VoiceInk/Views/Settings/SettingsView.swift | 21 +++++----- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/VoiceInk/Views/Settings/ExpandableToggleSection.swift b/VoiceInk/Views/Settings/ExpandableToggleSection.swift index ca1f1ef..1b903ca 100644 --- a/VoiceInk/Views/Settings/ExpandableToggleSection.swift +++ b/VoiceInk/Views/Settings/ExpandableToggleSection.swift @@ -1,26 +1,42 @@ import SwiftUI +enum ExpandableSection: Hashable { + case soundFeedback + case systemMute + case pauseMedia + case clipboardRestore + case customCancel + case middleClick +} + struct ExpandableToggleSection: View { + let section: ExpandableSection let title: String let helpText: String @Binding var isEnabled: Bool - @Binding var isExpanded: Bool + @Binding var expandedSections: Set let content: Content init( + section: ExpandableSection, title: String, helpText: String, isEnabled: Binding, - isExpanded: Binding, + expandedSections: Binding>, @ViewBuilder content: () -> Content ) { + self.section = section self.title = title self.helpText = helpText self._isEnabled = isEnabled - self._isExpanded = isExpanded + self._expandedSections = expandedSections self.content = content() } + private var isExpanded: Bool { + expandedSections.contains(section) + } + var body: some View { VStack(alignment: .leading, spacing: 12) { HStack { @@ -30,12 +46,12 @@ struct ExpandableToggleSection: View { .toggleStyle(.switch) .help(helpText) .onChange(of: isEnabled) { _, newValue in - if newValue { - withAnimation(.easeInOut(duration: 0.2)) { - isExpanded = true + withAnimation(.easeInOut(duration: 0.2)) { + if newValue { + _ = expandedSections.insert(section) + } else { + expandedSections.remove(section) } - } else { - isExpanded = false } } @@ -53,7 +69,11 @@ struct ExpandableToggleSection: View { .onTapGesture { if isEnabled { withAnimation(.easeInOut(duration: 0.2)) { - isExpanded.toggle() + if isExpanded { + expandedSections.remove(section) + } else { + _ = expandedSections.insert(section) + } } } } diff --git a/VoiceInk/Views/Settings/ExperimentalFeaturesSection.swift b/VoiceInk/Views/Settings/ExperimentalFeaturesSection.swift index 222eccd..80c164b 100644 --- a/VoiceInk/Views/Settings/ExperimentalFeaturesSection.swift +++ b/VoiceInk/Views/Settings/ExperimentalFeaturesSection.swift @@ -4,7 +4,7 @@ struct ExperimentalFeaturesSection: View { @AppStorage("isExperimentalFeaturesEnabled") private var isExperimentalFeaturesEnabled = false @ObservedObject private var playbackController = PlaybackController.shared @ObservedObject private var mediaController = MediaController.shared - @State private var isPauseMediaExpanded = false + @State private var expandedSections: Set = [] var body: some View { VStack(alignment: .leading, spacing: 12) { @@ -40,10 +40,11 @@ struct ExperimentalFeaturesSection: View { .transition(.opacity.combined(with: .move(edge: .top))) ExpandableToggleSection( + section: .pauseMedia, title: "Pause Media during recording", helpText: "Automatically pause active media playback during recordings and resume afterward.", isEnabled: $playbackController.isPauseMediaEnabled, - isExpanded: $isPauseMediaExpanded + expandedSections: $expandedSections ) { HStack(spacing: 8) { Text("Resumption Delay") diff --git a/VoiceInk/Views/Settings/SettingsView.swift b/VoiceInk/Views/Settings/SettingsView.swift index 40a5885..0173ff7 100644 --- a/VoiceInk/Views/Settings/SettingsView.swift +++ b/VoiceInk/Views/Settings/SettingsView.swift @@ -22,11 +22,7 @@ struct SettingsView: View { @State private var showResetOnboardingAlert = false @State private var currentShortcut = KeyboardShortcuts.getShortcut(for: .toggleMiniRecorder) @State private var isCustomCancelEnabled = false - @State private var isCustomSoundsExpanded = false - @State private var isSystemMuteExpanded = false - @State private var isClipboardRestoreExpanded = false - @State private var isCustomCancelExpanded = false - @State private var isMiddleClickExpanded = false + @State private var expandedSections: Set = [] var body: some View { @@ -141,10 +137,11 @@ struct SettingsView: View { ExpandableToggleSection( + section: .customCancel, title: "Custom Cancel Shortcut", helpText: "Shortcut for cancelling the current recording session. Default: double-tap Escape.", isEnabled: $isCustomCancelEnabled, - isExpanded: $isCustomCancelExpanded + expandedSections: $expandedSections ) { HStack(spacing: 12) { Text("Cancel Shortcut") @@ -166,10 +163,11 @@ struct SettingsView: View { Divider() ExpandableToggleSection( + section: .middleClick, title: "Enable Middle-Click Toggle", helpText: "Use middle mouse button to toggle VoiceInk recording.", isEnabled: $hotkeyManager.isMiddleClickToggleEnabled, - isExpanded: $isMiddleClickExpanded + expandedSections: $expandedSections ) { HStack(spacing: 8) { Text("Activation Delay") @@ -204,10 +202,11 @@ struct SettingsView: View { ) { VStack(alignment: .leading, spacing: 12) { ExpandableToggleSection( + section: .soundFeedback, title: "Sound feedback", helpText: "Play sounds when recording starts and stops", isEnabled: $soundManager.isEnabled, - isExpanded: $isCustomSoundsExpanded + expandedSections: $expandedSections ) { CustomSoundSettingsView() } @@ -215,10 +214,11 @@ struct SettingsView: View { Divider() ExpandableToggleSection( + section: .systemMute, title: "Mute system audio during recording", helpText: "Automatically mute system audio when recording starts and restore when recording stops", isEnabled: $mediaController.isSystemMuteEnabled, - isExpanded: $isSystemMuteExpanded + expandedSections: $expandedSections ) { HStack(spacing: 8) { Text("Resumption Delay") @@ -247,10 +247,11 @@ struct SettingsView: View { Divider() ExpandableToggleSection( + section: .clipboardRestore, title: "Restore clipboard after paste", helpText: "When enabled, VoiceInk will restore your original clipboard content after pasting the transcription.", isEnabled: $restoreClipboardAfterPaste, - isExpanded: $isClipboardRestoreExpanded + expandedSections: $expandedSections ) { HStack(spacing: 8) { Text("Restore Delay")