ann2
This commit is contained in:
@@ -23,7 +23,10 @@ import type { EnteFile } from "@/media/file.js";
|
||||
import { Button, styled } from "@mui/material";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { fileInfoExifForFile, type FileInfoExif } from "./data-source";
|
||||
import { FileViewerPhotoSwipe } from "./photoswipe";
|
||||
import {
|
||||
FileViewerPhotoSwipe,
|
||||
type FileViewerAnnotatedFile,
|
||||
} from "./photoswipe";
|
||||
|
||||
export type FileViewerProps = ModalVisibilityProps & {
|
||||
/**
|
||||
@@ -76,6 +79,7 @@ export type FileViewerProps = ModalVisibilityProps & {
|
||||
const FileViewer: React.FC<FileViewerProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
user,
|
||||
files,
|
||||
initialIndex,
|
||||
disableDownload,
|
||||
@@ -93,11 +97,11 @@ const FileViewer: React.FC<FileViewerProps> = ({
|
||||
// This is not guaranteed, or even intended, to be in sync with the active
|
||||
// file shown within the file viewer. All that this guarantees is this will
|
||||
// refer to the file on which the last user initiated action was performed.
|
||||
const [activeFile, setActiveFile] = useState<EnteFile | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const [activeAnnotatedFile, setActiveAnnotatedFile] = useState<
|
||||
FileViewerAnnotatedFile | undefined
|
||||
>(undefined);
|
||||
// With semantics similar to activeFile, this is the exif data associated
|
||||
// with the activeFile, if any.
|
||||
// with the activeAnnotatedFile, if any.
|
||||
const [activeFileExif, setActiveFileExif] = useState<
|
||||
FileInfoExif | undefined
|
||||
>(undefined);
|
||||
@@ -105,11 +109,22 @@ const FileViewer: React.FC<FileViewerProps> = ({
|
||||
const { show: showFileInfo, props: fileInfoVisibilityProps } =
|
||||
useModalVisibility();
|
||||
|
||||
const handleViewInfo = useCallback(
|
||||
const handleAnnotate = useCallback(
|
||||
(file: EnteFile) => {
|
||||
setActiveFile(file);
|
||||
const fileID = file.id;
|
||||
const isOwnFile = file.ownerID == user?.id;
|
||||
return { fileID, isOwnFile };
|
||||
},
|
||||
[user],
|
||||
);
|
||||
|
||||
const handleViewInfo = useCallback(
|
||||
(annotatedFile: FileViewerAnnotatedFile) => {
|
||||
setActiveAnnotatedFile(annotatedFile);
|
||||
setActiveFileExif(
|
||||
fileInfoExifForFile(file, (exif) => setActiveFileExif(exif)),
|
||||
fileInfoExifForFile(annotatedFile.file, (exif) =>
|
||||
setActiveFileExif(exif),
|
||||
),
|
||||
);
|
||||
showFileInfo();
|
||||
},
|
||||
@@ -139,6 +154,7 @@ const FileViewer: React.FC<FileViewerProps> = ({
|
||||
initialIndex,
|
||||
disableDownload,
|
||||
onClose,
|
||||
onAnnotate: handleAnnotate,
|
||||
onViewInfo: handleViewInfo,
|
||||
});
|
||||
pswpRef.current = pswp;
|
||||
@@ -169,7 +185,7 @@ const FileViewer: React.FC<FileViewerProps> = ({
|
||||
<Button>Test</Button>
|
||||
<FileInfo
|
||||
{...fileInfoVisibilityProps}
|
||||
file={activeFile}
|
||||
file={activeAnnotatedFile.file}
|
||||
exif={activeFileExif}
|
||||
onSelectCollection={handleSelectCollection}
|
||||
onSelectPerson={handleSelectPerson}
|
||||
|
||||
@@ -47,22 +47,34 @@ type FileViewerPhotoSwipeOptions = {
|
||||
/**
|
||||
* Called when the user activates the info action on a file.
|
||||
*/
|
||||
onViewInfo: (file: EnteFile) => void;
|
||||
onViewInfo: (annotatedFile: FileViewerAnnotatedFile) => void;
|
||||
} & Pick<FileViewerProps, "files" | "initialIndex" | "disableDownload">;
|
||||
|
||||
/**
|
||||
* Derived data for a file that is needed to display the file viewer controls
|
||||
* etc associated with the file.
|
||||
*
|
||||
* This is recomputed each time the slide changes.
|
||||
* This is recomputed on-demand each time the slide changes.
|
||||
*/
|
||||
interface FileViewerFileAnnotation {
|
||||
export interface FileViewerFileAnnotation {
|
||||
/**
|
||||
* The id of the file whose annotation this is.
|
||||
*/
|
||||
fileID: number;
|
||||
/**
|
||||
* `true` if this file is owned by the logged in user (if any).
|
||||
*/
|
||||
isOwnFile: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A file and its annotation, in a nice cosy box.
|
||||
*/
|
||||
export interface FileViewerAnnotatedFile {
|
||||
file: EnteFile;
|
||||
annotation: FileViewerFileAnnotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper over {@link PhotoSwipe} to tailor its interface for use by our file
|
||||
* viewer.
|
||||
@@ -115,9 +127,14 @@ export class FileViewerPhotoSwipe {
|
||||
/**
|
||||
* Derived data about the currently displayed file.
|
||||
*
|
||||
* This is recomputed each time the slide changes.
|
||||
* This is recomputed on-demand (by using the {@link onAnnotate} callback)
|
||||
* each time the slide changes, and cached until the next slide change.
|
||||
*
|
||||
* Instead of accessing this property directly, code should funnel through
|
||||
* the `activeFileAnnotation` helper function defined in the constructor
|
||||
* scope.
|
||||
*/
|
||||
// private activeFileAnnotation;
|
||||
private activeFileAnnotation: FileViewerFileAnnotation | undefined;
|
||||
|
||||
constructor({
|
||||
files,
|
||||
@@ -129,6 +146,7 @@ export class FileViewerPhotoSwipe {
|
||||
}: FileViewerPhotoSwipeOptions) {
|
||||
this.files = files;
|
||||
this.opts = { disableDownload };
|
||||
this.lastActivityDate = new Date();
|
||||
|
||||
const pswp = new PhotoSwipe({
|
||||
// Opaque background.
|
||||
@@ -185,10 +203,35 @@ export class FileViewerPhotoSwipe {
|
||||
errorMsg: "This file could not be previewed",
|
||||
});
|
||||
|
||||
this.pswp = pswp;
|
||||
|
||||
// Helper routines to obtain the file at `currIndex`.
|
||||
|
||||
const currentFile = () => this.files[pswp.currIndex]!;
|
||||
const withCurrentFile = (cb: (file: EnteFile) => void) => () =>
|
||||
cb(currentFile());
|
||||
|
||||
const currentAnnotatedFile = () => {
|
||||
const file = currentFile();
|
||||
let annotation = this.activeFileAnnotation;
|
||||
if (annotation?.fileID != file.id) {
|
||||
annotation = onAnnotate(file);
|
||||
this.activeFileAnnotation = annotation;
|
||||
}
|
||||
return {
|
||||
file,
|
||||
// The above condition implies that annotation can never be
|
||||
// undefined, but it doesn't seem to be enough to convince
|
||||
// TypeScript. Writing the condition in a more unnatural way
|
||||
// `(annotation && annotation?.fileID == file.id)` works, but
|
||||
// instead we use a non-null assertion here.
|
||||
annotation: annotation!,
|
||||
};
|
||||
};
|
||||
|
||||
const currentFileAnnotation = () => currentAnnotatedFile().annotation;
|
||||
|
||||
const withCurrentAnnotatedFile =
|
||||
(cb: (af: AnnotatedFile) => void) => () =>
|
||||
cb(currentFileAnnotation());
|
||||
|
||||
// Provide data about slides to PhotoSwipe via callbacks
|
||||
// https://photoswipe.com/data-sources/#dynamically-generated-data
|
||||
@@ -363,7 +406,7 @@ export class FileViewerPhotoSwipe {
|
||||
order: 15,
|
||||
isButton: true,
|
||||
html: createPSRegisterElementIconHTML("info"),
|
||||
onClick: withCurrentFile(onViewInfo),
|
||||
onClick: withCurrentAnnotatedFile(onViewInfo),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -380,8 +423,6 @@ export class FileViewerPhotoSwipe {
|
||||
// the class "pswp".
|
||||
pswp.init();
|
||||
|
||||
this.pswp = pswp;
|
||||
|
||||
this.autoHideCheckIntervalId = setInterval(() => {
|
||||
this.autoHideIfInactive();
|
||||
}, 1000);
|
||||
|
||||
Reference in New Issue
Block a user