Просмотр исходного кода

feat(app): option to turn off sound effects

Adam 2 месяцев назад
Родитель
Сommit
4e0f509e7b

+ 3 - 0
packages/app/e2e/selectors.ts

@@ -10,8 +10,11 @@ export const settingsNotificationsAgentSelector = '[data-action="settings-notifi
 export const settingsNotificationsPermissionsSelector = '[data-action="settings-notifications-permissions"]'
 export const settingsNotificationsErrorsSelector = '[data-action="settings-notifications-errors"]'
 export const settingsSoundsAgentSelector = '[data-action="settings-sounds-agent"]'
+export const settingsSoundsAgentEnabledSelector = '[data-action="settings-sounds-agent-enabled"]'
 export const settingsSoundsPermissionsSelector = '[data-action="settings-sounds-permissions"]'
+export const settingsSoundsPermissionsEnabledSelector = '[data-action="settings-sounds-permissions-enabled"]'
 export const settingsSoundsErrorsSelector = '[data-action="settings-sounds-errors"]'
+export const settingsSoundsErrorsEnabledSelector = '[data-action="settings-sounds-errors-enabled"]'
 export const settingsUpdatesStartupSelector = '[data-action="settings-updates-startup"]'
 export const settingsReleaseNotesSelector = '[data-action="settings-release-notes"]'
 

+ 25 - 0
packages/app/e2e/settings/settings.spec.ts

