mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
fix: remove message from loading UI
This commit is contained in:
parent
2434e64093
commit
ba304be977
13 changed files with 34 additions and 110 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 <ErrorScreen message={error?.message || "Failed to load search spaces"} />;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,29 +2,18 @@ import { atom } from "jotai";
|
|||
|
||||
interface GlobalLoadingState {
|
||||
isLoading: boolean;
|
||||
message?: string;
|
||||
variant: "login" | "default";
|
||||
}
|
||||
|
||||
export const globalLoadingAtom = atom<GlobalLoadingState>({
|
||||
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 });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<ElectricClient | null>(null);
|
||||
const [error, setError] = useState<Error | null>(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)
|
||||
|
|
|
|||
|
|
@ -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" ? (
|
||||
<div className="relative w-full h-full overflow-hidden bg-background">
|
||||
<AmbientBackground />
|
||||
<div className="mx-auto flex h-screen max-w-lg flex-col items-center justify-center">
|
||||
<Logo className="rounded-md" />
|
||||
<div className="mt-8 flex flex-col items-center space-y-4">
|
||||
<div className="h-12 w-12 flex items-center justify-center">
|
||||
{/* Spinner is always mounted, animation never resets */}
|
||||
<Spinner size="lg" className="text-muted-foreground" />
|
||||
</div>
|
||||
<span className="text-muted-foreground text-sm min-h-[1.25rem] text-center max-w-xs">
|
||||
{message}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-background">
|
||||
<div className="h-12 w-12 flex items-center justify-center">
|
||||
<Spinner size="lg" className="text-muted-foreground" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex min-h-screen flex-col items-center justify-center bg-background">
|
||||
<div className="flex flex-col items-center space-y-4">
|
||||
<div className="h-12 w-12 flex items-center justify-center">
|
||||
{/* Spinner is always mounted, animation never resets */}
|
||||
<Spinner size="xl" className="text-primary" />
|
||||
</div>
|
||||
<span className="text-muted-foreground text-sm min-h-[1.25rem] text-center max-w-md px-4">
|
||||
{message}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,21 +20,18 @@ let pendingHideTimeout: ReturnType<typeof setTimeout> | 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<number | null>(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(() => {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": "出现错误",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue