This commit is contained in:
Manav Rathi
2025-03-06 10:47:51 +05:30
parent 27ad9840d0
commit 63faa29cd4
2 changed files with 30 additions and 47 deletions

View File

@@ -120,11 +120,12 @@ export interface FileViewerFileAnnotation {
}
/**
* A file and its annotation, in a nice cosy box.
* A file, its annotation, and its item data, in a nice cosy box.
*/
export interface FileViewerAnnotatedFile {
file: EnteFile;
annotation: FileViewerFileAnnotation;
itemData: ItemData;
}
export type FileViewerProps = ModalVisibilityProps & {
@@ -309,12 +310,6 @@ const FileViewer: React.FC<FileViewerProps> = ({
FileInfoExif | undefined
>(undefined);
// With semantics similar to `activeAnnotatedFile`, this is the imageURL
// associated with the `activeAnnotatedFile`, if any.
const [activeImageURL, setActiveImageURL] = useState<string | undefined>(
undefined,
);
const [openFileInfo, setOpenFileInfo] = useState(false);
const [moreMenuAnchorEl, setMoreMenuAnchorEl] =
useState<HTMLElement | null>(null);
@@ -448,20 +443,21 @@ const FileViewer: React.FC<FileViewerProps> = ({
});
};
// Not memoized since it uses the frequently changing `activeImageURL`.
// Not memoized since it uses the frequently changing `activeAnnotatedFile`.
const handleCopyImage = useCallback(() => {
handleMoreMenuCloseIfNeeded();
const imageURL = activeAnnotatedFile?.itemData.imageURL;
// Safari does not copy if we do not call `navigator.clipboard.write`
// synchronously within the click event handler, but it does supports
// passing a promise in lieu of the blob.
void window.navigator.clipboard
.write([
new ClipboardItem({
"image/png": createImagePNGBlob(activeImageURL!),
"image/png": createImagePNGBlob(imageURL!),
}),
])
.catch(onGenericError);
}, [onGenericError, handleMoreMenuCloseIfNeeded, activeImageURL]);
}, [onGenericError, handleMoreMenuCloseIfNeeded, activeAnnotatedFile]);
const handleEditImage = useMemo(() => {
return onSaveEditedImageCopy
@@ -486,7 +482,7 @@ const FileViewer: React.FC<FileViewerProps> = ({
);
const handleAnnotate = useCallback(
(file: EnteFile, itemData: ItemData): FileViewerFileAnnotation => {
(file: EnteFile, itemData: ItemData): FileViewerAnnotatedFile => {
const fileID = file.id;
const isOwnFile = file.ownerID == user?.id;
@@ -537,10 +533,9 @@ const FileViewer: React.FC<FileViewerProps> = ({
showEditImage,
};
setActiveAnnotatedFile({ file, annotation });
setActiveImageURL(itemData.imageURL);
return annotation;
const annotatedFile = { file, annotation, itemData };
setActiveAnnotatedFile(annotatedFile);
return annotatedFile;
},
[
user,
@@ -663,6 +658,7 @@ const FileViewer: React.FC<FileViewerProps> = ({
},
[
handleConfirmDelete,
activeAnnotatedFile,
activeImageURL,
handleCopyImage,
handleToggleFullscreen,

View File

@@ -15,10 +15,7 @@ import {
updateFileInfoExifIfNeeded,
type ItemData,
} from "./data-source";
import {
type FileViewerAnnotatedFile,
type FileViewerFileAnnotation,
} from "./FileViewer";
import { type FileViewerAnnotatedFile } from "./FileViewer";
import { createPSRegisterElementIconHTML } from "./icons";
// TODO(PS): WIP gallery using upstream photoswipe
@@ -132,10 +129,7 @@ type FileViewerPhotoSwipeOptions = Pick<
* @param itemData This is the best currently available {@link ItemData}
* corresponding to the current file.
*/
onAnnotate: (
file: EnteFile,
itemData: ItemData,
) => FileViewerFileAnnotation;
onAnnotate: (file: EnteFile, itemData: ItemData) => FileViewerAnnotatedFile;
/**
* Called when the user activates the info action on a file.
*/
@@ -216,17 +210,6 @@ export class FileViewerPhotoSwipe {
* - "auto-hidden" if controls were hidden by us because of inactivity.
*/
private lastActivityDate: Date | "auto-hidden" | "already-hidden";
/**
* Derived data about the currently displayed file.
*
* 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 `currentAnnotatedFile` helper function defined in the constructor
* scope.
*/
private activeFileAnnotation: FileViewerFileAnnotation | undefined;
/**
* IDs of files for which a there is a favorite update in progress.
*/
@@ -305,24 +288,28 @@ export class FileViewerPhotoSwipe {
// Various helper routines to obtain the file at `currIndex`.
/**
* Derived data about the currently displayed file.
*
* 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 `currentAnnotatedFile` helper function.
*/
let _currentAnnotatedFile: FileViewerAnnotatedFile | undefined;
const currentFile = () => delegate.getFiles()[pswp.currIndex]!;
const currentAnnotatedFile = () => {
const file = currentFile();
let annotation = this.activeFileAnnotation;
if (annotation?.fileID != file.id) {
annotation = onAnnotate(file, pswp.currSlide.content.data);
this.activeFileAnnotation = annotation;
const annotatedFile = _currentAnnotatedFile;
if (!annotatedFile || annotatedFile.file.fileID != file.id) {
annotatedFile = onAnnotate(file, pswp.currSlide.content.data);
_currentAnnotatedFile = annotatedFile;
}
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!,
};
return annotatedFile;
};
const currentFileAnnotation = () => currentAnnotatedFile().annotation;