From 85e9dba208f088c776ff5ae2bb366d1d0d822cb4 Mon Sep 17 00:00:00 2001 From: Jamie Pine Date: Sun, 11 Jan 2026 01:54:25 -0800 Subject: [PATCH] fix double ding --- apps/landing | 2 +- .../components/JobManager/hooks/useJobs.ts | 20 +++++++++---- .../src/components/SpacesSidebar/index.tsx | 30 +++++++++++-------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/apps/landing b/apps/landing index 3580e19cf..72e6628e1 160000 --- a/apps/landing +++ b/apps/landing @@ -1 +1 @@ -Subproject commit 3580e19cf724a2d37f3d69dbac46beb93bc2e1ca +Subproject commit 72e6628e1a8e650d63f1bce4f7c0f444306b5bd2 diff --git a/packages/interface/src/components/JobManager/hooks/useJobs.ts b/packages/interface/src/components/JobManager/hooks/useJobs.ts index 29de6f5d4..2e351f925 100644 --- a/packages/interface/src/components/JobManager/hooks/useJobs.ts +++ b/packages/interface/src/components/JobManager/hooks/useJobs.ts @@ -7,6 +7,10 @@ import { sounds } from "@sd/assets/sounds"; // This prevents multiple hook instances from playing the sound multiple times const completedJobSounds = new Set(); +// Global throttle to prevent multiple sounds within 5 seconds +let lastSoundPlayedAt = 0; +const SOUND_THROTTLE_MS = 5000; + /** * Unified hook for job management and counting. * Prevents duplicate queries and subscriptions that were causing infinite loops. @@ -56,11 +60,17 @@ export function useJobs() { if (jobId && !completedJobSounds.has(jobId)) { completedJobSounds.add(jobId); - // Play job-specific sound - if (jobType?.includes("copy") || jobType?.includes("Copy")) { - sounds.copy(); - } else { - sounds.jobDone(); + // Throttle: only play sound if enough time has passed since last sound + const now = Date.now(); + if (now - lastSoundPlayedAt >= SOUND_THROTTLE_MS) { + lastSoundPlayedAt = now; + + // Play job-specific sound + if (jobType?.includes("copy") || jobType?.includes("Copy")) { + sounds.copy(); + } else { + sounds.jobDone(); + } } // Clean up old entries after 5 seconds to prevent memory leak diff --git a/packages/interface/src/components/SpacesSidebar/index.tsx b/packages/interface/src/components/SpacesSidebar/index.tsx index f65700888..3a5e06efa 100644 --- a/packages/interface/src/components/SpacesSidebar/index.tsx +++ b/packages/interface/src/components/SpacesSidebar/index.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, memo } from "react"; import { GearSix, Palette, ArrowsClockwise, ListBullets, CircleNotch, ArrowsOut, FunnelSimple } from "@phosphor-icons/react"; import { useSidebarStore, useLibraryMutation } from "@sd/ts-client"; import type { SpaceGroup as SpaceGroupType, SpaceItem as SpaceItemType } from "@sd/ts-client"; @@ -95,7 +95,7 @@ function SpaceGroupWithDropZone({ } // Sync Monitor Button with Popover -function SyncButton() { +const SyncButton = memo(function SyncButton() { const popover = usePopover(); const navigate = useNavigate(); const [showActivityFeed, setShowActivityFeed] = useState(false); @@ -198,18 +198,18 @@ function SyncButton() { )} ); -} +}); // Jobs Button with Popover -function JobsButton({ - activeJobCount, - hasRunningJobs, - jobs, - pause, - resume, +const JobsButton = memo(function JobsButton({ + activeJobCount, + hasRunningJobs, + jobs, + pause, + resume, cancel, - navigate -}: { + navigate +}: { activeJobCount: number; hasRunningJobs: boolean; jobs: any[]; @@ -291,7 +291,13 @@ function JobsButton({ )} ); -} +}, (prevProps, nextProps) => { + // Only re-render if these specific values change + return ( + prevProps.activeJobCount === nextProps.activeJobCount && + prevProps.hasRunningJobs === nextProps.hasRunningJobs + ); +}); interface SpacesSidebarProps { isPreviewActive?: boolean;