@@ -9,6 +9,7 @@ import {
   settingsNotificationsPermissionsSelector,
   settingsReleaseNotesSelector,
   settingsSoundsAgentSelector,
+  settingsSoundsAgentEnabledSelector,
   settingsSoundsErrorsSelector,
   settingsSoundsPermissionsSelector,
   settingsThemeSelector,
@@ -335,6 +336,30 @@ test("changing sound agent selection persists in localStorage", async ({ page, g
   expect(stored?.sounds?.agent).not.toBe("staplebops-01")
 })
 
+test("disabling agent sound disables sound selection", async ({ page, gotoSession }) => {
+  await gotoSession()
+
+  const dialog = await openSettings(page)
+  const select = dialog.locator(settingsSoundsAgentSelector)
+  const switchContainer = dialog.locator(settingsSoundsAgentEnabledSelector)
+  const trigger = select.locator('[data-slot="select-select-trigger"]')
+  await expect(select).toBeVisible()
+  await expect(switchContainer).toBeVisible()
+  await expect(trigger).toBeEnabled()
+
+  await switchContainer.locator('[data-slot="switch-control"]').click()
+  await page.waitForTimeout(100)
+
+  await expect(trigger).toBeDisabled()
+
+  const stored = await page.evaluate((key) => {
+    const raw = localStorage.getItem(key)
+    return raw ? JSON.parse(raw) : null
+  }, settingsKey)
+
+  expect(stored?.sounds?.agentEnabled).toBe(false)
+})
+
 test("changing permissions and errors sounds updates localStorage", async ({ page, gotoSession }) => {
   await gotoSession()
 

+ 48 - 21
packages/app/src/components/settings-general.tsx

@@ -306,39 +306,66 @@ export const SettingsGeneral: Component = () => {
           title={language.t("settings.general.sounds.agent.title")}
           description={language.t("settings.general.sounds.agent.description")}
         >
-          <Select
-            data-action="settings-sounds-agent"
-            {...soundSelectProps(
-              () => settings.sounds.agent(),
-              (id) => settings.sounds.setAgent(id),
-            )}
-          />
+          <div class="flex items-center gap-2">
+            <div data-action="settings-sounds-agent-enabled">
+              <Switch
+                checked={settings.sounds.agentEnabled()}
+                onChange={(checked) => settings.sounds.setAgentEnabled(checked)}
+              />
+            </div>
+            <Select
+              disabled={!settings.sounds.agentEnabled()}
+              data-action="settings-sounds-agent"
+              {...soundSelectProps(
+                () => settings.sounds.agent(),
+                (id) => settings.sounds.setAgent(id),
+              )}
+            />
+          </div>
         </SettingsRow>
 
         <SettingsRow
           title={language.t("settings.general.sounds.permissions.title")}
           description={language.t("settings.general.sounds.permissions.description")}
         >
-          <Select
-            data-action="settings-sounds-permissions"
-            {...soundSelectProps(
-              () => settings.sounds.permissions(),
-              (id) => settings.sounds.setPermissions(id),
-            )}
-          />
+          <div class="flex items-center gap-2">
+            <div data-action="settings-sounds-permissions-enabled">
+              <Switch
+                checked={settings.sounds.permissionsEnabled()}
+                onChange={(checked) => settings.sounds.setPermissionsEnabled(checked)}
+              />
+            </div>
+            <Select
+              disabled={!settings.sounds.permissionsEnabled()}
+              data-action="settings-sounds-permissions"
+              {...soundSelectProps(
+                () => settings.sounds.permissions(),
+                (id) => settings.sounds.setPermissions(id),
+              )}
+            />
+          </div>
         </SettingsRow>
 
         <SettingsRow
           title={language.t("settings.general.sounds.errors.title")}
           description={language.t("settings.general.sounds.errors.description")}
         >
-          <Select
-            data-action="settings-sounds-errors"
-            {...soundSelectProps(
-              () => settings.sounds.errors(),
-              (id) => settings.sounds.setErrors(id),
-            )}
-          />
+          <div class="flex items-center gap-2">
+            <div data-action="settings-sounds-errors-enabled">
+              <Switch
+                checked={settings.sounds.errorsEnabled()}
+                onChange={(checked) => settings.sounds.setErrorsEnabled(checked)}
+              />
+            </div>
+            <Select
+              disabled={!settings.sounds.errorsEnabled()}
+              data-action="settings-sounds-errors"
+              {...soundSelectProps(
+                () => settings.sounds.errors(),
+                (id) => settings.sounds.setErrors(id),
+              )}
+            />
+          </div>
         </SettingsRow>
       </div>
     </div>

+ 6 - 2
packages/app/src/context/notification.tsx

@@ -233,7 +233,9 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
         if (!session) return
         if (session.parentID) return
 
-        playSound(soundSrc(settings.sounds.agent()))
+        if (settings.sounds.agentEnabled()) {
+          playSound(soundSrc(settings.sounds.agent()))
+        }
 
         append({
           directory,
@@ -260,7 +262,9 @@ export const { use: useNotification, provider: NotificationProvider } = createSi
         if (meta.disposed) return
         if (session?.parentID) return
 
-        playSound(soundSrc(settings.sounds.errors()))
+        if (settings.sounds.errorsEnabled()) {
+          playSound(soundSrc(settings.sounds.errors()))
+        }
 
         const error = "error" in event.properties ? event.properties.error : undefined
         append({

+ 21 - 0
packages/app/src/context/settings.tsx

@@ -10,8 +10,11 @@ export interface NotificationSettings {
 }
 
 export interface SoundSettings {
+  agentEnabled: boolean
   agent: string
+  permissionsEnabled: boolean
   permissions: string
+  errorsEnabled: boolean
   errors: string
 }
 
@@ -57,8 +60,11 @@ const defaultSettings: Settings = {
     errors: false,
   },
   sounds: {
+    agentEnabled: true,
     agent: "staplebops-01",
+    permissionsEnabled: true,
     permissions: "staplebops-02",
+    errorsEnabled: true,
     errors: "nope-03",
   },
 }
@@ -168,14 +174,29 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
         },
       },
       sounds: {
+        agentEnabled: withFallback(() => store.sounds?.agentEnabled, defaultSettings.sounds.agentEnabled),
+        setAgentEnabled(value: boolean) {
+          setStore("sounds", "agentEnabled", value)
+        },
         agent: withFallback(() => store.sounds?.agent, defaultSettings.sounds.agent),
         setAgent(value: string) {
           setStore("sounds", "agent", value)
         },
+        permissionsEnabled: withFallback(
+          () => store.sounds?.permissionsEnabled,
+          defaultSettings.sounds.permissionsEnabled,
+        ),
+        setPermissionsEnabled(value: boolean) {
+          setStore("sounds", "permissionsEnabled", value)
+        },
         permissions: withFallback(() => store.sounds?.permissions, defaultSettings.sounds.permissions),
         setPermissions(value: string) {
           setStore("sounds", "permissions", value)
         },
+        errorsEnabled: withFallback(() => store.sounds?.errorsEnabled, defaultSettings.sounds.errorsEnabled),
+        setErrorsEnabled(value: boolean) {
+          setStore("sounds", "errorsEnabled", value)
+        },
         errors: withFallback(() => store.sounds?.errors, defaultSettings.sounds.errors),
         setErrors(value: string) {
           setStore("sounds", "errors", value)

+ 3 - 1
packages/app/src/pages/layout.tsx

@@ -388,7 +388,9 @@ export default function Layout(props: ParentProps) {
         alertedAtBySession.set(sessionKey, now)
 
         if (e.details.type === "permission.asked") {
-          playSound(soundSrc(settings.sounds.permissions()))
+          if (settings.sounds.permissionsEnabled()) {
+            playSound(soundSrc(settings.sounds.permissions()))
+          }
           if (settings.notifications.permissions()) {
             void platform.notify(title, description, href)
           }