refactor(desktop): hoist shared ToggleRow into settings primitives

System + Notifications each had an identical local ToggleRow; lift one
haptic-baked version into primitives and reuse it. Net -12 lines.
This commit is contained in:
Brooklyn Nicholson 2026-07-20 12:31:06 -05:00
parent 9399839dd4
commit 9b513a3b8d
3 changed files with 37 additions and 49 deletions

View file

@ -3,7 +3,6 @@ import type { ReactNode } from 'react'
import { Button } from '@/components/ui/button'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Switch } from '@/components/ui/switch'
import { useI18n } from '@/i18n'
import { COMPLETION_SOUND_VARIANTS, previewCompletionSound } from '@/lib/completion-sound'
import { triggerHaptic } from '@/lib/haptics'
@ -20,7 +19,7 @@ import {
import { notify } from '@/store/notifications'
import { CONTROL_TEXT } from './constants'
import { ListRow, SectionHeading, SettingsContent } from './primitives'
import { ListRow, SectionHeading, SettingsContent, ToggleRow } from './primitives'
const CAPTION = 'text-[length:var(--conversation-caption-font-size)] text-(--ui-text-tertiary)'
@ -28,32 +27,6 @@ function Caption({ children, className }: { children: ReactNode; className?: str
return <p className={cn(CAPTION, className)}>{children}</p>
}
function ToggleRow(props: {
checked: boolean
description: string
disabled?: boolean
label: string
onChange: (on: boolean) => void
}) {
return (
<ListRow
action={
<Switch
aria-label={props.label}
checked={props.checked}
disabled={props.disabled}
onCheckedChange={on => {
triggerHaptic('selection')
props.onChange(on)
}}
/>
}
description={props.description}
title={props.label}
/>
)
}
export function NotificationsSettings() {
const { t } = useI18n()
const prefs = useStore($nativeNotifyPrefs)

View file

@ -3,6 +3,8 @@ import type { ReactNode } from 'react'
import { PageLoader } from '@/components/page-loader'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Switch } from '@/components/ui/switch'
import { triggerHaptic } from '@/lib/haptics'
import type { IconComponent } from '@/lib/icons'
import { cn } from '@/lib/utils'
@ -108,6 +110,39 @@ export function ListRow({
)
}
// A labelled on/off row — the canonical device-pref switch (haptic baked in).
export function ToggleRow({
checked,
description,
disabled,
label,
onChange
}: {
checked: boolean
description?: string
disabled?: boolean
label: string
onChange: (on: boolean) => void
}) {
return (
<ListRow
action={
<Switch
aria-label={label}
checked={checked}
disabled={disabled}
onCheckedChange={on => {
triggerHaptic('selection')
onChange(on)
}}
/>
}
description={description}
title={label}
/>
)
}
export function LoadingState({ label }: { label: string }) {
return <PageLoader label={label} />
}

View file

@ -1,7 +1,6 @@
import { useStore } from '@nanostores/react'
import { SegmentedControl } from '@/components/ui/segmented-control'
import { Switch } from '@/components/ui/switch'
import { useI18n } from '@/i18n'
import { triggerHaptic } from '@/lib/haptics'
import { Cpu } from '@/lib/icons'
@ -10,7 +9,7 @@ import { $keepAwake, setKeepAwake } from '@/store/keep-awake'
import { $translucency, setTranslucency } from '@/store/translucency'
import { $zoomPercent, setZoomPercent } from '@/store/zoom'
import { ListRow, SectionHeading, SettingsContent } from './primitives'
import { ListRow, SectionHeading, SettingsContent, ToggleRow } from './primitives'
// UI scale presets as zoom percentages (100 = browser default); the ids double
// as the percent sent to main. A Cmd/Ctrl +/- step between presets highlights
@ -19,25 +18,6 @@ const UI_SCALE_PRESETS = ['90', '100', '110', '125', '150', '175'] as const
type UiScalePreset = (typeof UI_SCALE_PRESETS)[number]
function ToggleRow(props: { checked: boolean; description: string; label: string; onChange: (on: boolean) => void }) {
return (
<ListRow
action={
<Switch
aria-label={props.label}
checked={props.checked}
onCheckedChange={on => {
triggerHaptic('selection')
props.onChange(on)
}}
/>
}
description={props.description}
title={props.label}
/>
)
}
export function SystemSettings() {
const { t } = useI18n()
const s = t.settings.system