mirror of
https://github.com/ente-io/ente.git
synced 2025-08-08 07:28:26 +00:00
[web] Use upstream Photoswipe - Heavily WIP (#5025)
This commit is contained in:
commit
d58c2a3d49
@ -7,6 +7,7 @@ import {
|
||||
} from "@/gallery/services/download";
|
||||
import { EnteFile } from "@/media/file";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import { FileViewer } from "@/new/photos/components/PhotoViewer";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer";
|
||||
import { TRASH_SECTION } from "@/new/photos/services/collection";
|
||||
import { styled } from "@mui/material";
|
||||
@ -122,7 +123,7 @@ export interface PhotoFrameProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Rename me to FileListWithViewer
|
||||
* TODO: Rename me to FileListWithViewer (or Gallery?)
|
||||
*/
|
||||
const PhotoFrame = ({
|
||||
mode,
|
||||
@ -158,6 +159,10 @@ const PhotoFrame = ({
|
||||
const [isShiftKeyPressed, setIsShiftKeyPressed] = useState(false);
|
||||
const router = useRouter();
|
||||
|
||||
// const { show: showPhotoSwipe, props: photoSwipeVisibilityProps } =
|
||||
// useModalVisibility();
|
||||
const [open5, setOpen5] = useState(false);
|
||||
|
||||
const [displayFiles, setDisplayFiles] = useState<DisplayFile[] | undefined>(
|
||||
undefined,
|
||||
);
|
||||
@ -254,15 +259,24 @@ const PhotoFrame = ({
|
||||
};
|
||||
|
||||
const handleClose = (needUpdate) => {
|
||||
setOpen(false);
|
||||
needUpdate && syncWithRemote();
|
||||
setIsPhotoSwipeOpen?.(false);
|
||||
if (process.env.NEXT_PUBLIC_ENTE_WIP_PS5) {
|
||||
setOpen5(false);
|
||||
} else {
|
||||
setOpen(false);
|
||||
needUpdate && syncWithRemote();
|
||||
setIsPhotoSwipeOpen?.(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onThumbnailClick = (index: number) => () => {
|
||||
setCurrentIndex(index);
|
||||
setOpen(true);
|
||||
setIsPhotoSwipeOpen?.(true);
|
||||
if (process.env.NEXT_PUBLIC_ENTE_WIP_PS5) {
|
||||
// showPhotoSwipe();
|
||||
setOpen5(true);
|
||||
} else {
|
||||
setOpen(true);
|
||||
setIsPhotoSwipeOpen?.(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelect = handleSelectCreator(
|
||||
@ -502,6 +516,16 @@ const PhotoFrame = ({
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{process.env.NEXT_PUBLIC_ENTE_WIP_PS5 && (
|
||||
<FileViewer
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
/* @ts-ignore TODO(PS): test */
|
||||
open={open5}
|
||||
onClose={handleClose}
|
||||
files={files}
|
||||
initialIndex={currentIndex}
|
||||
/>
|
||||
)}
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<PhotoList
|
||||
|
@ -51,11 +51,14 @@ import { useNotification } from "components/utils/hooks-app";
|
||||
import { t } from "i18next";
|
||||
import type { AppProps } from "next/app";
|
||||
import { useRouter } from "next/router";
|
||||
import "photoswipe/dist/photoswipe.css";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { resumeExportsIfNeeded } from "services/export";
|
||||
import { photosLogout } from "services/logout";
|
||||
|
||||
import "photoswipe/dist/photoswipe.css";
|
||||
// TODO(PS): Note, auto hide only works with the new CSS.
|
||||
// import "../../../../packages/new/photos/components/ps5/dist/photoswipe.css";
|
||||
|
||||
import "styles/global.css";
|
||||
|
||||
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
|
||||
|
2
web/packages/new/photos/components/.gitignore
vendored
Normal file
2
web/packages/new/photos/components/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# TODO(PS): Remove me once PS5 is integrated
|
||||
ps5
|
96
web/packages/new/photos/components/FileViewer5.tsx
Normal file
96
web/packages/new/photos/components/FileViewer5.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
/* eslint-disable */
|
||||
// @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 type { EnteFile } from "@/media/file.js";
|
||||
import { Button, styled } from "@mui/material";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { FileViewerPhotoSwipe } from "./FileViewerPhotoSwipe";
|
||||
|
||||
export interface FileViewerProps {
|
||||
/**
|
||||
* The list of files that are currently being displayed in the context in
|
||||
* which the file viewer was invoked.
|
||||
*
|
||||
* Although the file viewer is called on to display a particular file
|
||||
* (specified by the {@link initialIndex} prop), the viewer is always used
|
||||
* in the context of a an album, or search results, or some other arbitrary
|
||||
* list of files. The {@link files} prop sets this underlying list of files.
|
||||
*
|
||||
* After the initial file has been shown, the user can navigate through the
|
||||
* other files from within the viewer by using the arrow buttons.
|
||||
*/
|
||||
files: EnteFile[];
|
||||
/**
|
||||
* The index of the file that should be initially shown.
|
||||
*
|
||||
* Subsequently the user may navigate between files by using the controls
|
||||
* provided within the file viewer itself.
|
||||
*/
|
||||
initialIndex: number;
|
||||
/**
|
||||
* If true then the viewer does not show controls for downloading the file.
|
||||
*/
|
||||
disableDownload?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A PhotoSwipe based image and video viewer.
|
||||
*/
|
||||
const FileViewer: React.FC<FileViewerProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
files,
|
||||
initialIndex,
|
||||
disableDownload,
|
||||
}) => {
|
||||
const pswpRef = useRef<FileViewerPhotoSwipe | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
// The close state will be handled by the cleanup function.
|
||||
return;
|
||||
}
|
||||
|
||||
const pswp = new FileViewerPhotoSwipe({
|
||||
files,
|
||||
initialIndex,
|
||||
onClose,
|
||||
disableDownload,
|
||||
});
|
||||
pswpRef.current = pswp;
|
||||
|
||||
return () => {
|
||||
pswpRef.current?.closeIfNeeded();
|
||||
pswpRef.current = undefined;
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Button>Test</Button>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
const Container = styled("div")`
|
||||
border: 1px solid red;
|
||||
|
||||
#test-gallery {
|
||||
border: 1px solid red;
|
||||
min-height: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
export default FileViewer;
|
417
web/packages/new/photos/components/FileViewerPhotoSwipe.ts
Normal file
417
web/packages/new/photos/components/FileViewerPhotoSwipe.ts
Normal file
@ -0,0 +1,417 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { assertionFailed } from "@/base/assert";
|
||||
import log from "@/base/log";
|
||||
import {
|
||||
downloadManager,
|
||||
type LivePhotoSourceURL,
|
||||
} from "@/gallery/services/download";
|
||||
import type { EnteFile } from "@/media/file";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import type { FileViewerProps } from "./FileViewer5";
|
||||
|
||||
// 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;
|
||||
}
|
||||
// TODO(PS):
|
||||
//import { type SlideData } from "./ps5/dist/types/slide/"
|
||||
type SlideData = {
|
||||
/**
|
||||
* thumbnail element
|
||||
*/
|
||||
element?: HTMLElement | undefined;
|
||||
/**
|
||||
* image URL
|
||||
*/
|
||||
src?: string | undefined;
|
||||
/**
|
||||
* image srcset
|
||||
*/
|
||||
srcset?: string | undefined;
|
||||
/**
|
||||
* image width (deprecated)
|
||||
*/
|
||||
w?: number | undefined;
|
||||
/**
|
||||
* image height (deprecated)
|
||||
*/
|
||||
h?: number | undefined;
|
||||
/**
|
||||
* image width
|
||||
*/
|
||||
width?: number | undefined;
|
||||
/**
|
||||
* image height
|
||||
*/
|
||||
height?: number | undefined;
|
||||
/**
|
||||
* placeholder image URL that's displayed before large image is loaded
|
||||
*/
|
||||
msrc?: string | undefined;
|
||||
/**
|
||||
* image alt text
|
||||
*/
|
||||
alt?: string | undefined;
|
||||
/**
|
||||
* whether thumbnail is cropped client-side or not
|
||||
*/
|
||||
thumbCropped?: boolean | undefined;
|
||||
/**
|
||||
* html content of a slide
|
||||
*/
|
||||
html?: string | undefined;
|
||||
/**
|
||||
* slide type
|
||||
*/
|
||||
type?: string | undefined;
|
||||
};
|
||||
|
||||
type FileViewerPhotoSwipeOptions = FileViewerProps & {
|
||||
/**
|
||||
* Called when the file viewer is closed.
|
||||
*/
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* A wrapper over {@link PhotoSwipe} to tailor its interface for use by our file
|
||||
* viewer.
|
||||
*
|
||||
* This is somewhat akin to the {@link PhotoSwipeLightbox}, except this doesn't
|
||||
* have any UI of its own, it only modifies PhotoSwipe Core's behaviour.
|
||||
*
|
||||
* [Note: PhotoSwipe]
|
||||
*
|
||||
* PhotoSwipe is a library that behaves similarly to the OG "lightbox" image
|
||||
* gallery JavaScript component from the middle ages.
|
||||
*
|
||||
* We don't need the lightbox functionality since we already have our own
|
||||
* thumbnail list (the "gallery"), so we only use the "Core" PhotoSwipe module
|
||||
* as our image viewer component.
|
||||
*
|
||||
* When the user clicks on one of the thumbnails in our gallery, we make the
|
||||
* root PhotoSwipe component visible. Within the DOM this is a dialog-like div
|
||||
* that takes up the entire viewport, shows the image, various controls etc.
|
||||
*
|
||||
* Documentation: https://photoswipe.com/.
|
||||
*/
|
||||
export class FileViewerPhotoSwipe {
|
||||
/**
|
||||
* The PhotoSwipe instance which we wrap.
|
||||
*/
|
||||
private pswp: PhotoSwipe;
|
||||
/**
|
||||
* The options with which we were initialized.
|
||||
*/
|
||||
private opts: Pick<FileViewerPhotoSwipeOptions, "disableDownload">;
|
||||
/**
|
||||
* The best available SlideData for rendering the file with the given ID.
|
||||
*
|
||||
* If an entry does not exist for a particular fileID, then it is lazily
|
||||
* added on demand. The same entry might get updated multiple times, as we
|
||||
* start with the thumbnail but then also update this with the original etc.
|
||||
*/
|
||||
private itemDataByFileID: Map<number, SlideData> = new Map();
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private autoHideCheckIntervalId: ReturnType<typeof setTimeout> | undefined;
|
||||
/**
|
||||
* The time the last activity occurred. Used in tandem with
|
||||
* {@link autoHideCheckIntervalId} to implement the auto hiding of controls
|
||||
* when the user stops moving the pointer for a while.
|
||||
*
|
||||
* Apart from a date, this can also be:
|
||||
*
|
||||
* - "already-hidden" if controls have already been hidden, say by a
|
||||
* bgClickAction.
|
||||
*
|
||||
* - "auto-hidden" if controls were hidden by us because of inactivity.
|
||||
*/
|
||||
private lastActivityDate: Date | "auto-hidden" | "already-hidden";
|
||||
|
||||
constructor({
|
||||
files,
|
||||
initialIndex,
|
||||
onClose,
|
||||
disableDownload,
|
||||
}: FileViewerPhotoSwipeOptions) {
|
||||
this.files = files;
|
||||
this.opts = { disableDownload };
|
||||
|
||||
const pswp = new PhotoSwipe({
|
||||
// Opaque background.
|
||||
bgOpacity: 1,
|
||||
// The default imageClickAction is "zoom-or-close". When the image
|
||||
// is small and cannot be zoomed into further (which is common when
|
||||
// just the thumbnail has been loaded), this causes PhotoSwipe to
|
||||
// close. Disable this behaviour.
|
||||
clickToCloseNonZoomable: false,
|
||||
// The default `bgClickAction` is "close", but it is not always
|
||||
// apparent where the background is and where the controls are,
|
||||
// since everything is black, and so accidentally closing PhotoSwipe
|
||||
// is easy.
|
||||
//
|
||||
// Disable this behaviour, instead repurposing this action to behave
|
||||
// the same as the `tapAction` ("tap on PhotoSwipe viewport
|
||||
// content") and toggle the visibility of UI controls (We also have
|
||||
// auto hide based on mouse activity, but that would not have any
|
||||
// effect on touch devices)
|
||||
bgClickAction: "toggle-controls",
|
||||
// At least on macOS, manual zooming with the trackpad is very
|
||||
// cumbersome (possibly because of the small multiplier in the
|
||||
// PhotoSwipe source, but I'm not sure). The other option to do a
|
||||
// manual zoom is to scroll (e.g. with the trackpad) but with the
|
||||
// CTRL key pressed, however on macOS this invokes the system zoom
|
||||
// if enabled in accessibility settings.
|
||||
//
|
||||
// Taking a step back though, the PhotoSwipe viewport is fixed, so
|
||||
// we can just directly map wheel / trackpad scrolls to zooming.
|
||||
wheelToZoom: true,
|
||||
// Set the index within files that we should open to. Subsequent
|
||||
// updates to the index will be tracked by PhotoSwipe internally.
|
||||
index: initialIndex,
|
||||
// TODO(PS): padding option? for handling custom title bar.
|
||||
// TODO(PS): will we need this?
|
||||
mainClass: "our-extra-pswp-main-class",
|
||||
});
|
||||
|
||||
// Provide data about slides to PhotoSwipe via callbacks
|
||||
// https://photoswipe.com/data-sources/#dynamically-generated-data
|
||||
|
||||
pswp.addFilter("numItems", () => {
|
||||
return this.files.length;
|
||||
});
|
||||
|
||||
pswp.addFilter("itemData", (_, index) => {
|
||||
const file = files[index];
|
||||
|
||||
let itemData: SlideData | undefined;
|
||||
if (file) {
|
||||
itemData = this.itemDataByFileID.get(file.id);
|
||||
if (!itemData) {
|
||||
// We don't have anything to show immediately, though in
|
||||
// most cases a cached renderable thumbnail URL will be
|
||||
// available shortly.
|
||||
//
|
||||
// Meanwhile,
|
||||
//
|
||||
// 1. Return empty slide data; PhotoSwipe will not show
|
||||
// anything in the image area but will otherwise render
|
||||
// the surrounding UI properly.
|
||||
//
|
||||
// 2. Insert empty data so that we don't enqueue multiple
|
||||
// updates.
|
||||
itemData = {};
|
||||
this.itemDataByFileID.set(file.id, itemData);
|
||||
this.enqueueUpdates(index, file);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug(() => ["[ps]", { itemData, index, file, itemData }]);
|
||||
if (!file) assertionFailed();
|
||||
|
||||
if (this.lastActivityDate != "already-hidden")
|
||||
this.lastActivityDate = new Date();
|
||||
|
||||
return itemData ?? {};
|
||||
});
|
||||
|
||||
pswp.addFilter("preventPointerEvent", (originalResult) => {
|
||||
// There was a pointer event. We don't care which one, we just use
|
||||
// this as a hook to show UI again (if needed) and update our last
|
||||
// activity date.
|
||||
this.onPointerActivity();
|
||||
return originalResult;
|
||||
});
|
||||
|
||||
pswp.on("contentLoad", (e) => {
|
||||
console.log("contentLoad", e);
|
||||
if (e.content.data.videoURL) {
|
||||
const holderEl = e.content.slide.holderElement;
|
||||
const vid = document.createElement("h1");
|
||||
vid.innerText = "Test 1";
|
||||
holderEl.appendChild(vid);
|
||||
}
|
||||
});
|
||||
pswp.on("contentAppend", (e) => {
|
||||
const containerEl = e.content.slide.container;
|
||||
console.log("contentAppend", containerEl);
|
||||
if (e.content.data.videoURL) {
|
||||
const vid = document.createElement("div");
|
||||
vid.innerHTML = livePhotoVideoHTML(e.content.data.videoURL);
|
||||
// vid.innerText = "Test 2";
|
||||
containerEl.appendChild(vid);
|
||||
vid.style =
|
||||
"position: absolute; left: 0; right: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none;";
|
||||
}
|
||||
});
|
||||
|
||||
pswp.on("close", () => {
|
||||
// The user did some action within the file viewer to close it.
|
||||
//
|
||||
// Clear intervals.
|
||||
clearIntervals();
|
||||
// Let our parent know that we have been closed.
|
||||
onClose();
|
||||
});
|
||||
|
||||
// Initializing PhotoSwipe adds it to the DOM as a dialog-like div with
|
||||
// the class "pswp".
|
||||
pswp.init();
|
||||
|
||||
this.pswp = pswp;
|
||||
|
||||
this.autoHideCheckIntervalId = setInterval(() => {
|
||||
this.autoHideIfInactive();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this instance of {@link FileViewerPhotoSwipe} if it hasn't itself
|
||||
* initiated the close.
|
||||
*
|
||||
* This instance **cannot** be used after this function has been called.
|
||||
*/
|
||||
closeIfNeeded() {
|
||||
// Closing PhotoSwipe removes it from the DOM.
|
||||
//
|
||||
// This will only have an effect if we're being closed externally (e.g.
|
||||
// if the user selects an album in the file info).
|
||||
//
|
||||
// If this cleanup function is running in the sequence where we were
|
||||
// closed internally (e.g. the user activated the close button within
|
||||
// the file viewer), then PhotoSwipe will ignore this extra close.
|
||||
this.pswp.close();
|
||||
this.clearAutoHideIntervalIfNeeded();
|
||||
}
|
||||
|
||||
updateFiles(files: EnteFile[]) {
|
||||
// TODO(PS)
|
||||
}
|
||||
|
||||
private clearAutoHideIntervalIfNeeded() {
|
||||
if (this.autoHideCheckIntervalId) {
|
||||
clearInterval(this.autoHideCheckIntervalId);
|
||||
this.autoHideCheckIntervalId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private onPointerActivity() {
|
||||
if (this.lastActivityDate == "already-hidden") return;
|
||||
if (this.lastActivityDate == "auto-hidden") this.showUIControls();
|
||||
this.lastActivityDate = new Date();
|
||||
}
|
||||
|
||||
private autoHideIfInactive() {
|
||||
if (this.lastActivityDate == "already-hidden") return;
|
||||
if (this.lastActivityDate == "auto-hidden") return;
|
||||
if (Date.now() - this.lastActivityDate.getTime() > 3000) {
|
||||
if (this.areUIControlsVisible()) {
|
||||
this.hideUIControls();
|
||||
this.lastActivityDate = "auto-hidden";
|
||||
} else {
|
||||
this.lastActivityDate = "already-hidden";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private areUIControlsVisible() {
|
||||
return this.pswp.element.classList.contains("pswp--ui-visible");
|
||||
}
|
||||
|
||||
private showUIControls() {
|
||||
this.pswp.element.classList.add("pswp--ui-visible");
|
||||
}
|
||||
|
||||
private hideUIControls() {
|
||||
this.pswp.element.classList.remove("pswp--ui-visible");
|
||||
}
|
||||
|
||||
private async enqueueUpdates(index: number, file: EnteFile) {
|
||||
const update = (itemData: SlideData) => {
|
||||
this.itemDataByFileID.set(file.id, itemData);
|
||||
this.pswp.refreshSlideContent(index);
|
||||
};
|
||||
|
||||
const thumbnailURL = await downloadManager.renderableThumbnailURL(file);
|
||||
// We don't have the dimensions of the thumbnail. We could try to deduce
|
||||
// something from the file's aspect ratio etc, but that's not needed:
|
||||
// PhotoSwipe already correctly (for our purposes) handles just a source
|
||||
// URL being present.
|
||||
update({ src: thumbnailURL });
|
||||
|
||||
switch (file.metadata.fileType) {
|
||||
case FileType.image: {
|
||||
const sourceURLs =
|
||||
await downloadManager.renderableSourceURLs(file);
|
||||
update({
|
||||
src: sourceURLs.url,
|
||||
width: file.pubMagicMetadata?.data?.w,
|
||||
height: file.pubMagicMetadata?.data?.h,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case FileType.video: {
|
||||
const sourceURLs =
|
||||
await downloadManager.renderableSourceURLs(file);
|
||||
const disableDownload = !!this.opts.disableDownload;
|
||||
update({ html: videoHTML(sourceURLs.url, disableDownload) });
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
const sourceURLs =
|
||||
await downloadManager.renderableSourceURLs(file);
|
||||
const livePhotoSourceURLs =
|
||||
sourceURLs.url as LivePhotoSourceURL;
|
||||
const imageURL = await livePhotoSourceURLs.image();
|
||||
update({
|
||||
src: imageURL,
|
||||
width: file.pubMagicMetadata?.data?.w,
|
||||
height: file.pubMagicMetadata?.data?.h,
|
||||
});
|
||||
const videoURL = await livePhotoSourceURLs.video();
|
||||
console.log(videoURL);
|
||||
// update({ html: livePhotoVideoHTML(videoURL) });
|
||||
update({
|
||||
src: imageURL,
|
||||
width: file.pubMagicMetadata?.data?.w,
|
||||
height: file.pubMagicMetadata?.data?.h,
|
||||
videoURL,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const videoHTML = (url: string, disableDownload: boolean) => `
|
||||
<video controls ${disableDownload && "controlsList=nodownload"} oncontextmenu="return false;">
|
||||
<source src="${url}" />
|
||||
Your browser does not support video playback.
|
||||
</video>
|
||||
`;
|
||||
|
||||
const livePhotoVideoHTML = (videoURL: string) => `
|
||||
<video autoplay loop muted oncontextmenu="return false;">
|
||||
<source src="${videoURL}" />
|
||||
</video>
|
||||
`;
|
@ -9,6 +9,19 @@ import { t } from "i18next";
|
||||
import { useState } from "react";
|
||||
import { aboveGalleryContentZ } 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("./FileViewer5"), { 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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user