From 556a0c070021640e6c96508474f80211a36200f8 Mon Sep 17 00:00:00 2001 From: Dmitri Date: Tue, 14 Apr 2026 19:36:36 +0400 Subject: [PATCH] unified URLs backgrounds flow --- .../src/components/RuntimePresentation.tsx | 14 ++- frontend/src/hooks/useBackgroundUrls.ts | 103 ++++++++++++++++++ frontend/src/pages/constructor.tsx | 25 +++-- 3 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 frontend/src/hooks/useBackgroundUrls.ts diff --git a/frontend/src/components/RuntimePresentation.tsx b/frontend/src/components/RuntimePresentation.tsx index 66efe48..9aeff65 100644 --- a/frontend/src/components/RuntimePresentation.tsx +++ b/frontend/src/components/RuntimePresentation.tsx @@ -40,6 +40,7 @@ import { import { usePageSwitch } from '../hooks/usePageSwitch'; import { useTransitionPlayback } from '../hooks/useTransitionPlayback'; import { useBackgroundTransition } from '../hooks/useBackgroundTransition'; +import { useBackgroundUrls } from '../hooks/useBackgroundUrls'; import { resolveAssetPlaybackUrl } from '../lib/assetUrl'; import { logger } from '../lib/logger'; import { @@ -475,10 +476,15 @@ export default function RuntimePresentation({ [preloadOrchestrator], ); - // Use resolved URLs from shared hook (blob URLs if cached, otherwise original URLs) - // Blob URLs render instantly since data is local in memory - const backgroundImageUrl = pageSwitch.currentBgImageUrl; - const backgroundVideoUrl = pageSwitch.currentBgVideoUrl; + // Unified background URL resolution via shared hook (same as constructor) + // No localPaths needed since RuntimePresentation has no editing mode + const { + backgroundImageSrc: backgroundImageUrl, + backgroundVideoSrc: backgroundVideoUrl, + } = useBackgroundUrls({ + pageSwitch, + resolveUrl: resolveUrlWithBlob, + }); // Background video playback settings from selected page const videoAutoplay = selectedPage?.background_video_autoplay ?? true; diff --git a/frontend/src/hooks/useBackgroundUrls.ts b/frontend/src/hooks/useBackgroundUrls.ts new file mode 100644 index 0000000..f7363b1 --- /dev/null +++ b/frontend/src/hooks/useBackgroundUrls.ts @@ -0,0 +1,103 @@ +/** + * useBackgroundUrls Hook + * + * Unified hook for resolving background display URLs. + * Used by both constructor and RuntimePresentation to ensure + * consistent URL resolution and timing. + * + * Priority: + * 1. Local storage paths (for constructor editing) resolved via blob cache + * 2. pageSwitch URLs (for navigation, already resolved) + * + * This ensures both views use the same logic for background URL resolution. + */ + +import { useMemo } from 'react'; +import type { UsePageSwitchResult } from './usePageSwitch'; + +/** + * Function type for resolving storage paths to display URLs + */ +export type UrlResolver = (storagePath: string) => string; + +export interface UseBackgroundUrlsOptions { + /** Page switch hook result */ + pageSwitch: Pick< + UsePageSwitchResult, + 'currentBgImageUrl' | 'currentBgVideoUrl' | 'currentBgAudioUrl' + >; + /** URL resolver function (typically from preload orchestrator) */ + resolveUrl: UrlResolver; + /** Local storage paths - used for constructor editing override */ + localPaths?: { + imageUrl?: string; + videoUrl?: string; + audioUrl?: string; + }; +} + +export interface UseBackgroundUrlsResult { + /** Resolved display URL for background image */ + backgroundImageSrc: string; + /** Resolved display URL for background video */ + backgroundVideoSrc: string; + /** Resolved display URL for background audio */ + backgroundAudioSrc: string; +} + +/** + * Hook for unified background URL resolution. + * + * @example + * // Constructor - with local editing state + * const { backgroundImageSrc, backgroundVideoSrc, backgroundAudioSrc } = useBackgroundUrls({ + * pageSwitch, + * resolveUrl: resolveUrlWithBlob, + * localPaths: { + * imageUrl: backgroundImageUrl, + * videoUrl: backgroundVideoUrl, + * audioUrl: backgroundAudioUrl, + * }, + * }); + * + * @example + * // RuntimePresentation - navigation only + * const { backgroundImageSrc, backgroundVideoSrc, backgroundAudioSrc } = useBackgroundUrls({ + * pageSwitch, + * resolveUrl: resolveUrlWithBlob, + * }); + */ +export function useBackgroundUrls({ + pageSwitch, + resolveUrl, + localPaths, +}: UseBackgroundUrlsOptions): UseBackgroundUrlsResult { + // Memoize resolved URLs to avoid unnecessary recalculations + const backgroundImageSrc = useMemo(() => { + // Priority: local path (editing) > pageSwitch (navigation) + if (localPaths?.imageUrl) { + return resolveUrl(localPaths.imageUrl); + } + return pageSwitch.currentBgImageUrl; + }, [localPaths?.imageUrl, pageSwitch.currentBgImageUrl, resolveUrl]); + + const backgroundVideoSrc = useMemo(() => { + if (localPaths?.videoUrl) { + return resolveUrl(localPaths.videoUrl); + } + return pageSwitch.currentBgVideoUrl; + }, [localPaths?.videoUrl, pageSwitch.currentBgVideoUrl, resolveUrl]); + + const backgroundAudioSrc = useMemo(() => { + if (localPaths?.audioUrl) { + return resolveUrl(localPaths.audioUrl); + } + return pageSwitch.currentBgAudioUrl; + }, [localPaths?.audioUrl, pageSwitch.currentBgAudioUrl, resolveUrl]); + + return { + backgroundImageSrc, + backgroundVideoSrc, + backgroundAudioSrc, + }; +} diff --git a/frontend/src/pages/constructor.tsx b/frontend/src/pages/constructor.tsx index 3267a28..bad89aa 100644 --- a/frontend/src/pages/constructor.tsx +++ b/frontend/src/pages/constructor.tsx @@ -26,6 +26,7 @@ import { usePageSwitch } from '../hooks/usePageSwitch'; import { usePageNavigation } from '../hooks/usePageNavigation'; import { useTransitionPlayback } from '../hooks/useTransitionPlayback'; import { useBackgroundTransition } from '../hooks/useBackgroundTransition'; +import { useBackgroundUrls } from '../hooks/useBackgroundUrls'; import { logger } from '../lib/logger'; import { resolveAssetPlaybackUrl } from '../lib/assetUrl'; import { parseJsonObject } from '../lib/parseJson'; @@ -1216,17 +1217,19 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => { ); const canvasBackgroundStyle: React.CSSProperties = {}; - // Use user's selection (backgroundImageUrl etc) as source of truth for display. - // Resolve via blob cache when available, fall back to pageSwitch state during transitions. - const backgroundImageSrc = backgroundImageUrl - ? resolveUrlWithBlob(backgroundImageUrl) - : pageSwitch.currentBgImageUrl; - const backgroundVideoSrc = backgroundVideoUrl - ? resolveUrlWithBlob(backgroundVideoUrl) - : pageSwitch.currentBgVideoUrl; - const backgroundAudioSrc = backgroundAudioUrl - ? resolveUrlWithBlob(backgroundAudioUrl) - : pageSwitch.currentBgAudioUrl; + + // Unified background URL resolution via shared hook + // Priority: local paths (editing) > pageSwitch (navigation) + const { backgroundImageSrc, backgroundVideoSrc, backgroundAudioSrc } = + useBackgroundUrls({ + pageSwitch, + resolveUrl: resolveUrlWithBlob, + localPaths: { + imageUrl: backgroundImageUrl, + videoUrl: backgroundVideoUrl, + audioUrl: backgroundAudioUrl, + }, + }); const hasEditorSelection = isConstructorEditMode &&