wip delegate

This commit is contained in:
Manav Rathi
2025-02-28 09:20:19 +05:30
parent 067e44e10a
commit 15a4e3cd98
2 changed files with 85 additions and 45 deletions

View File

@@ -183,7 +183,8 @@ const FileViewer: React.FC<FileViewerProps> = ({
// onMarkTempDeleted,
onSaveEditedImageCopy,
}) => {
const pswpRef = useRef<FileViewerPhotoSwipe | undefined>();
const psRef = useRef<FileViewerPhotoSwipe | undefined>();
const psDelegateRef = useRef<FileViewerPhotoSwipeDelegate | undefined>();
// Whenever we get a callback from our custom PhotoSwipe instance, we also
// get the active file on which that action was performed as an argument.
@@ -321,34 +322,56 @@ const FileViewer: React.FC<FileViewerProps> = ({
[onSaveEditedImageCopy, handleImageEditorClose, handleClose],
);
useEffect(() => {
log.debug(() => ["viewer", { action: "useEffect", open }]);
useEffect(() => (
psDelegateRef.current.onClose = handleClose;
onAnnotate: handleAnnotate,
onToggleFavorite: handleToggleFavorite,
onViewInfo: handleViewInfo,
onEditImage: handleEditImage,
}), [
handleClose,
onAnnotate: handleAnnotate,
onToggleFavorite: handleToggleFavorite,
onViewInfo: handleViewInfo,
onEditImage: handleEditImage,
]);
useEffect(() => {
if (open && !psRef.current) {
// We're open, but we don't have a PhotoSwipe instance. Create
// one. This will show the file viewer dialog.
//
// Before creating it, also create a delegate. The delegate has
// a stable identity so that PhotoSwipe callbacks can be routed
// via it. When any of the
// callbacks change, we update the props of the delegate instead
// of changing the delegate itself.
log.debug(() => ["viewer", "open"]);
const delegate = {
onClose: handleClose,
onAnnotate: handleAnnotate,
onToggleFavorite: handleToggleFavorite,
onViewInfo: handleViewInfo,
onEditImage: handleEditImage,
}
const pswp = new FileViewerPhotoSwipe({
files,
initialIndex,
disableDownload,
delegate,
});
psRef.current = pswp;
psDelegateRef.current = delegate;
} else if (!open && psRef.current) {
// We're closed, but we still have a PhotoSwipe instance. Cleanup.
log.debug(() => ["viewer", "close"]);
psRef.current?.closeIfNeeded();
psRef.current = undefined;
psDelegateRef.current = undefined;
if (!open) {
// The close state will be handled by the cleanup function.
return;
}
const pswp = new FileViewerPhotoSwipe({
files,
initialIndex,
disableDownload,
onClose: handleClose,
onAnnotate: handleAnnotate,
onToggleFavorite: handleToggleFavorite,
onViewInfo: handleViewInfo,
onEditImage: handleEditImage,
});
pswpRef.current = pswp;
return () => {
log.debug(() => [
"viewer",
{ action: "useEffect/cleanup", pswpRef: pswpRef.current },
]);
pswpRef.current?.closeIfNeeded();
pswpRef.current = undefined;
};
// The hook is missing dependencies; this is intentional - we don't want
// to recreate the PhotoSwipe dialog when these dependencies change.
//

View File

@@ -1,6 +1,7 @@
/* eslint-disable */
// @ts-nocheck
import { assertionFailed } from "@/base/assert";
import { pt } from "@/base/i18n";
import log from "@/base/log";
import type { EnteFile } from "@/media/file";
@@ -66,7 +67,7 @@ export interface FileViewerFileAnnotation {
isEditableImage?: boolean | undefined;
}
type FileViewerPhotoSwipeOptions = {
interface FileViewerPhotoSwipeDelegate {
/**
* Called when the file viewer is closed.
*/
@@ -99,7 +100,20 @@ type FileViewerPhotoSwipeOptions = {
* {@link FileViewerFileAnnotation} for the file.
*/
onEditImage?: (annotatedFile: FileViewerAnnotatedFile) => void;
} & Pick<FileViewerProps, "files" | "initialIndex" | "disableDownload">;
}
type FileViewerPhotoSwipeOptions = Pick<
FileViewerProps,
"files" | "initialIndex" | "disableDownload"
> & {
/**
* Callbacks.
*
* The extra level of indirection allows these to be updated without
* recreating us.
*/
delegate: FileViewerPhotoSwipeDelegate;
};
/**
* A file and its annotation, in a nice cosy box.
@@ -140,6 +154,10 @@ export class FileViewerPhotoSwipe {
* The options with which we were initialized.
*/
private opts: Pick<FileViewerPhotoSwipeOptions, "disableDownload">;
/**
* An object to which we should route various callbacks.
*/
private delegate: FileViewerPhotoSwipeDelegate;
/**
* An interval that invokes a periodic check of whether we should the hide
* controls if the user does not perform any pointer events for a while.
@@ -174,11 +192,7 @@ export class FileViewerPhotoSwipe {
files,
initialIndex,
disableDownload,
onClose,
onAnnotate,
onToggleFavorite,
onViewInfo,
onEditImage,
delegate,
}: FileViewerPhotoSwipeOptions) {
this.files = files;
this.opts = { disableDownload };
@@ -249,7 +263,7 @@ export class FileViewerPhotoSwipe {
const file = currentFile();
let annotation = this.activeFileAnnotation;
if (annotation?.fileID != file.id) {
annotation = onAnnotate(file);
annotation = delegate.onAnnotate(file);
this.activeFileAnnotation = annotation;
}
return {
@@ -266,8 +280,8 @@ export class FileViewerPhotoSwipe {
const currentFileAnnotation = () => currentAnnotatedFile().annotation;
const withCurrentAnnotatedFile =
(cb: (af: AnnotatedFile) => void) => () =>
cb(currentAnnotatedFile());
(cb: ((af: AnnotatedFile) => void) | undefined) => () =>
cb ? cb(currentAnnotatedFile()) : assertionFailed();
// Provide data about slides to PhotoSwipe via callbacks
// https://photoswipe.com/data-sources/#dynamically-generated-data
@@ -404,7 +418,7 @@ export class FileViewerPhotoSwipe {
forgetFailedItems();
forgetExif();
// Let our parent know that we have been closed.
onClose();
delegate.onClose();
});
const showIf = (element: HTMLElement, condition: boolean) =>
@@ -451,7 +465,7 @@ export class FileViewerPhotoSwipe {
},
});
if (onToggleFavorite) {
if (delegate.onToggleFavorite) {
// Only one of these two will end up being shown, so they can
// safely share the same order.
pswp.ui.registerElement({
@@ -460,7 +474,9 @@ export class FileViewerPhotoSwipe {
order: 8,
isButton: true,
html: createPSRegisterElementIconHTML("favorite"),
onClick: withCurrentAnnotatedFile(onToggleFavorite),
onClick: withCurrentAnnotatedFile(
delegate.onToggleFavorite,
),
onInit: (buttonElement) =>
pswp.on("change", () =>
showIf(
@@ -475,7 +491,9 @@ export class FileViewerPhotoSwipe {
order: 8,
isButton: true,
html: createPSRegisterElementIconHTML("unfavorite"),
onClick: withCurrentAnnotatedFile(onToggleFavorite),
onClick: withCurrentAnnotatedFile(
delegate.onToggleFavorite,
),
onInit: (buttonElement) =>
pswp.on("change", () =>
showIf(
@@ -492,11 +510,11 @@ export class FileViewerPhotoSwipe {
order: 9,
isButton: true,
html: createPSRegisterElementIconHTML("info"),
onClick: withCurrentAnnotatedFile(onViewInfo),
onClick: withCurrentAnnotatedFile(delegate.onViewInfo),
});
// TODO(PS):
if (onEditImage && false) {
if (delegate.onEditImage && false) {
pswp.ui.registerElement({
name: "edit",
// TODO(PS):
@@ -505,7 +523,7 @@ export class FileViewerPhotoSwipe {
order: 16,
isButton: true,
html: createPSRegisterElementIconHTML("edit"),
onClick: withCurrentAnnotatedFile(onEditImage),
onClick: withCurrentAnnotatedFile(delegate.onEditImage),
onInit: (buttonElement) =>
pswp.on("change", () =>
showIf(
@@ -523,9 +541,8 @@ export class FileViewerPhotoSwipe {
order: 17,
isButton: true,
html: createPSRegisterElementIconHTML("more"),
onClick: withCurrentAnnotatedFile(onViewInfo),
onClick: withCurrentAnnotatedFile(delegate.onViewInfo),
});
});
// Modify the default UI elements.