feat(FN-2260): merge fusion/fn-2260

This commit is contained in:
Fusion
2026-04-22 13:47:00 -07:00
committed by gsxdsm
parent 794da6a030
commit a1409389a2
32 changed files with 2419 additions and 423 deletions

View File

@@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from "react";
export type EmbedStatus = "unknown" | "loading" | "embedded" | "blocked" | "error";
export type EmbedDetectionMethod = "auto" | "manual" | null;
const BLOCKED_CONTEXT = "The server may block iframe embedding via X-Frame-Options or Content-Security-Policy headers. Browsers prevent detecting these headers from JavaScript.";
const ERROR_CONTEXT = "The preview URL could not be loaded. The server may not be running or the URL may be incorrect.";
@@ -8,17 +9,22 @@ const TIMEOUT_CONTEXT = "Preview is taking longer than expected to load. The ser
interface UsePreviewEmbedOptions {
loadTimeoutMs?: number;
detectionMethod?: EmbedDetectionMethod;
}
interface UsePreviewEmbedResult {
embedStatus: EmbedStatus;
setEmbedStatus: (status: EmbedStatus) => void;
resetEmbedStatus: () => void;
iframeRef: RefObject<HTMLIFrameElement | null>;
isEmbedded: boolean;
isBlocked: boolean;
embedContext: string | null;
blockReason: string | null;
detectionMethod: EmbedDetectionMethod;
iframeRef: RefObject<HTMLIFrameElement | null>;
resetEmbedStatus: () => void;
// Extended API for direct status control (backward compatibility)
setEmbedStatus: (status: EmbedStatus) => void;
retry: () => void;
// Legacy aliases for backward compatibility
embedContext: string | null;
handleIframeLoad: () => void;
handleIframeError: () => void;
resetEmbed: () => void;
@@ -39,12 +45,13 @@ function defaultContextForStatus(status: EmbedStatus): string | null {
}
export function usePreviewEmbed(url: string | null, options: UsePreviewEmbedOptions = {}): UsePreviewEmbedResult {
const { loadTimeoutMs = 10000 } = options;
const { loadTimeoutMs = 10000, detectionMethod: initialDetectionMethod = null } = options;
const iframeRef = useRef<HTMLIFrameElement | null>(null);
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const [embedStatus, setEmbedStatusState] = useState<EmbedStatus>("unknown");
const [embedContext, setEmbedContext] = useState<string | null>(null);
const [blockReason, setBlockReason] = useState<string | null>(null);
const [detectionMethod, setDetectionMethod] = useState<EmbedDetectionMethod>(initialDetectionMethod);
const clearLoadingTimeout = useCallback(() => {
if (timeoutRef.current !== null) {
@@ -55,12 +62,12 @@ export function usePreviewEmbed(url: string | null, options: UsePreviewEmbedOpti
const setEmbedStatus = useCallback((status: EmbedStatus) => {
setEmbedStatusState(status);
setEmbedContext(defaultContextForStatus(status));
setBlockReason(defaultContextForStatus(status));
}, []);
const setBlockedByTimeout = useCallback(() => {
setEmbedStatusState("blocked");
setEmbedContext(TIMEOUT_CONTEXT);
setBlockReason(TIMEOUT_CONTEXT);
}, []);
useEffect(() => {
@@ -68,12 +75,12 @@ export function usePreviewEmbed(url: string | null, options: UsePreviewEmbedOpti
if (!url) {
setEmbedStatusState("unknown");
setEmbedContext(null);
setBlockReason(null);
return;
}
setEmbedStatusState("unknown");
setEmbedContext(null);
setBlockReason(null);
let canceled = false;
queueMicrotask(() => {
@@ -81,7 +88,7 @@ export function usePreviewEmbed(url: string | null, options: UsePreviewEmbedOpti
return;
}
setEmbedStatusState("loading");
setEmbedContext(null);
setBlockReason(null);
});
return () => {
@@ -138,13 +145,13 @@ export function usePreviewEmbed(url: string | null, options: UsePreviewEmbedOpti
const resetEmbedStatus = useCallback(() => {
clearLoadingTimeout();
setEmbedStatusState("unknown");
setEmbedContext(null);
setBlockReason(null);
}, [clearLoadingTimeout]);
const retry = useCallback(() => {
clearLoadingTimeout();
setEmbedStatusState("unknown");
setEmbedContext(null);
setBlockReason(null);
}, [clearLoadingTimeout]);
const isEmbedded = useMemo(() => embedStatus === "embedded", [embedStatus]);
@@ -155,12 +162,15 @@ export function usePreviewEmbed(url: string | null, options: UsePreviewEmbedOpti
return {
embedStatus,
setEmbedStatus,
resetEmbedStatus,
iframeRef,
isEmbedded,
isBlocked,
embedContext,
blockReason,
detectionMethod,
iframeRef,
resetEmbedStatus,
// Legacy aliases
setEmbedStatus,
embedContext: blockReason, // Alias for backward compatibility
retry,
handleIframeLoad,
handleIframeError,