Swap
This commit is contained in:
@@ -3,24 +3,17 @@ import { isSameDay } from "@/base/date";
|
||||
import { formattedDate } from "@/base/i18n-date";
|
||||
import log from "@/base/log";
|
||||
import type { FileInfoProps } from "@/gallery/components/FileInfo";
|
||||
import {
|
||||
downloadManager,
|
||||
type LivePhotoSourceURL,
|
||||
type LoadedLivePhotoSourceURL,
|
||||
type RenderableSourceURLs,
|
||||
} from "@/gallery/services/download";
|
||||
import { FileViewer } from "@/gallery/components/viewer/FileViewer";
|
||||
import { type RenderableSourceURLs } from "@/gallery/services/download";
|
||||
import type { Collection } from "@/media/collection";
|
||||
import { EnteFile } from "@/media/file";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import { FileViewer } from "@/new/photos/components/FileViewerComponents";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer";
|
||||
import { moveToTrash, TRASH_SECTION } from "@/new/photos/services/collection";
|
||||
import { styled } from "@mui/material";
|
||||
import { PhotoViewer } from "components/PhotoViewer";
|
||||
import { t } from "i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { GalleryContext } from "pages/gallery";
|
||||
import PhotoSwipe from "photoswipe";
|
||||
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
||||
import AutoSizer from "react-virtualized-auto-sizer";
|
||||
import {
|
||||
@@ -182,10 +175,7 @@ const PhotoFrame = ({
|
||||
}: PhotoFrameProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [currentIndex, setCurrentIndex] = useState<number>(0);
|
||||
const [fetching, setFetching] = useState<Record<number, boolean>>({});
|
||||
const [thumbFetching, setThumbFetching] = useState<Record<number, boolean>>(
|
||||
{},
|
||||
);
|
||||
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const [rangeStart, setRangeStart] = useState(null);
|
||||
const [currentHover, setCurrentHover] = useState(null);
|
||||
@@ -200,6 +190,7 @@ const PhotoFrame = ({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// TODO(PS): Audit
|
||||
const result = files.map((file) => ({
|
||||
...file,
|
||||
w: window.innerWidth,
|
||||
@@ -208,8 +199,6 @@ const PhotoFrame = ({
|
||||
timelineDateString: fileTimelineDateString(file),
|
||||
}));
|
||||
setDisplayFiles(result);
|
||||
setFetching({});
|
||||
setThumbFetching({});
|
||||
}, [files]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -334,28 +323,14 @@ const PhotoFrame = ({
|
||||
if (file.msrc && !forceUpdate) {
|
||||
return false;
|
||||
}
|
||||
// TODO(PS): Audit
|
||||
updateDisplayFileThumbnail(file, url);
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleClose = (needUpdate) => {
|
||||
if (process.env.NEXT_PUBLIC_ENTE_WIP_PS5) {
|
||||
throw new Error("Not implemented");
|
||||
} else {
|
||||
setOpen(false);
|
||||
needUpdate && onSyncWithRemote();
|
||||
setIsPhotoSwipeOpen?.(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onThumbnailClick = (index: number) => () => {
|
||||
setCurrentIndex(index);
|
||||
if (process.env.NEXT_PUBLIC_ENTE_WIP_PS5) {
|
||||
showFileViewer();
|
||||
} else {
|
||||
setOpen(true);
|
||||
setIsPhotoSwipeOpen?.(true);
|
||||
}
|
||||
showFileViewer();
|
||||
};
|
||||
|
||||
const handleSelect = handleSelectCreator(
|
||||
@@ -429,135 +404,7 @@ const PhotoFrame = ({
|
||||
/>
|
||||
);
|
||||
|
||||
const getSlideData = async (
|
||||
instance: PhotoSwipe<PhotoSwipe.Options>,
|
||||
index: number,
|
||||
item: DisplayFile,
|
||||
) => {
|
||||
log.info(
|
||||
`[${item.id}] getSlideData called for thumbnail: ${!!item.msrc} sourceLoaded: ${!!item.isSourceLoaded} fetching: ${!!fetching[item.id]}`,
|
||||
);
|
||||
|
||||
if (!item.msrc) {
|
||||
try {
|
||||
if (thumbFetching[item.id]) {
|
||||
log.info(`[${item.id}] thumb download already in progress`);
|
||||
return;
|
||||
}
|
||||
log.info(`[${item.id}] doesn't have thumbnail`);
|
||||
thumbFetching[item.id] = true;
|
||||
// URL will always be defined (unless an error is thrown) since
|
||||
// we are not passing the `cachedOnly` option.
|
||||
const url = await downloadManager.renderableThumbnailURL(item)!;
|
||||
updateThumbnail(instance, index, item, url, false);
|
||||
} catch (e) {
|
||||
log.error("getSlideData failed get msrc url failed", e);
|
||||
thumbFetching[item.id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (item.isSourceLoaded || item.conversionFailed) {
|
||||
if (item.isSourceLoaded) {
|
||||
log.info(`[${item.id}] source already loaded`);
|
||||
}
|
||||
if (item.conversionFailed) {
|
||||
log.info(`[${item.id}] conversion failed`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (fetching[item.id]) {
|
||||
log.info(`[${item.id}] file download already in progress`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info(`[${item.id}] new file src request`);
|
||||
fetching[item.id] = true;
|
||||
const srcURLs = await downloadManager.renderableSourceURLs(item);
|
||||
if (item.metadata.fileType === FileType.livePhoto) {
|
||||
const srcImgURL = srcURLs.url as LivePhotoSourceURL;
|
||||
const imageURL = await srcImgURL.image();
|
||||
|
||||
const dummyImgSrcUrl: RenderableSourceURLs = {
|
||||
url: imageURL,
|
||||
type: "normal",
|
||||
};
|
||||
updateSource(instance, index, item, dummyImgSrcUrl, false);
|
||||
if (!imageURL) {
|
||||
// no image url, no need to load video
|
||||
return;
|
||||
}
|
||||
|
||||
const videoURL = await srcImgURL.video();
|
||||
const loadedLivePhotoSrcURL: RenderableSourceURLs = {
|
||||
url: { video: videoURL, image: imageURL },
|
||||
type: "livePhoto",
|
||||
};
|
||||
updateSource(
|
||||
instance,
|
||||
index,
|
||||
item,
|
||||
loadedLivePhotoSrcURL,
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
updateSource(instance, index, item, srcURLs, false);
|
||||
}
|
||||
} catch (e) {
|
||||
log.error("getSlideData failed get src url failed", e);
|
||||
fetching[item.id] = false;
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
|
||||
const updateThumbnail = (
|
||||
instance: PhotoSwipe<PhotoSwipe.Options>,
|
||||
index: number,
|
||||
item: DisplayFile,
|
||||
url: string,
|
||||
forceUpdate?: boolean,
|
||||
) => {
|
||||
try {
|
||||
if (updateThumbURL(index)(item.id, url, forceUpdate)) {
|
||||
log.info(
|
||||
`[${item.id}] calling invalidateCurrItems for thumbnail msrc: ${!!item.msrc}`,
|
||||
);
|
||||
instance.invalidateCurrItems();
|
||||
if ((instance as any).isOpen()) {
|
||||
instance.updateSize(true);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log.error("updating photoswipe after msrc url update failed", e);
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
const updateSource = (
|
||||
instance: PhotoSwipe<PhotoSwipe.Options>,
|
||||
index: number,
|
||||
item: DisplayFile,
|
||||
srcURL: RenderableSourceURLs,
|
||||
overwrite: boolean,
|
||||
) => {
|
||||
const file = displayFiles[index];
|
||||
// This is to prevent outdated call from updating the wrong file.
|
||||
if (file.id !== item.id) {
|
||||
log.info(
|
||||
`Ignoring stale updateSourceURL for display file at index ${index} (file ID ${file.id}, expected ${item.id})`,
|
||||
);
|
||||
throw new Error("Update URL file id mismatch");
|
||||
}
|
||||
if (file.isSourceLoaded && !overwrite) return;
|
||||
if (file.conversionFailed) throw new Error("File conversion failed");
|
||||
|
||||
updateDisplayFileSource(file, srcURL, enableDownload);
|
||||
instance.invalidateCurrItems();
|
||||
if ((instance as any).isOpen()) {
|
||||
instance.updateSize(true);
|
||||
}
|
||||
};
|
||||
|
||||
/* TODO(PS):
|
||||
const forceConvertItem = async (
|
||||
instance: PhotoSwipe<PhotoSwipe.Options>,
|
||||
index: number,
|
||||
@@ -582,35 +429,10 @@ const PhotoFrame = ({
|
||||
// no-op
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{process.env.NEXT_PUBLIC_ENTE_WIP_PS5 && (
|
||||
<FileViewer
|
||||
{...fileViewerVisibilityProps}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
/* @ts-ignore TODO(PS): test */
|
||||
user={galleryContext.user ?? undefined}
|
||||
files={files}
|
||||
initialIndex={currentIndex}
|
||||
disableDownload={!enableDownload}
|
||||
isInIncomingSharedCollection={isInIncomingSharedCollection}
|
||||
isInTrashSection={activeCollectionID === TRASH_SECTION}
|
||||
isInHiddenSection={isInHiddenSection}
|
||||
onTriggerSyncWithRemote={handleTriggerSyncWithRemote}
|
||||
onToggleFavorite={handleToggleFavorite}
|
||||
onDownload={handleDownload}
|
||||
onDelete={handleDelete}
|
||||
onSaveEditedImageCopy={handleSaveEditedImageCopy}
|
||||
{...{
|
||||
favoriteFileIDs,
|
||||
fileCollectionIDs,
|
||||
allCollectionsNameByID,
|
||||
onSelectCollection,
|
||||
onSelectPerson,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<PhotoList
|
||||
@@ -626,21 +448,24 @@ const PhotoFrame = ({
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
<PhotoViewer
|
||||
isOpen={open}
|
||||
items={displayFiles}
|
||||
currentIndex={currentIndex}
|
||||
onClose={handleClose}
|
||||
gettingData={getSlideData}
|
||||
forceConvertItem={forceConvertItem}
|
||||
isTrashCollection={activeCollectionID === TRASH_SECTION}
|
||||
<FileViewer
|
||||
{...fileViewerVisibilityProps}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
/* @ts-ignore TODO(PS): test */
|
||||
user={galleryContext.user ?? undefined}
|
||||
files={files}
|
||||
initialIndex={currentIndex}
|
||||
disableDownload={!enableDownload}
|
||||
isInIncomingSharedCollection={isInIncomingSharedCollection}
|
||||
isInTrashSection={activeCollectionID === TRASH_SECTION}
|
||||
isInHiddenSection={isInHiddenSection}
|
||||
enableDownload={enableDownload}
|
||||
onTriggerSyncWithRemote={handleTriggerSyncWithRemote}
|
||||
onToggleFavorite={handleToggleFavorite}
|
||||
onDownload={handleDownload}
|
||||
onDelete={handleDelete}
|
||||
onSaveEditedImageCopy={handleSaveEditedImageCopy}
|
||||
{...{
|
||||
favoriteFileIDs,
|
||||
onMarkUnsyncedFavoriteUpdate,
|
||||
onMarkTempDeleted,
|
||||
setFilesDownloadProgressAttributesCreator,
|
||||
fileCollectionIDs,
|
||||
allCollectionsNameByID,
|
||||
onSelectCollection,
|
||||
@@ -671,74 +496,6 @@ const updateDisplayFileThumbnail = (file: DisplayFile, url: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const updateDisplayFileSource = (
|
||||
file: DisplayFile,
|
||||
srcURLs: RenderableSourceURLs,
|
||||
enableDownload: boolean,
|
||||
) => {
|
||||
const { url } = srcURLs;
|
||||
const isRenderable = !!url;
|
||||
file.w = window.innerWidth;
|
||||
file.h = window.innerHeight;
|
||||
file.isSourceLoaded =
|
||||
file.metadata.fileType === FileType.livePhoto
|
||||
? srcURLs.type === "livePhoto"
|
||||
: true;
|
||||
file.canForceConvert = srcURLs.canForceConvert;
|
||||
file.conversionFailed = !isRenderable;
|
||||
file.associatedImageURL = (() => {
|
||||
switch (file.metadata.fileType) {
|
||||
case FileType.image:
|
||||
return srcURLs.url as string;
|
||||
case FileType.livePhoto:
|
||||
return (srcURLs.url as LoadedLivePhotoSourceURL).image;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
if (!isRenderable) {
|
||||
file.isSourceLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.metadata.fileType === FileType.video) {
|
||||
file.html = `
|
||||
<video controls ${
|
||||
!enableDownload && 'controlsList="nodownload"'
|
||||
} onContextMenu="return false;">
|
||||
<source src="${url}" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
`;
|
||||
} else if (file.metadata.fileType === FileType.livePhoto) {
|
||||
if (srcURLs.type === "normal") {
|
||||
file.html = `
|
||||
<div class = 'pswp-item-container'>
|
||||
<img id = "live-photo-image-${file.id}" src="${url}" onContextMenu="return false;"/>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
const { image: imageURL, video: videoURL } =
|
||||
url as LoadedLivePhotoSourceURL;
|
||||
|
||||
file.html = `
|
||||
<div class = 'pswp-item-container'>
|
||||
<img id = "live-photo-image-${file.id}" src="${imageURL}" onContextMenu="return false;"/>
|
||||
<video id = "live-photo-video-${file.id}" loop muted onContextMenu="return false;">
|
||||
<source src="${videoURL}" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
} else if (file.metadata.fileType === FileType.image) {
|
||||
file.src = url as string;
|
||||
} else {
|
||||
log.error(`unknown file type - ${file.metadata.fileType}`);
|
||||
file.src = url as string;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* See: [Note: Timeline date string]
|
||||
*/
|
||||
|
||||
@@ -12,13 +12,13 @@ import { useModalVisibility } from "@/base/components/utils/modal";
|
||||
import { useBaseContext } from "@/base/context";
|
||||
import log from "@/base/log";
|
||||
import { FullScreenDropZone } from "@/gallery/components/FullScreenDropZone";
|
||||
import { resetFileViewerDataSourceOnClose } from "@/gallery/components/viewer/data-source";
|
||||
import { type Collection } from "@/media/collection";
|
||||
import { mergeMetadata, type EnteFile } from "@/media/file";
|
||||
import {
|
||||
CollectionSelector,
|
||||
type CollectionSelectorAttributes,
|
||||
} from "@/new/photos/components/CollectionSelector";
|
||||
import { resetFileViewerDataSourceOnClose } from "@/new/photos/components/FileViewerComponents-temp";
|
||||
import { PlanSelector } from "@/new/photos/components/PlanSelector";
|
||||
import {
|
||||
SearchBar,
|
||||
@@ -570,7 +570,6 @@ const Page: React.FC = () => {
|
||||
);
|
||||
if (didUpdateNormalFiles || didUpdateHiddenFiles) {
|
||||
exportService.onLocalFilesUpdated();
|
||||
// TODO(PS): Use direct one
|
||||
resetFileViewerDataSourceOnClose();
|
||||
}
|
||||
// syncWithRemote is called with the force flag set to true before
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
// @ts-nocheck
|
||||
|
||||
// TODO(PS): WIP gallery using upstream photoswipe
|
||||
//
|
||||
// Needs (not committed yet):
|
||||
// yarn workspace gallery add photoswipe@^5.4.4
|
||||
// mv node_modules/photoswipe packages/new/photos/components/ps5
|
||||
|
||||
if (process.env.NEXT_PUBLIC_ENTE_WIP_PS5) {
|
||||
console.warn("Using WIP upstream photoswipe");
|
||||
} else {
|
||||
throw new Error("Whoa");
|
||||
}
|
||||
|
||||
import { isDesktop } from "@/base/app";
|
||||
import { SpacedRow } from "@/base/components/containers";
|
||||
import { DialogCloseIconButton } from "@/base/components/mui/DialogCloseIconButton";
|
||||
@@ -248,7 +236,7 @@ export type FileViewerProps = ModalVisibilityProps & {
|
||||
/**
|
||||
* A PhotoSwipe based image and video viewer.
|
||||
*/
|
||||
const FileViewer: React.FC<FileViewerProps> = ({
|
||||
export const FileViewer: React.FC<FileViewerProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
user,
|
||||
@@ -870,8 +858,6 @@ const FileViewer: React.FC<FileViewerProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default FileViewer;
|
||||
|
||||
const Container = styled("div")`
|
||||
border: 1px solid red;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import log from "@/base/log";
|
||||
import type { EnteFile } from "@/media/file";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import { t } from "i18next";
|
||||
import PhotoSwipe from "photoswipe";
|
||||
import {
|
||||
fileViewerDidClose,
|
||||
fileViewerWillOpen,
|
||||
@@ -18,24 +19,6 @@ import {
|
||||
import { type FileViewerAnnotatedFile } from "./FileViewer";
|
||||
import { createPSRegisterElementIconHTML } from "./icons";
|
||||
|
||||
// TODO(PS): WIP gallery using upstream photoswipe
|
||||
//
|
||||
// Needs (not committed yet):
|
||||
// yarn workspace gallery add photoswipe@^5.4.4
|
||||
// mv node_modules/photoswipe packages/new/photos/components/ps5
|
||||
|
||||
if (process.env.NEXT_PUBLIC_ENTE_WIP_PS5) {
|
||||
console.warn("Using WIP upstream photoswipe");
|
||||
} else {
|
||||
throw new Error("Whoa");
|
||||
}
|
||||
|
||||
let PhotoSwipe;
|
||||
if (process.env.NEXT_PUBLIC_ENTE_WIP_PS5) {
|
||||
// TODO(PS): Comment me before merging into main.
|
||||
// PhotoSwipe = require("./ps5/dist/photoswipe.esm.js").default;
|
||||
}
|
||||
|
||||
export interface FileViewerPhotoSwipeDelegate {
|
||||
/**
|
||||
* Called to obtain the latest list of files.
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// TODO(PS): Temporary trampoline
|
||||
export const resetFileViewerDataSourceOnClose = async () => {
|
||||
if (!process.env.NEXT_PUBLIC_ENTE_WIP_PS5) return;
|
||||
(
|
||||
await import("@/gallery/components/viewer/data-source")
|
||||
).resetFileViewerDataSourceOnClose();
|
||||
};
|
||||
@@ -9,22 +9,6 @@ import { t } from "i18next";
|
||||
import { useState } from "react";
|
||||
import { aboveFileViewerContentZ } from "./utils/z-index";
|
||||
|
||||
// TODO(PS)
|
||||
import dynamic from "next/dynamic";
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const FV5 = dynamic(() => import("@/gallery/components/viewer/FileViewer"), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
const FVD = () => <></>;
|
||||
|
||||
export const FileViewer: React.FC = (props) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return process.env.NEXT_PUBLIC_ENTE_WIP_PS5 ? <FV5 {...props} /> : <FVD />;
|
||||
};
|
||||
|
||||
type ConfirmDeleteFileDialogProps = ModalVisibilityProps & {
|
||||
/**
|
||||
* Called when the user confirms the deletion.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
import { resetFileViewerDataSourceOnClose } from "@/gallery/components/viewer/data-source";
|
||||
import { isHiddenCollection } from "@/new/photos/services/collection";
|
||||
import {
|
||||
getAllLatestCollections,
|
||||
@@ -9,7 +10,6 @@ import { isMLSupported, mlStatusSync, mlSync } from "@/new/photos/services/ml";
|
||||
import { searchDataSync } from "@/new/photos/services/search";
|
||||
import { syncSettings } from "@/new/photos/services/settings";
|
||||
import { splitByPredicate } from "@/utils/array";
|
||||
import { resetFileViewerDataSourceOnClose } from "../components/FileViewerComponents-temp";
|
||||
|
||||
/**
|
||||
* Part 1 of {@link sync}. See TODO below for why this is split.
|
||||
@@ -77,9 +77,9 @@ export const syncFilesAndCollections = async () => {
|
||||
);
|
||||
await syncTrash(allCollections, () => {});
|
||||
if (didUpdateNormalFiles || didUpdateHiddenFiles) {
|
||||
// TODO:
|
||||
// TODO: Ok for now since we're only called by deduper, but still needs
|
||||
// fixing instead of a hidden gotcha.
|
||||
// exportService.onLocalFilesUpdated();
|
||||
// TODO(PS): Use direct one
|
||||
await resetFileViewerDataSourceOnClose();
|
||||
resetFileViewerDataSourceOnClose();
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user