6ede33ccdb
Build and Push Docker Images / create_manifest (web, surfsense-web, , cpu) (push) Has been cancelled
Build and Push Docker Images / finalize_release (push) Has been cancelled
Obsidian Plugin Lint / lint (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-24.04-arm, linux/arm64, arm64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_web, cpu, ./surfsense_web/Dockerfile, web, surfsense-web, ubuntu-latest, linux/amd64, amd64, , runner, false, cpu) (push) Has been cancelled
Build and Push Docker Images / compute_version (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cpu, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, , production, false, cpu) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu126, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda126, production, true, cuda126) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-24.04-arm, linux/arm64, arm64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / build (./surfsense_backend, cu128, ./surfsense_backend/Dockerfile, backend, surfsense-backend, ubuntu-latest, linux/amd64, amd64, -cuda, production, true, cuda) (push) Has been cancelled
Build and Push Docker Images / verify_digests (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, , cpu) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda, cuda) (push) Has been cancelled
Build and Push Docker Images / create_manifest (backend, surfsense-backend, -cuda126, cuda126) (push) Has been cancelled
96 lines
2.9 KiB
TypeScript
96 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
import { useAtom } from "jotai";
|
|
import { useCallback, useEffect, useRef } from "react";
|
|
import { globalLoadingAtom } from "@/atoms/ui/loading.atoms";
|
|
|
|
// Global counter to generate unique IDs for each loading request
|
|
let loadingIdCounter = 0;
|
|
|
|
// Track the current active loading ID globally
|
|
let currentLoadingId: number | null = null;
|
|
|
|
// Pending hide timeout - allows new loading requests to take over before hiding
|
|
let pendingHideTimeout: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
/**
|
|
* Hook to control the global loading screen.
|
|
* The spinner is always mounted in the DOM to prevent animation reset.
|
|
*/
|
|
export function useGlobalLoading() {
|
|
const [loading, setLoading] = useAtom(globalLoadingAtom);
|
|
|
|
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 });
|
|
return id;
|
|
}, [setLoading]);
|
|
|
|
const hide = useCallback(
|
|
(id?: number) => {
|
|
// Only hide if this is the current loading, or if no ID provided (force hide)
|
|
if (id === undefined || id === currentLoadingId) {
|
|
// Use a small delay to allow React to flush pending mounts
|
|
// This prevents flash when transitioning between loading states
|
|
if (pendingHideTimeout) {
|
|
clearTimeout(pendingHideTimeout);
|
|
}
|
|
|
|
pendingHideTimeout = setTimeout(() => {
|
|
// Double-check we're still the current loading after the delay
|
|
if (id === undefined || id === currentLoadingId) {
|
|
currentLoadingId = null;
|
|
setLoading({ isLoading: false });
|
|
}
|
|
pendingHideTimeout = null;
|
|
}, 50); // Small delay to allow next component to mount and show loading
|
|
}
|
|
},
|
|
[setLoading]
|
|
);
|
|
|
|
return { show, hide, isLoading: loading.isLoading };
|
|
}
|
|
|
|
/**
|
|
* Hook that automatically shows/hides the global loading screen based on a condition.
|
|
* Useful for components that show loading on mount and hide on unmount.
|
|
*
|
|
* Uses ownership tracking to prevent flashes when multiple components
|
|
* transition loading states (e.g., layout → page).
|
|
*
|
|
* @param shouldShow - Whether the loading screen should be visible
|
|
*/
|
|
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();
|
|
} else if (loadingIdRef.current !== null) {
|
|
// Only hide if we were the ones showing loading
|
|
hide(loadingIdRef.current);
|
|
loadingIdRef.current = null;
|
|
}
|
|
}, [shouldShow, show, hide]);
|
|
|
|
// Cleanup on unmount - only hide if we're still the active loading
|
|
useEffect(() => {
|
|
return () => {
|
|
if (loadingIdRef.current !== null) {
|
|
hide(loadingIdRef.current);
|
|
loadingIdRef.current = null;
|
|
}
|
|
};
|
|
}, [hide]);
|
|
}
|