fix(web): wait for image to load before playing memories. (#19757)

This commit is contained in:
Dag Stuan 2025-08-31 15:50:33 +02:00 committed by GitHub
parent 03dafba522
commit fd2b7a344c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 10 deletions

View File

@ -10,26 +10,28 @@
interface Props { interface Props {
asset: TimelineAsset; asset: TimelineAsset;
onImageLoad: () => void;
} }
const { asset }: Props = $props(); const { asset, onImageLoad }: Props = $props();
let assetFileUrl: string = $state(''); let assetFileUrl: string = $state('');
let imageLoaded: boolean = $state(false); let imageLoaded: boolean = $state(false);
let loader = $state<HTMLImageElement>(); let loader = $state<HTMLImageElement>();
const onload = () => { const onLoadCallback = () => {
imageLoaded = true; imageLoaded = true;
assetFileUrl = imageLoaderUrl; assetFileUrl = imageLoaderUrl;
onImageLoad();
}; };
onMount(() => { onMount(() => {
if (loader?.complete) { if (loader?.complete) {
onload(); onLoadCallback();
} }
loader?.addEventListener('load', onload); loader?.addEventListener('load', onLoadCallback);
return () => { return () => {
loader?.removeEventListener('load', onload); loader?.removeEventListener('load', onLoadCallback);
}; };
}); });

View File

@ -84,6 +84,7 @@
let progressBarController: Tween<number> | undefined = $state(undefined); let progressBarController: Tween<number> | undefined = $state(undefined);
let videoPlayer: HTMLVideoElement | undefined = $state(); let videoPlayer: HTMLVideoElement | undefined = $state();
const asHref = (asset: { id: string }) => `?${QueryParameter.ID}=${asset.id}`; const asHref = (asset: { id: string }) => `?${QueryParameter.ID}=${asset.id}`;
const handleNavigate = async (asset?: { id: string }) => { const handleNavigate = async (asset?: { id: string }) => {
if ($isViewing) { if ($isViewing) {
return asset; return asset;
@ -95,6 +96,7 @@
await goto(asHref(asset)); await goto(asHref(asset));
}; };
const setProgressDuration = (asset: TimelineAsset) => { const setProgressDuration = (asset: TimelineAsset) => {
if (asset.isVideo) { if (asset.isVideo) {
const timeParts = asset.duration!.split(':').map(Number); const timeParts = asset.duration!.split(':').map(Number);
@ -108,6 +110,7 @@
}); });
} }
}; };
const handleNextAsset = () => handleNavigate(current?.next?.asset); const handleNextAsset = () => handleNavigate(current?.next?.asset);
const handlePreviousAsset = () => handleNavigate(current?.previous?.asset); const handlePreviousAsset = () => handleNavigate(current?.previous?.asset);
const handleNextMemory = () => handleNavigate(current?.nextMemory?.assets[0]); const handleNextMemory = () => handleNavigate(current?.nextMemory?.assets[0]);
@ -115,6 +118,7 @@
const handleEscape = async () => goto(AppRoute.PHOTOS); const handleEscape = async () => goto(AppRoute.PHOTOS);
const handleSelectAll = () => const handleSelectAll = () =>
assetInteraction.selectAssets(current?.memory.assets.map((a) => toTimelineAsset(a)) || []); assetInteraction.selectAssets(current?.memory.assets.map((a) => toTimelineAsset(a)) || []);
const handleAction = async (callingContext: string, action: 'reset' | 'pause' | 'play') => { const handleAction = async (callingContext: string, action: 'reset' | 'pause' | 'play') => {
// leaving these log statements here as comments. Very useful to figure out what's going on during dev! // leaving these log statements here as comments. Very useful to figure out what's going on during dev!
// console.log(`handleAction[${callingContext}] called with: ${action}`); // console.log(`handleAction[${callingContext}] called with: ${action}`);
@ -154,6 +158,7 @@
} }
} }
}; };
const handleProgress = async (progress: number) => { const handleProgress = async (progress: number) => {
if (!progressBarController) { if (!progressBarController) {
return; return;
@ -184,6 +189,7 @@
memoryStore.hideAssetsFromMemory(ids); memoryStore.hideAssetsFromMemory(ids);
init(page); init(page);
}; };
const handleDeleteMemoryAsset = async () => { const handleDeleteMemoryAsset = async () => {
if (!current) { if (!current) {
return; return;
@ -192,6 +198,7 @@
await memoryStore.deleteAssetFromMemory(current.asset.id); await memoryStore.deleteAssetFromMemory(current.asset.id);
init(page); init(page);
}; };
const handleDeleteMemory = async () => { const handleDeleteMemory = async () => {
if (!current) { if (!current) {
return; return;
@ -201,6 +208,7 @@
notificationController.show({ message: $t('removed_memory'), type: NotificationType.Info }); notificationController.show({ message: $t('removed_memory'), type: NotificationType.Info });
init(page); init(page);
}; };
const handleSaveMemory = async () => { const handleSaveMemory = async () => {
if (!current) { if (!current) {
return; return;
@ -214,10 +222,12 @@
}); });
init(page); init(page);
}; };
const handleGalleryScrollsIntoView = () => { const handleGalleryScrollsIntoView = () => {
galleryInView = true; galleryInView = true;
handlePromiseError(handleAction('galleryInView', 'pause')); handlePromiseError(handleAction('galleryInView', 'pause'));
}; };
const handleGalleryScrollsOutOfView = () => { const handleGalleryScrollsOutOfView = () => {
galleryInView = false; galleryInView = false;
// only call play after the first page load. When page first loads the gallery will not be visible // only call play after the first page load. When page first loads the gallery will not be visible
@ -246,16 +256,22 @@
playerInitialized = false; playerInitialized = false;
}; };
const resetAndPlay = () => {
handlePromiseError(handleAction('resetAndPlay', 'reset'));
handlePromiseError(handleAction('resetAndPlay', 'play'));
};
const initPlayer = () => { const initPlayer = () => {
const isVideoAssetButPlayerHasNotLoadedYet = current && current.asset.isVideo && !videoPlayer; const isVideo = current && current.asset.isVideo;
const isVideoAssetButPlayerHasNotLoadedYet = isVideo && !videoPlayer;
if (playerInitialized || isVideoAssetButPlayerHasNotLoadedYet) { if (playerInitialized || isVideoAssetButPlayerHasNotLoadedYet) {
return; return;
} }
if ($isViewing) { if ($isViewing) {
handlePromiseError(handleAction('initPlayer[AssetViewOpen]', 'pause')); handlePromiseError(handleAction('initPlayer[AssetViewOpen]', 'pause'));
} else { } else if (isVideo) {
handlePromiseError(handleAction('initPlayer[AssetViewClosed]', 'reset')); // Image assets will start playing when the image is loaded. Only autostart video assets.
handlePromiseError(handleAction('initPlayer[AssetViewClosed]', 'play')); resetAndPlay();
} }
playerInitialized = true; playerInitialized = true;
}; };
@ -474,7 +490,7 @@
videoViewerVolume={$videoViewerVolume} videoViewerVolume={$videoViewerVolume}
/> />
{:else} {:else}
<MemoryPhotoViewer asset={current.asset} /> <MemoryPhotoViewer asset={current.asset} onImageLoad={resetAndPlay} />
{/if} {/if}
{/key} {/key}