[WIP] Fix bug loading movies

This was a fun journey! Turns out Next 12 is using a new faster JS compiler called SWC, which had a compiler bug that triggered here!

The incorrect looping behavior caused `libraryUrl` to sometimes be `null` by the time the movie promise completes, because `layer` was set to whatever the `last` layer in the list had been. https://github.com/swc-project/swc/issues/2624

Anyway, turns out this code has been through a few refactors, and the `async` function wrapper is extraneous now! So I've just deleted it and inlined its code. Ta da! lol
This commit is contained in:
Emi Matchu 2021-11-02 00:11:23 -07:00
parent 4af2719098
commit 7fd85e5e2a

View file

@ -388,83 +388,79 @@ export function usePreloadLayers(layers) {
setError(null);
setLayersHaveAnimations(false);
const loadAssets = async () => {
const minimalAssetPromises = [];
const imageAssetPromises = [];
const movieAssetPromises = [];
for (const layer of layers) {
const imageAssetPromise = loadImage(
getBestImageUrlForLayer(layer, { hiResMode })
const minimalAssetPromises = [];
const imageAssetPromises = [];
const movieAssetPromises = [];
for (const layer of layers) {
const imageAssetPromise = loadImage(
getBestImageUrlForLayer(layer, { hiResMode })
);
imageAssetPromises.push(imageAssetPromise);
if (layer.canvasMovieLibraryUrl) {
// Start preloading the movie. But we won't block on it! The blocking
// request will still be the image, which we'll show as a
// placeholder, which should usually be noticeably faster!
const movieLibraryPromise = loadMovieLibrary(
layer.canvasMovieLibraryUrl
);
imageAssetPromises.push(imageAssetPromise);
const movieAssetPromise = movieLibraryPromise.then((library) => ({
library,
libraryUrl: layer.canvasMovieLibraryUrl,
}));
movieAssetPromise.libraryUrl = layer.canvasMovieLibraryUrl;
movieAssetPromise.cancel = () => movieLibraryPromise.cancel();
movieAssetPromises.push(movieAssetPromise);
if (layer.canvasMovieLibraryUrl) {
// Start preloading the movie. But we won't block on it! The blocking
// request will still be the image, which we'll show as a
// placeholder, which should usually be noticeably faster!
const movieLibraryPromise = loadMovieLibrary(
layer.canvasMovieLibraryUrl
);
const movieAssetPromise = movieLibraryPromise.then((library) => ({
library,
libraryUrl: layer.canvasMovieLibraryUrl,
}));
movieAssetPromise.libraryUrl = layer.canvasMovieLibraryUrl;
movieAssetPromise.cancel = () => movieLibraryPromise.cancel();
movieAssetPromises.push(movieAssetPromise);
// The minimal asset for the movie case is *either* the image *or*
// the movie, because we can start rendering when either is ready.
minimalAssetPromises.push(
Promise.any([imageAssetPromise, movieAssetPromise])
);
} else {
minimalAssetPromises.push(imageAssetPromise);
}
}
// The minimal asset for the movie case is *either* the image *or*
// the movie, because we can start rendering when either is ready.
minimalAssetPromises.push(
Promise.any([imageAssetPromise, movieAssetPromise])
);
} else {
minimalAssetPromises.push(imageAssetPromise);
}
// When the minimal assets have loaded, we can say the layers have
// loaded, and allow the UI to start showing them!
Promise.all(minimalAssetPromises)
.then(() => {
if (canceled) return;
setLoadedLayers(layers);
})
.catch((e) => {
if (canceled) return;
console.error("Error preloading outfit layers", e);
setError(e);
// Cancel any remaining promises, if cancelable.
imageAssetPromises.forEach((p) => p.cancel && p.cancel());
movieAssetPromises.forEach((p) => p.cancel && p.cancel());
});
// As the movie assets come in, check them for animations, to decide
// whether to show the Play/Pause button.
const checkHasAnimations = (asset) => {
if (canceled) return;
let assetHasAnimations;
try {
assetHasAnimations = getHasAnimationsForMovieAsset(asset);
} catch (e) {
console.error("Error testing layers for animations", e);
setError(e);
return;
}
// When the minimal assets have loaded, we can say the layers have
// loaded, and allow the UI to start showing them!
Promise.all(minimalAssetPromises)
.then(() => {
if (canceled) return;
setLoadedLayers(layers);
})
.catch((e) => {
if (canceled) return;
console.error("Error preloading outfit layers", e);
setError(e);
// Cancel any remaining promises, if cancelable.
imageAssetPromises.forEach((p) => p.cancel && p.cancel());
movieAssetPromises.forEach((p) => p.cancel && p.cancel());
});
// As the movie assets come in, check them for animations, to decide
// whether to show the Play/Pause button.
const checkHasAnimations = (asset) => {
if (canceled) return;
let assetHasAnimations;
try {
assetHasAnimations = getHasAnimationsForMovieAsset(asset);
} catch (e) {
console.error("Error testing layers for animations", e);
setError(e);
return;
}
setLayersHaveAnimations(
(alreadyHasAnimations) => alreadyHasAnimations || assetHasAnimations
);
};
movieAssetPromises.forEach((p) =>
p.then(checkHasAnimations).catch((e) => {
console.error(`Error preloading movie library ${p.libraryUrl}:`, e);
})
setLayersHaveAnimations(
(alreadyHasAnimations) => alreadyHasAnimations || assetHasAnimations
);
};
loadAssets();
movieAssetPromises.forEach((p) =>
p.then(checkHasAnimations).catch((e) => {
console.error(`Error preloading movie library ${p.libraryUrl}:`, e);
})
);
return () => {
canceled = true;