diff --git a/surfsense_web/app/(home)/login/page.tsx b/surfsense_web/app/(home)/login/page.tsx index 0dc9c445f..a3ef7cd8f 100644 --- a/surfsense_web/app/(home)/login/page.tsx +++ b/surfsense_web/app/(home)/login/page.tsx @@ -93,7 +93,7 @@ function LoginContent() { }, [searchParams, t, tCommon]); // Use global loading screen for auth type determination - spinner animation won't reset - useGlobalLoadingEffect(isLoading, tCommon("loading"), "login"); + useGlobalLoadingEffect(isLoading); // Show nothing while loading - the GlobalLoadingProvider handles the loading UI if (isLoading) { diff --git a/surfsense_web/app/auth/callback/loading.tsx b/surfsense_web/app/auth/callback/loading.tsx index 0c94e1ee0..f12b3847d 100644 --- a/surfsense_web/app/auth/callback/loading.tsx +++ b/surfsense_web/app/auth/callback/loading.tsx @@ -1,13 +1,10 @@ "use client"; -import { useTranslations } from "next-intl"; import { useGlobalLoadingEffect } from "@/hooks/use-global-loading"; export default function AuthCallbackLoading() { - const t = useTranslations("auth"); - // Use global loading - spinner animation won't reset when page transitions - useGlobalLoadingEffect(true, t("processing_authentication"), "default"); + useGlobalLoadingEffect(true); // Return null - the GlobalLoadingProvider handles the loading UI return null; diff --git a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx index e6730d8d1..8418d4719 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx @@ -154,11 +154,7 @@ export function DashboardClientLayout({ isAutoConfiguring; // Use global loading screen - spinner animation won't reset - useGlobalLoadingEffect( - shouldShowLoading, - isAutoConfiguring ? t("setting_up_ai") : t("checking_llm_prefs"), - "default" - ); + useGlobalLoadingEffect(shouldShowLoading); if (shouldShowLoading) { return null; diff --git a/surfsense_web/app/dashboard/layout.tsx b/surfsense_web/app/dashboard/layout.tsx index 889b823d6..4a32c2147 100644 --- a/surfsense_web/app/dashboard/layout.tsx +++ b/surfsense_web/app/dashboard/layout.tsx @@ -1,6 +1,5 @@ "use client"; -import { useTranslations } from "next-intl"; import { useEffect, useState } from "react"; import { useGlobalLoadingEffect } from "@/hooks/use-global-loading"; import { getBearerToken, redirectToLogin } from "@/lib/auth-utils"; @@ -10,11 +9,10 @@ interface DashboardLayoutProps { } export default function DashboardLayout({ children }: DashboardLayoutProps) { - const t = useTranslations("dashboard"); const [isCheckingAuth, setIsCheckingAuth] = useState(true); // Use the global loading screen - spinner animation won't reset - useGlobalLoadingEffect(isCheckingAuth, t("checking_auth"), "default"); + useGlobalLoadingEffect(isCheckingAuth); useEffect(() => { // Check if user is authenticated diff --git a/surfsense_web/app/dashboard/loading.tsx b/surfsense_web/app/dashboard/loading.tsx index 2eee93651..ca6b05de0 100644 --- a/surfsense_web/app/dashboard/loading.tsx +++ b/surfsense_web/app/dashboard/loading.tsx @@ -1,13 +1,10 @@ "use client"; -import { useTranslations } from "next-intl"; import { useGlobalLoadingEffect } from "@/hooks/use-global-loading"; export default function DashboardLoading() { - const t = useTranslations("common"); - // Use global loading - spinner animation won't reset when page transitions - useGlobalLoadingEffect(true, t("loading"), "default"); + useGlobalLoadingEffect(true); // Return null - the GlobalLoadingProvider handles the loading UI return null; diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx index 504d172c3..2bd8f4462 100644 --- a/surfsense_web/app/dashboard/page.tsx +++ b/surfsense_web/app/dashboard/page.tsx @@ -106,7 +106,7 @@ export default function DashboardPage() { const shouldShowLoading = isLoading || searchSpaces.length > 0; // Use global loading screen - spinner animation won't reset - useGlobalLoadingEffect(shouldShowLoading, t("fetching_spaces"), "default"); + useGlobalLoadingEffect(shouldShowLoading); if (error) return ; diff --git a/surfsense_web/atoms/ui/loading.atoms.ts b/surfsense_web/atoms/ui/loading.atoms.ts index f10d9247b..ca37e1cdc 100644 --- a/surfsense_web/atoms/ui/loading.atoms.ts +++ b/surfsense_web/atoms/ui/loading.atoms.ts @@ -2,29 +2,18 @@ import { atom } from "jotai"; interface GlobalLoadingState { isLoading: boolean; - message?: string; - variant: "login" | "default"; } export const globalLoadingAtom = atom({ isLoading: false, - message: undefined, - variant: "default", }); // Helper atom for showing global loading -export const showGlobalLoadingAtom = atom( - null, - ( - get, - set, - { message, variant = "default" }: { message?: string; variant?: "login" | "default" } - ) => { - set(globalLoadingAtom, { isLoading: true, message, variant }); - } -); +export const showGlobalLoadingAtom = atom(null, (get, set) => { + set(globalLoadingAtom, { isLoading: true }); +}); // Helper atom for hiding global loading export const hideGlobalLoadingAtom = atom(null, (get, set) => { - set(globalLoadingAtom, { isLoading: false, message: undefined, variant: "default" }); + set(globalLoadingAtom, { isLoading: false }); }); diff --git a/surfsense_web/components/TokenHandler.tsx b/surfsense_web/components/TokenHandler.tsx index 35408c1b2..e3295df7c 100644 --- a/surfsense_web/components/TokenHandler.tsx +++ b/surfsense_web/components/TokenHandler.tsx @@ -1,7 +1,6 @@ "use client"; import { useSearchParams } from "next/navigation"; -import { useTranslations } from "next-intl"; import { useEffect } from "react"; import { useGlobalLoadingEffect } from "@/hooks/use-global-loading"; import { getAndClearRedirectPath, setBearerToken } from "@/lib/auth-utils"; @@ -27,11 +26,10 @@ const TokenHandler = ({ tokenParamName = "token", storageKey = "surfsense_bearer_token", }: TokenHandlerProps) => { - const t = useTranslations("auth"); const searchParams = useSearchParams(); // Always show loading for this component - spinner animation won't reset - useGlobalLoadingEffect(true, t("processing_authentication"), "default"); + useGlobalLoadingEffect(true); useEffect(() => { // Only run on client-side diff --git a/surfsense_web/components/providers/ElectricProvider.tsx b/surfsense_web/components/providers/ElectricProvider.tsx index 07d736c64..4aa83b304 100644 --- a/surfsense_web/components/providers/ElectricProvider.tsx +++ b/surfsense_web/components/providers/ElectricProvider.tsx @@ -1,7 +1,6 @@ "use client"; import { useAtomValue } from "jotai"; -import { useTranslations } from "next-intl"; import { useEffect, useRef, useState } from "react"; import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { useGlobalLoadingEffect } from "@/hooks/use-global-loading"; @@ -30,7 +29,6 @@ interface ElectricProviderProps { * 5. Provides client via context - hooks should use useElectricClient() */ export function ElectricProvider({ children }: ElectricProviderProps) { - const t = useTranslations("common"); const [electricClient, setElectricClient] = useState(null); const [error, setError] = useState(null); const { @@ -117,7 +115,7 @@ export function ElectricProvider({ children }: ElectricProviderProps) { const shouldShowLoading = hasToken && isUserLoaded && !!user?.id && !electricClient && !error; // Use global loading hook with ownership tracking - prevents flash during transitions - useGlobalLoadingEffect(shouldShowLoading, t("initializing"), "default"); + useGlobalLoadingEffect(shouldShowLoading); // For non-authenticated pages (like landing page), render immediately with null context // Also render immediately if user query failed (e.g., token expired) diff --git a/surfsense_web/components/providers/GlobalLoadingProvider.tsx b/surfsense_web/components/providers/GlobalLoadingProvider.tsx index db66b9a64..08c888954 100644 --- a/surfsense_web/components/providers/GlobalLoadingProvider.tsx +++ b/surfsense_web/components/providers/GlobalLoadingProvider.tsx @@ -3,9 +3,7 @@ import { useAtomValue } from "jotai"; import { useEffect, useState } from "react"; import { createPortal } from "react-dom"; -import { AmbientBackground } from "@/app/(home)/login/AmbientBackground"; import { globalLoadingAtom } from "@/atoms/ui/loading.atoms"; -import { Logo } from "@/components/Logo"; import { Spinner } from "@/components/ui/spinner"; import { cn } from "@/lib/utils"; @@ -18,7 +16,7 @@ import { cn } from "@/lib/utils"; */ export function GlobalLoadingProvider({ children }: { children: React.ReactNode }) { const [mounted, setMounted] = useState(false); - const { isLoading, message, variant } = useAtomValue(globalLoadingAtom); + const { isLoading } = useAtomValue(globalLoadingAtom); useEffect(() => { setMounted(true); @@ -36,35 +34,11 @@ export function GlobalLoadingProvider({ children }: { children: React.ReactNode )} aria-hidden={!isLoading} > - {variant === "login" ? ( - - - - - - - {/* Spinner is always mounted, animation never resets */} - - - - {message} - - - + + + - ) : ( - - - - {/* Spinner is always mounted, animation never resets */} - - - - {message} - - - - )} + ); diff --git a/surfsense_web/hooks/use-global-loading.ts b/surfsense_web/hooks/use-global-loading.ts index baaa1f089..fee8ae18e 100644 --- a/surfsense_web/hooks/use-global-loading.ts +++ b/surfsense_web/hooks/use-global-loading.ts @@ -20,21 +20,18 @@ let pendingHideTimeout: ReturnType | null = null; export function useGlobalLoading() { const [loading, setLoading] = useAtom(globalLoadingAtom); - const show = useCallback( - (message?: string, variant: "login" | "default" = "default") => { - // Cancel any pending hide - new loading request takes over - if (pendingHideTimeout) { - clearTimeout(pendingHideTimeout); - pendingHideTimeout = null; - } + const show = useCallback(() => { + // Cancel any pending hide - new loading request takes over + if (pendingHideTimeout) { + clearTimeout(pendingHideTimeout); + pendingHideTimeout = null; + } - const id = ++loadingIdCounter; - currentLoadingId = id; - setLoading({ isLoading: true, message, variant }); - return id; - }, - [setLoading] - ); + const id = ++loadingIdCounter; + currentLoadingId = id; + setLoading({ isLoading: true }); + return id; + }, [setLoading]); const hide = useCallback( (id?: number) => { @@ -50,7 +47,7 @@ export function useGlobalLoading() { // Double-check we're still the current loading after the delay if (id === undefined || id === currentLoadingId) { currentLoadingId = null; - setLoading({ isLoading: false, message: undefined, variant: "default" }); + setLoading({ isLoading: false }); } pendingHideTimeout = null; }, 50); // Small delay to allow next component to mount and show loading @@ -70,27 +67,21 @@ export function useGlobalLoading() { * transition loading states (e.g., layout → page). * * @param shouldShow - Whether the loading screen should be visible - * @param message - Optional message to display - * @param variant - Visual style variant ("login" or "default") */ -export function useGlobalLoadingEffect( - shouldShow: boolean, - message?: string, - variant: "login" | "default" = "default" -) { +export function useGlobalLoadingEffect(shouldShow: boolean) { const { show, hide } = useGlobalLoading(); const loadingIdRef = useRef(null); useEffect(() => { if (shouldShow) { // Show loading and store the ID - loadingIdRef.current = show(message, variant); + loadingIdRef.current = show(); } else if (loadingIdRef.current !== null) { // Only hide if we were the ones showing loading hide(loadingIdRef.current); loadingIdRef.current = null; } - }, [shouldShow, message, variant, show, hide]); + }, [shouldShow, show, hide]); // Cleanup on unmount - only hide if we're still the active loading useEffect(() => { diff --git a/surfsense_web/messages/en.json b/surfsense_web/messages/en.json index f14c73ddc..578bb9002 100644 --- a/surfsense_web/messages/en.json +++ b/surfsense_web/messages/en.json @@ -2,8 +2,6 @@ "common": { "app_name": "SurfSense", "welcome": "Welcome", - "loading": "Loading", - "initializing": "Initializing", "save": "Save", "cancel": "Cancel", "delete": "Delete", @@ -80,8 +78,7 @@ "passwords_no_match_desc": "The passwords you entered do not match", "creating_account": "Creating your account", "creating_account_btn": "Creating account", - "redirecting_login": "Redirecting to login page", - "processing_authentication": "Processing authentication" + "redirecting_login": "Redirecting to login page" }, "searchSpace": { "create_title": "Create Search Space", @@ -146,10 +143,7 @@ "api_keys": "API Keys", "profile": "Profile", "loading_dashboard": "Loading Dashboard", - "checking_auth": "Checking authentication", "loading_config": "Loading Configuration", - "checking_llm_prefs": "Checking your LLM preferences", - "setting_up_ai": "Setting up AI", "config_error": "Configuration Error", "failed_load_llm_config": "Failed to load your LLM configuration", "error_loading_chats": "Error loading chats", @@ -171,7 +165,6 @@ "create_search_space": "Create Search Space", "add_new_search_space": "Add New Search Space", "loading": "Loading", - "fetching_spaces": "Fetching your search spaces", "may_take_moment": "This may take a moment", "error": "Error", "something_wrong": "Something went wrong", diff --git a/surfsense_web/messages/zh.json b/surfsense_web/messages/zh.json index 6838b0f52..9bbbe1ecf 100644 --- a/surfsense_web/messages/zh.json +++ b/surfsense_web/messages/zh.json @@ -2,8 +2,6 @@ "common": { "app_name": "SurfSense", "welcome": "欢迎", - "loading": "加载中...", - "initializing": "正在初始化", "save": "保存", "cancel": "取消", "delete": "删除", @@ -80,8 +78,7 @@ "passwords_no_match_desc": "您输入的密码不一致", "creating_account": "正在创建您的账户", "creating_account_btn": "创建中", - "redirecting_login": "正在跳转到登录页面", - "processing_authentication": "正在处理身份验证" + "redirecting_login": "正在跳转到登录页面" }, "searchSpace": { "create_title": "创建搜索空间", @@ -131,10 +128,7 @@ "api_keys": "API 密钥", "profile": "个人资料", "loading_dashboard": "正在加载仪表盘", - "checking_auth": "正在检查身份验证", "loading_config": "正在加载配置", - "checking_llm_prefs": "正在检查您的 LLM 偏好设置", - "setting_up_ai": "正在设置 AI", "config_error": "配置错误", "failed_load_llm_config": "无法加载您的 LLM 配置", "error_loading_chats": "加载对话失败", @@ -156,7 +150,6 @@ "create_search_space": "创建搜索空间", "add_new_search_space": "添加新的搜索空间", "loading": "加载中", - "fetching_spaces": "正在获取您的搜索空间", "may_take_moment": "这可能需要一些时间", "error": "错误", "something_wrong": "出现错误",