From 3724a1530cdce2cd4c75d20e63b92e8e72f2474c Mon Sep 17 00:00:00 2001 From: JoeMakuta Date: Sun, 29 Mar 2026 18:11:49 +0200 Subject: [PATCH 1/3] refactor: simplify hasChanges tracking in GeneralSettingsManager and PromptConfigManager components --- .../settings/general-settings-manager.tsx | 14 ++------------ .../components/settings/prompt-config-manager.tsx | 14 +++----------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/surfsense_web/components/settings/general-settings-manager.tsx b/surfsense_web/components/settings/general-settings-manager.tsx index ca90142b1..831a4a435 100644 --- a/surfsense_web/components/settings/general-settings-manager.tsx +++ b/surfsense_web/components/settings/general-settings-manager.tsx @@ -40,26 +40,17 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [saving, setSaving] = useState(false); - const [hasChanges, setHasChanges] = useState(false); // Initialize state from fetched search space useEffect(() => { if (searchSpace) { setName(searchSpace.name || ""); setDescription(searchSpace.description || ""); - setHasChanges(false); } }, [searchSpace]); - // Track changes - useEffect(() => { - if (searchSpace) { - const currentName = searchSpace.name || ""; - const currentDescription = searchSpace.description || ""; - const changed = currentName !== name || currentDescription !== description; - setHasChanges(changed); - } - }, [searchSpace, name, description]); + // Derive hasChanges during render + const hasChanges = !!searchSpace && ((searchSpace.name || "") !== name || (searchSpace.description || "") !== description); const handleSave = async () => { try { @@ -73,7 +64,6 @@ export function GeneralSettingsManager({ searchSpaceId }: GeneralSettingsManager }, }); - setHasChanges(false); await fetchSearchSpace(); } catch (error: any) { console.error("Error saving search space details:", error); diff --git a/surfsense_web/components/settings/prompt-config-manager.tsx b/surfsense_web/components/settings/prompt-config-manager.tsx index b3cd64a5b..a1e345abe 100644 --- a/surfsense_web/components/settings/prompt-config-manager.tsx +++ b/surfsense_web/components/settings/prompt-config-manager.tsx @@ -32,24 +32,16 @@ export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) const [customInstructions, setCustomInstructions] = useState(""); const [saving, setSaving] = useState(false); - const [hasChanges, setHasChanges] = useState(false); // Initialize state from fetched search space useEffect(() => { if (searchSpace) { setCustomInstructions(searchSpace.qna_custom_instructions || ""); - setHasChanges(false); } }, [searchSpace]); - // Track changes - useEffect(() => { - if (searchSpace) { - const currentCustom = searchSpace.qna_custom_instructions || ""; - const changed = currentCustom !== customInstructions; - setHasChanges(changed); - } - }, [searchSpace, customInstructions]); + // Derive hasChanges during render + const hasChanges = !!searchSpace && (searchSpace.qna_custom_instructions || "") !== customInstructions; const handleSave = async () => { try { @@ -74,7 +66,7 @@ export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) } toast.success("System instructions saved successfully"); - setHasChanges(false); + await fetchSearchSpace(); } catch (error: any) { console.error("Error saving system instructions:", error); From 1705e881ecec036a2b65afeffaca6317d7e91ef9 Mon Sep 17 00:00:00 2001 From: JoeMakuta Date: Sun, 29 Mar 2026 18:35:54 +0200 Subject: [PATCH 2/3] refactor: use functional setState in useCallback for stable callbacks in onboarding tour and sidebar --- .../layout/hooks/useSidebarState.ts | 10 ++++- surfsense_web/components/onboarding-tour.tsx | 42 +++++++++++-------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/surfsense_web/components/layout/hooks/useSidebarState.ts b/surfsense_web/components/layout/hooks/useSidebarState.ts index 78631694d..63ecbe49a 100644 --- a/surfsense_web/components/layout/hooks/useSidebarState.ts +++ b/surfsense_web/components/layout/hooks/useSidebarState.ts @@ -37,8 +37,14 @@ export function useSidebarState(defaultCollapsed = false): UseSidebarStateReturn }, []); const toggleCollapsed = useCallback(() => { - setIsCollapsed(!isCollapsed); - }, [isCollapsed, setIsCollapsed]); + setIsCollapsedState(prev => { + const next = !prev; + try { + document.cookie = `${SIDEBAR_COOKIE_NAME}=${next}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; + } catch {} + return next; + }); + }, []); // Keyboard shortcut: Cmd/Ctrl + \ useEffect(() => { diff --git a/surfsense_web/components/onboarding-tour.tsx b/surfsense_web/components/onboarding-tour.tsx index e5dd3a825..11d3b78fa 100644 --- a/surfsense_web/components/onboarding-tour.tsx +++ b/surfsense_web/components/onboarding-tour.tsx @@ -666,27 +666,33 @@ export function OnboardingTour() { }, [targetEl, isActive]); const handleNext = useCallback(() => { - if (stepIndex < TOUR_STEPS.length - 1) { - retryCountRef.current = 0; - setShouldAnimate(true); - setStepIndex(stepIndex + 1); - } else { - // Tour completed - save to localStorage - if (user?.id) { - const tourKey = `surfsense-tour-${user.id}`; - localStorage.setItem(tourKey, "true"); + retryCountRef.current = 0; + setShouldAnimate(true); + setStepIndex(prev => { + if (prev < TOUR_STEPS.length - 1) { + return prev + 1; + } else { + // Tour completed - save to localStorage + if (user?.id) { + const tourKey = `surfsense-tour-${user.id}`; + localStorage.setItem(tourKey, "true"); + } + setIsActive(false); + return prev; } - setIsActive(false); - } - }, [stepIndex, user?.id]); + }); + }, [user?.id]); const handlePrev = useCallback(() => { - if (stepIndex > 0) { - retryCountRef.current = 0; - setShouldAnimate(true); - setStepIndex(stepIndex - 1); - } - }, [stepIndex]); + retryCountRef.current = 0; + setShouldAnimate(true); + setStepIndex(prev => { + if (prev > 0) { + return prev - 1; + } + return prev; + }); + }, []); const handleSkip = useCallback(() => { // Tour skipped - save to localStorage From 4e6251ea04e3e9f0a3e94ee7e4ae4c38f903a7bc Mon Sep 17 00:00:00 2001 From: JoeMakuta Date: Sun, 29 Mar 2026 18:47:05 +0200 Subject: [PATCH 3/3] Replace setOpen(!open) toggles with functional setOpen(prev => !prev) across codebase --- .../connect-forms/components/mcp-connect-form.tsx | 2 +- .../connector-configs/components/composio-drive-config.tsx | 2 +- .../connector-popup/connector-configs/components/mcp-config.tsx | 2 +- .../connector-configs/components/onedrive-config.tsx | 2 +- .../connector-configs/components/webcrawler-config.tsx | 2 +- surfsense_web/components/assistant-ui/thinking-steps.tsx | 2 +- surfsense_web/components/assistant-ui/tool-fallback.tsx | 2 +- .../components/chat-comments/comment-thread/comment-thread.tsx | 2 +- surfsense_web/components/homepage/navbar.tsx | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/mcp-connect-form.tsx b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/mcp-connect-form.tsx index 2a5bb2121..18dcbebc3 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/mcp-connect-form.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/components/mcp-connect-form.tsx @@ -243,7 +243,7 @@ export const MCPConnectForm: FC = ({ onSubmit, isSubmitting }) onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setShowDetails(!showDetails); + setShowDetails(prev => !prev); }} > {showDetails ? ( diff --git a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx index 0f6044050..dcaadb34d 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/components/composio-drive-config.tsx @@ -250,7 +250,7 @@ export const ComposioDriveConfig: FC = ({ connector, onCon