unified URLs backgrounds flow

This commit is contained in:
Dmitri 2026-04-14 19:36:36 +04:00
parent a6de052952
commit 556a0c0700
3 changed files with 127 additions and 15 deletions

View File

@ -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;

View File

@ -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,
};
}

View File

@ -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 &&