Reset failures

This commit is contained in:
Manav Rathi 2025-02-18 12:11:25 +05:30
parent f47837f550
commit b3100f098b
No known key found for this signature in database
4 changed files with 80 additions and 90 deletions

View File

@ -147,15 +147,12 @@ body {
overflow: hidden;
width: 50px;
height: 60px;
/* "visibility" is used to toggle visibility, opacity is fixed to be similar
to that of the loading indicator when it is visible. */
opacity: 0.5;
/* Unlike the loading indicator, "display" is used to toggle visibility, and
the opacity is fixed to be similar to that of the counter. */
opacity: 0.85;
display: none;
}
.pswp-ente .pswp__error .pswp__icn {
visibility: hidden;
}
.pswp-ente .pswp__error--active .pswp__icn {
visibility: visible;
.pswp-ente .pswp__error--active {
display: initial;
}

View File

@ -154,7 +154,7 @@ export const itemDataForFile = (file: EnteFile, needsRefresh: () => void) => {
// point of time. This assumption is currently valid.
_state.needsRefreshByFileID.set(file.id, needsRefresh);
if (!itemData || itemData.failureReason) {
if (!itemData) {
itemData = {};
_state.itemDataByFileID.set(file.id, itemData);
void enqueueUpdates(file);
@ -163,24 +163,37 @@ export const itemDataForFile = (file: EnteFile, needsRefresh: () => void) => {
return itemData;
};
/**
* Reset any failure reasons for the given {@link file}.
*
* This is called when the user moves away from a slide, so that when the come
* back the next time, the entire process is retried.
*/
export const resetFailuresForFile = (file: EnteFile) => {
if (_state.itemDataByFileID.get(file.id)?.failureReason) {
_state.itemDataByFileID.delete(file.id);
}
};
const enqueueUpdates = async (file: EnteFile) => {
const update = (itemData: ItemData) => {
_state.itemDataByFileID.set(file.id, itemData);
_state.needsRefreshByFileID.get(file.id)?.();
};
let thumbnailData: ItemData;
try {
const thumbnailURL = await downloadManager.renderableThumbnailURL(file);
// TODO(PS):
const thumbnailData = await withDimensions(thumbnailURL!);
thumbnailData = await withDimensions(thumbnailURL!);
update({
...thumbnailData,
isContentLoading: true,
isContentZoomable: false,
});
} catch (e) {
// If we can't even get the thumbnail, then a persistent (for now)
// network error is likely (download manager already has retries).
// If we can't even get the thumbnail, then a network error is likely
// (download manager already has retries).
//
// Notify the user of the error. The entire process will be retried when
// they reopen the slide later.
@ -189,33 +202,42 @@ const enqueueUpdates = async (file: EnteFile) => {
return;
}
switch (file.metadata.fileType) {
case FileType.image: {
const sourceURLs = await downloadManager.renderableSourceURLs(file);
// TODO(PS):
const itemData = await withDimensions(sourceURLs.url as string);
update(itemData);
break;
}
try {
switch (file.metadata.fileType) {
case FileType.image: {
const sourceURLs =
await downloadManager.renderableSourceURLs(file);
// TODO(PS):
const itemData = await withDimensions(sourceURLs.url as string);
update(itemData);
break;
}
case FileType.video: {
const sourceURLs = await downloadManager.renderableSourceURLs(file);
// TODO(PS):
update({ videoURL: sourceURLs.url as string });
break;
}
case FileType.video: {
const sourceURLs =
await downloadManager.renderableSourceURLs(file);
// TODO(PS):
update({ videoURL: sourceURLs.url as string });
break;
}
case FileType.livePhoto: {
const sourceURLs = await downloadManager.renderableSourceURLs(file);
const livePhotoSourceURLs = sourceURLs.url as LivePhotoSourceURL;
const imageURL = await livePhotoSourceURLs.image();
// TODO(PS):
const imageData = await withDimensions(imageURL!);
update(imageData);
const livePhotoVideoURL = await livePhotoSourceURLs.video();
update({ ...imageData, livePhotoVideoURL });
break;
case FileType.livePhoto: {
const sourceURLs =
await downloadManager.renderableSourceURLs(file);
const livePhotoSourceURLs =
sourceURLs.url as LivePhotoSourceURL;
const imageURL = await livePhotoSourceURLs.image();
// TODO(PS):
const imageData = await withDimensions(imageURL!);
update(imageData);
const livePhotoVideoURL = await livePhotoSourceURLs.video();
update({ ...imageData, livePhotoVideoURL });
break;
}
}
} catch (e) {
log.error("Failed to show file", e);
update({ ...thumbnailData, failureReason: "other" });
}
};

View File

@ -20,7 +20,7 @@ const paths = {
// TODO(PS): This transform is temporary, audit later.
info: '<path d="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8" transform="translate(3.5, 3.5)"',
// "@mui/icons-material/ErrorOutline"
error: '<path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2M12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8" transform="translate(10, 5.7) scale(0.85)"',
error: '<path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2M12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8" transform="translate(7, 5.7) scale(0.85)"',
};
/**
@ -41,6 +41,6 @@ const paths = {
*/
export const createPSRegisterElementIconHTML = (name: "info") => ({
isCustomSVG: true,
inner: `${paths[name]} id="pswp__icn-${name}"`,
inner: `${paths[name]} id="pswp__icn-${name}" />`,
outlineID: `pswp__icn-${name}`,
});

View File

@ -4,7 +4,7 @@
import log from "@/base/log";
import type { EnteFile } from "@/media/file";
import { t } from "i18next";
import { itemDataForFile } from "./data-source";
import { itemDataForFile, resetFailuresForFile } from "./data-source";
import type { FileViewerProps } from "./FileViewer";
import { createPSRegisterElementIconHTML } from "./icons";
@ -145,6 +145,11 @@ export class FileViewerPhotoSwipe {
mainClass: "pswp-ente",
});
// Helper routines to obtain the file at `currIndex`.
const currentFile = () => this.files[pswp.currIndex]!;
const withCurrentFile = (cb: (file: EnteFile) => void) => () =>
cb(currentFile());
// Provide data about slides to PhotoSwipe via callbacks
// https://photoswipe.com/data-sources/#dynamically-generated-data
@ -234,15 +239,24 @@ export class FileViewerPhotoSwipe {
});
pswp.on("contentDeactivate", (e) => {
// Pause the video element (if any) on a slide when we move away
// from it.
// Reset failures, if any, for this file so that the fetch is tried
// again when we come back to it^.
//
// ^ Note that because of how the preloading works, this will have
// an effect (i.e. the retry will happen) only if the user moves
// more than 2 slides and then back, or if they reopen the viewer.
resetFailuresForFile(currentFile());
// Pause the video element, if any, when we move away from the
// slide.
const video =
e.content?.slide?.container?.getElementsByTagName("video")[0];
video?.pause();
});
pswp.on("contentActivate", (e) => {
// Undo the effect of a previous "contentDeactivate".
// Undo the effect of a previous "contentDeactivate" if it was
// displaying a live photo.
if (e.content?.slide.data?.livePhotoVideoURL) {
e.content?.slide?.container
?.getElementsByTagName("video")[0]
@ -258,9 +272,6 @@ export class FileViewerPhotoSwipe {
onClose();
});
const withCurrentFile = (cb: (file: EnteFile) => void) => () =>
cb(this.files[this.pswp.currIndex]!);
// Add our custom UI elements to inside the PhotoSwipe dialog.
//
// API docs for registerElement:
@ -277,35 +288,12 @@ export class FileViewerPhotoSwipe {
order: 6,
html: createPSRegisterElementIconHTML("error"),
onInit: (errorElement, pswp) => {
let isVisible = false;
const toggleIndicatorClass = (className, add) => {
pswp.on("change", () => {
errorElement.classList.toggle(
"pswp__error--" + className,
add,
"pswp__error--active",
!!pswp.currSlide.content.data.failureReason,
);
};
const setIndicatorVisibility = (visible) => {
if (isVisible !== visible) {
isVisible = visible;
toggleIndicatorClass("active", visible);
}
};
const updateErrorIndicatorVisibility = () => {
console.log(
"updateErrorIndicatorVisibility",
pswp.currSlide.content,
);
if (!pswp.currSlide.content.data.failureReason) {
setIndicatorVisibility(true);
} else {
setIndicatorVisibility(false);
}
};
pswp.on("change", updateErrorIndicatorVisibility);
});
},
});
pswp.ui.registerElement({
@ -316,23 +304,6 @@ export class FileViewerPhotoSwipe {
html: createPSRegisterElementIconHTML("info"),
onClick: withCurrentFile(onViewInfo),
});
// const counterIndicator2 = {
// name: "counter-2",
// order: 5,
// html: '<div><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true" class="xxxpswp__icn"><path d="M20.5 14.3 17.1 18V10h-2.2v7.9l-3.4-3.6L10 16l6 6.1 6-6.1ZM23 23H9v2h14Z" /></svg></div>',
// onInit: (counterElement, pswp) => {
// pswp.on("change", () => {
// counterElement.style.fill = "red";
// // counterElement.innerText =
// // pswp.currIndex +
// // 1 +
// // pswp.options.indexIndicatorSep +
// // pswp.getNumItems();
// });
// },
// };
// pswp.ui.registerElement(counterIndicator2);
});
// Modify the default UI elements.