made mute button global
This commit is contained in:
parent
9d7e6f6cd2
commit
7a72117b6a
@ -45,6 +45,7 @@ import { usePageNavigationState } from '../hooks/usePageNavigationState';
|
||||
import { useTransitionPlayback } from '../hooks/useTransitionPlayback';
|
||||
import { useNetworkAware } from '../hooks/useNetworkAware';
|
||||
import { resolveAssetPlaybackUrl } from '../lib/assetUrl';
|
||||
import { presentationHasAudio } from '../lib/presentationAudio';
|
||||
import { downloadManager } from '../lib/offline/DownloadManager';
|
||||
import { isSafari } from '../lib/browserUtils';
|
||||
import { logger } from '../lib/logger';
|
||||
@ -817,39 +818,9 @@ export default function RuntimePresentation({
|
||||
selectedPage?.background_audio_end_time != null
|
||||
? parseFloat(String(selectedPage.background_audio_end_time))
|
||||
: null;
|
||||
const hasElementAudio = useMemo(
|
||||
() =>
|
||||
pageElements.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;
|
||||
}),
|
||||
[pageElements],
|
||||
const hasPresentationAudio = useMemo(
|
||||
() => presentationHasAudio(pages),
|
||||
[pages],
|
||||
);
|
||||
|
||||
// Global sound control starts muted for browser autoplay compatibility.
|
||||
@ -857,7 +828,7 @@ export default function RuntimePresentation({
|
||||
pageHasSound: pageVideoMuted === false,
|
||||
hasBackgroundVideo: Boolean(backgroundVideoUrl),
|
||||
hasBackgroundAudio: Boolean(backgroundAudioUrl),
|
||||
hasElementAudio,
|
||||
hasPresentationAudio,
|
||||
});
|
||||
|
||||
// Note: useBackgroundVideoPlayback is handled internally by CanvasBackground component
|
||||
|
||||
@ -24,6 +24,8 @@ export interface UseVideoSoundControlOptions {
|
||||
hasBackgroundAudio?: boolean;
|
||||
/** Whether page elements have hover/click audio effects or media player sound */
|
||||
hasElementAudio?: boolean;
|
||||
/** Whether any page in the presentation can produce sound */
|
||||
hasPresentationAudio?: boolean;
|
||||
}
|
||||
|
||||
export interface UseVideoSoundControlResult {
|
||||
@ -61,6 +63,7 @@ export function useVideoSoundControl({
|
||||
hasBackgroundVideo,
|
||||
hasBackgroundAudio = false,
|
||||
hasElementAudio = false,
|
||||
hasPresentationAudio = false,
|
||||
}: UseVideoSoundControlOptions): UseVideoSoundControlResult {
|
||||
const { isMuted } = useGlobalAudioMute();
|
||||
|
||||
@ -71,6 +74,7 @@ export function useVideoSoundControl({
|
||||
return {
|
||||
isMuted,
|
||||
showSoundButton:
|
||||
hasPresentationAudio ||
|
||||
(pageHasSound && hasBackgroundVideo) ||
|
||||
hasBackgroundAudio ||
|
||||
hasElementAudio,
|
||||
|
||||
75
frontend/src/lib/presentationAudio.ts
Normal file
75
frontend/src/lib/presentationAudio.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import type { CanvasElement, ConstructorSchema } from '../types/constructor';
|
||||
import type { TourPage } from '../types/entities';
|
||||
import { parseJsonObject } from './parseJson';
|
||||
|
||||
type PageWithAudioFields = Pick<
|
||||
TourPage,
|
||||
| 'background_audio_url'
|
||||
| 'background_video_url'
|
||||
| 'background_video_muted'
|
||||
| 'ui_schema_json'
|
||||
>;
|
||||
|
||||
const asRecords = (value: unknown): Record<string, unknown>[] =>
|
||||
Array.isArray(value)
|
||||
? value.filter(
|
||||
(item): item is Record<string, unknown> =>
|
||||
Boolean(item) && typeof item === 'object',
|
||||
)
|
||||
: [];
|
||||
|
||||
const elementHasAudio = (element: Partial<CanvasElement>): boolean => {
|
||||
if (element.hoverAudioUrl || element.clickAudioUrl) return true;
|
||||
|
||||
if (
|
||||
(element.type === 'audio_player' || element.type === 'video_player') &&
|
||||
element.mediaUrl &&
|
||||
!element.mediaMuted
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const galleryCards = asRecords(element.galleryCards);
|
||||
if (
|
||||
galleryCards.some(
|
||||
(card) => card.mediaType === 'video' || Boolean(card.videoUrl),
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const infoPanelSections = asRecords(element.infoPanelSections);
|
||||
if (
|
||||
infoPanelSections.some((section) =>
|
||||
asRecords(section.images).some(
|
||||
(item) => item.itemType === 'video' || Boolean(item.videoUrl),
|
||||
),
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const elementsHaveAudio = (
|
||||
elements: Array<Partial<CanvasElement>> = [],
|
||||
): boolean => elements.some(elementHasAudio);
|
||||
|
||||
export const pageHasAudio = (page: PageWithAudioFields): boolean => {
|
||||
if (page.background_audio_url) return true;
|
||||
if (page.background_video_url && page.background_video_muted === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const schema = parseJsonObject<ConstructorSchema>(
|
||||
page.ui_schema_json,
|
||||
{} as ConstructorSchema,
|
||||
);
|
||||
return Array.isArray(schema.elements) && elementsHaveAudio(schema.elements);
|
||||
};
|
||||
|
||||
export const presentationHasAudio = (
|
||||
pages: PageWithAudioFields[] = [],
|
||||
extraElements: Array<Partial<CanvasElement>> = [],
|
||||
): boolean => pages.some(pageHasAudio) || elementsHaveAudio(extraElements);
|
||||
@ -48,6 +48,7 @@ import { backgroundAudioController } from '../lib/backgroundAudioController';
|
||||
import { isSafari } from '../lib/browserUtils';
|
||||
import { resolveAssetPlaybackUrl } from '../lib/assetUrl';
|
||||
import { downloadManager } from '../lib/offline/DownloadManager';
|
||||
import { presentationHasAudio } from '../lib/presentationAudio';
|
||||
import { parseJsonObject } from '../lib/parseJson';
|
||||
import {
|
||||
resolveNavigationTarget,
|
||||
@ -366,39 +367,18 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
||||
[elements],
|
||||
);
|
||||
|
||||
const hasElementAudio = useMemo(
|
||||
const hasPresentationAudio = 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],
|
||||
presentationHasAudio(pages, elements) ||
|
||||
Boolean(backgroundAudioUrl) ||
|
||||
Boolean(backgroundVideoUrl && backgroundVideoMuted === false),
|
||||
[
|
||||
pages,
|
||||
elements,
|
||||
backgroundAudioUrl,
|
||||
backgroundVideoUrl,
|
||||
backgroundVideoMuted,
|
||||
],
|
||||
);
|
||||
|
||||
// Global sound control starts muted for browser autoplay compatibility.
|
||||
@ -406,7 +386,7 @@ const ConstructorPage = ({ mode = 'constructor' }: ConstructorPageProps) => {
|
||||
pageHasSound: backgroundVideoMuted === false,
|
||||
hasBackgroundVideo: Boolean(backgroundVideoUrl),
|
||||
hasBackgroundAudio: Boolean(backgroundAudioUrl),
|
||||
hasElementAudio,
|
||||
hasPresentationAudio,
|
||||
});
|
||||
|
||||
// Look up current element for gallery carousel (so it receives updates from element editor)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user