fixed unmute for constructor

This commit is contained in:
Dmitri 2026-06-15 09:34:52 +02:00
parent c3d949702c
commit 9d7e6f6cd2
2 changed files with 93 additions and 20 deletions

View File

@ -46,6 +46,10 @@ interface RuntimeControlsProps {
isMuted?: boolean;
/** Callback to toggle all presentation sound on/off */
onSoundToggle?: () => void;
/** Whether to show the offline download button */
showOfflineButton?: boolean;
/** Whether to show the fullscreen button */
showFullscreenButton?: boolean;
}
/**
@ -468,6 +472,8 @@ export default function RuntimeControls({
showSoundButton = false,
isMuted = true,
onSoundToggle,
showOfflineButton = true,
showFullscreenButton = true,
}: RuntimeControlsProps) {
// Counter-scale to resist pinch-zoom
const counterScale = useCounterZoom();
@ -520,18 +526,22 @@ export default function RuntimeControls({
onTouchEnd={stopControlEvent}
onTouchEndCapture={stopControlEvent}
>
<OfflineControl
projectId={projectId}
projectSlug={projectSlug}
projectName={projectName}
pages={pages}
/>
<ControlButton
icon={isFullscreen ? mdiFullscreenExit : mdiFullscreen}
color='info'
onClick={toggleFullscreen}
title={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}
/>
{showOfflineButton && (
<OfflineControl
projectId={projectId}
projectSlug={projectSlug}
projectName={projectName}
pages={pages}
/>
)}
{showFullscreenButton && (
<ControlButton
icon={isFullscreen ? mdiFullscreenExit : mdiFullscreen}
color='info'
onClick={toggleFullscreen}
title={isFullscreen ? 'Exit fullscreen' : 'Enter fullscreen'}
/>
)}
{showSoundButton && onSoundToggle && (
<ControlButton
icon={isMuted ? mdiVolumeOff : mdiVolumeHigh}

View File

@ -18,6 +18,7 @@ import TransitionBlackOverlay from '../components/TransitionBlackOverlay';
import ConstructorToolbar from '../components/Constructor/ConstructorToolbar';
import TransitionPreviewOverlay from '../components/Constructor/TransitionPreviewOverlay';
import CanvasElementComponent from '../components/Constructor/CanvasElement';
import RuntimeControls from '../components/Runtime/RuntimeControls';
import GalleryCarouselOverlay from '../components/UiElements/GalleryCarouselOverlay';
import InfoPanelOverlay from '../components/UiElements/InfoPanelOverlay';
import ImageDetailPanel from '../components/UiElements/ImageDetailPanel';
@ -198,7 +199,12 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
});
// Canvas scale for responsive UI elements and letterbox mode
const { cssVars: canvasCssVars, letterboxStyles } = useCanvasScale({
const {
cssVars: canvasCssVars,
letterboxStyles,
canvasWidth,
canvasHeight,
} = useCanvasScale({
designWidth: project?.design_width,
designHeight: project?.design_height,
});
@ -241,13 +247,6 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
backgroundAudioEndTime,
} = usePageBackground();
// Global sound control starts muted for browser autoplay compatibility.
const soundControl = useVideoSoundControl({
pageHasSound: backgroundVideoMuted === false,
hasBackgroundVideo: Boolean(backgroundVideoUrl),
hasBackgroundAudio: Boolean(backgroundAudioUrl),
});
// Network-aware transitions: skip video on slow networks, use CSS fade instead
const { shouldUseVideoTransitions, networkInfo } = useNetworkAware();
@ -367,6 +366,49 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
[elements],
);
const hasElementAudio = useMemo(
() =>
elements.some((element) => {
if (element.hoverAudioUrl || element.clickAudioUrl) return true;
if (
(element.type === 'audio_player' ||
element.type === 'video_player') &&
element.mediaUrl &&
!element.mediaMuted
) {
return true;
}
if (
element.galleryCards?.some(
(card: GalleryCarouselMediaItem) =>
card.mediaType === 'video' || Boolean(card.videoUrl),
)
) {
return true;
}
if (
element.infoPanelSections?.some((section) =>
section.images?.some(
(item: InfoPanelImage) =>
item.itemType === 'video' || Boolean(item.videoUrl),
),
)
) {
return true;
}
return false;
}),
[elements],
);
// Global sound control starts muted for browser autoplay compatibility.
const soundControl = useVideoSoundControl({
pageHasSound: backgroundVideoMuted === false,
hasBackgroundVideo: Boolean(backgroundVideoUrl),
hasBackgroundAudio: Boolean(backgroundAudioUrl),
hasElementAudio,
});
// Look up current element for gallery carousel (so it receives updates from element editor)
const activeGalleryCarouselElement = useMemo(() => {
if (!activeGalleryCarousel) return null;
@ -2184,6 +2226,27 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
</BackdropPortalProvider>
</div>
{!isConstructorEditMode &&
!activeGalleryCarousel &&
!activeInfoPanelGallery &&
soundControl.showSoundButton && (
<RuntimeControls
projectId={projectId || null}
projectSlug=''
projectName={projectName}
pages={pages}
isFullscreen={false}
toggleFullscreen={() => undefined}
canvasWidth={canvasWidth}
canvasHeight={canvasHeight}
showOfflineButton={false}
showFullscreenButton={false}
showSoundButton={soundControl.showSoundButton}
isMuted={soundControl.isMuted}
onSoundToggle={soundControl.toggleSound}
/>
)}
{/* ElementEditorPanel now uses ConstructorContext for all state */}
{pages.length > 0 && hasEditorSelection && (
<ElementEditorPanel