返回 presentation-ai
usePresentingLoadingGate.ts
根目录 / src / hooks / presentation / usePresentingLoadingGate.ts
1 "use client";
2
3 import { useCallback, useEffect, useRef, useState } from "react";
4
5 interface UsePresentingLoadingGateOptions {
6 isPresenting: boolean;
7 isPresentingLoading: boolean;
8 slideIds: string[];
9 presentingScaleLocks: Record<string, boolean>;
10 setIsPresentingLoading: (isLoading: boolean) => void;
11 clearDelayMs?: number;
12 }
13
14 export function usePresentingLoadingGate({
15 isPresenting,
16 isPresentingLoading,
17 slideIds,
18 presentingScaleLocks,
19 setIsPresentingLoading,
20 clearDelayMs = 150,
21 }: UsePresentingLoadingGateOptions) {
22 const [isLayoutStable, setIsLayoutStable] = useState(false);
23 const clearLoadingTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
24 null,
25 );
26
27 const cancelClearLoading = useCallback(() => {
28 if (clearLoadingTimeoutRef.current) {
29 clearTimeout(clearLoadingTimeoutRef.current);
30 clearLoadingTimeoutRef.current = null;
31 }
32 }, []);
33
34 useEffect(() => {
35 if (!isPresenting || !isPresentingLoading) {
36 setIsLayoutStable(false);
37 cancelClearLoading();
38 return;
39 }
40
41 let rafId = 0;
42 let stableFrames = 0;
43 let lastWidth = 0;
44 let lastHeight = 0;
45
46 const measure = () => {
47 const container = document.querySelector<HTMLElement>(
48 ".presentation-slides",
49 );
50
51 if (container) {
52 const width = Math.max(container.scrollWidth, container.clientWidth);
53 const height = Math.max(container.scrollHeight, container.clientHeight);
54
55 if (width === lastWidth && height === lastHeight) {
56 stableFrames += 1;
57 } else {
58 stableFrames = 0;
59 lastWidth = width;
60 lastHeight = height;
61 }
62
63 if (stableFrames >= 2) {
64 setIsLayoutStable(true);
65 return;
66 }
67 }
68
69 rafId = window.requestAnimationFrame(measure);
70 };
71
72 rafId = window.requestAnimationFrame(measure);
73 return () => {
74 if (rafId) {
75 window.cancelAnimationFrame(rafId);
76 }
77 };
78 }, [cancelClearLoading, isPresenting, isPresentingLoading, slideIds.length]);
79
80 useEffect(() => {
81 if (!isPresenting || !isPresentingLoading) return;
82
83 const allLocked = slideIds.length
84 ? slideIds.every((slideId) => presentingScaleLocks[slideId])
85 : true;
86 const shouldClearLoading = allLocked && isLayoutStable;
87
88 if (!shouldClearLoading) {
89 cancelClearLoading();
90 return;
91 }
92
93 cancelClearLoading();
94 clearLoadingTimeoutRef.current = setTimeout(() => {
95 setIsPresentingLoading(false);
96 }, clearDelayMs);
97 }, [
98 cancelClearLoading,
99 clearDelayMs,
100 isLayoutStable,
101 isPresenting,
102 isPresentingLoading,
103 presentingScaleLocks,
104 setIsPresentingLoading,
105 slideIds,
106 ]);
107 }
108
108 lines TYPESCRIPT