From ed60c290ce6d3f408c2cafd3afde367a14aa4a04 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Sat, 4 Jun 2022 16:51:06 +0530 Subject: [PATCH] udpate photoswipe info dialog --- src/components/Container.ts | 5 +- src/components/PhotoFrame.tsx | 2 +- .../PhotoSwipe/InfoDialog/ExifData.tsx | 63 ++ .../InfoDialog/FileNameEditForm.tsx | 99 ++ .../InfoDialog/RenderCreationTime.tsx | 121 +++ .../PhotoSwipe/InfoDialog/RenderFileName.tsx | 103 ++ .../PhotoSwipe/InfoDialog/RenderInfoItem.tsx | 9 + .../PhotoSwipe/InfoDialog/index.tsx | 77 ++ src/components/PhotoSwipe/PhotoSwipe.tsx | 908 ------------------ src/components/PhotoSwipe/index.tsx | 437 +++++++++ .../PhotoSwipe/styledComponents/Legend.tsx | 7 + .../styledComponents/LegendContainer.tsx | 6 + .../styledComponents/LivePhotoBtn.tsx | 16 + .../PhotoSwipe/styledComponents/Pre.tsx | 6 + .../styledComponents/SmallLoadingSpinner.tsx | 11 + src/constants/photoswipe/index.ts | 7 + src/utils/strings/englishConstants.tsx | 28 +- 17 files changed, 979 insertions(+), 926 deletions(-) create mode 100644 src/components/PhotoSwipe/InfoDialog/ExifData.tsx create mode 100644 src/components/PhotoSwipe/InfoDialog/FileNameEditForm.tsx create mode 100644 src/components/PhotoSwipe/InfoDialog/RenderCreationTime.tsx create mode 100644 src/components/PhotoSwipe/InfoDialog/RenderFileName.tsx create mode 100644 src/components/PhotoSwipe/InfoDialog/RenderInfoItem.tsx create mode 100644 src/components/PhotoSwipe/InfoDialog/index.tsx delete mode 100644 src/components/PhotoSwipe/PhotoSwipe.tsx create mode 100644 src/components/PhotoSwipe/index.tsx create mode 100644 src/components/PhotoSwipe/styledComponents/Legend.tsx create mode 100644 src/components/PhotoSwipe/styledComponents/LegendContainer.tsx create mode 100644 src/components/PhotoSwipe/styledComponents/LivePhotoBtn.tsx create mode 100644 src/components/PhotoSwipe/styledComponents/Pre.tsx create mode 100644 src/components/PhotoSwipe/styledComponents/SmallLoadingSpinner.tsx create mode 100644 src/constants/photoswipe/index.ts diff --git a/src/components/Container.ts b/src/components/Container.ts index c6016873e..53c414e7f 100644 --- a/src/components/Container.ts +++ b/src/components/Container.ts @@ -41,20 +41,19 @@ export const IconButton = styled.button` export const Row = styled.div` display: flex; align-items: center; - margin-bottom: 20px; + margin-bottom: ${({ theme }) => theme.spacing(2)}; flex: 1; `; export const Label = styled.div<{ width?: string }>` width: ${(props) => props.width ?? '70%'}; + color: ${(props) => props.theme.palette.text.secondary}; `; export const Value = styled.div<{ width?: string }>` display: flex; justify-content: flex-start; align-items: center; width: ${(props) => props.width ?? '30%'}; - - color: #ddd; `; export const FlexWrapper = styled(Box)` diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index 9e4a053ab..01f26afd1 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -6,7 +6,7 @@ import styled from 'styled-components'; import DownloadManager from 'services/downloadManager'; import constants from 'utils/strings/constants'; import AutoSizer from 'react-virtualized-auto-sizer'; -import PhotoSwipe from 'components/PhotoSwipe/PhotoSwipe'; +import PhotoSwipe from 'components/PhotoSwipe'; import { formatDateRelative } from 'utils/file'; import { ALL_SECTION, diff --git a/src/components/PhotoSwipe/InfoDialog/ExifData.tsx b/src/components/PhotoSwipe/InfoDialog/ExifData.tsx new file mode 100644 index 000000000..8c5545d81 --- /dev/null +++ b/src/components/PhotoSwipe/InfoDialog/ExifData.tsx @@ -0,0 +1,63 @@ +import React, { useState } from 'react'; +import constants from 'utils/strings/constants'; +import { FormCheck } from 'react-bootstrap'; + +import { RenderInfoItem } from './RenderInfoItem'; +import { LegendContainer } from '../styledComponents/LegendContainer'; +import { Pre } from '../styledComponents/Pre'; +import { Typography } from '@mui/material'; + +export function ExifData(props: { exif: any }) { + const { exif } = props; + const [showAll, setShowAll] = useState(false); + + const changeHandler = (e: React.ChangeEvent) => { + setShowAll(e.target.checked); + }; + + const renderAllValues = () =>
{exif.raw}
; + + const renderSelectedValues = () => ( + <> + {exif?.Make && + exif?.Model && + RenderInfoItem(constants.DEVICE, `${exif.Make} ${exif.Model}`)} + {exif?.ImageWidth && + exif?.ImageHeight && + RenderInfoItem( + constants.IMAGE_SIZE, + `${exif.ImageWidth} x ${exif.ImageHeight}` + )} + {exif?.Flash && RenderInfoItem(constants.FLASH, exif.Flash)} + {exif?.FocalLength && + RenderInfoItem( + constants.FOCAL_LENGTH, + exif.FocalLength.toString() + )} + {exif?.ApertureValue && + RenderInfoItem( + constants.APERTURE, + exif.ApertureValue.toString() + )} + {exif?.ISOSpeedRatings && + RenderInfoItem(constants.ISO, exif.ISOSpeedRatings.toString())} + + ); + + return ( + <> + + + {constants.EXIF} + + + + + {constants.SHOW_ALL} + + + + {showAll ? renderAllValues() : renderSelectedValues()} + + ); +} diff --git a/src/components/PhotoSwipe/InfoDialog/FileNameEditForm.tsx b/src/components/PhotoSwipe/InfoDialog/FileNameEditForm.tsx new file mode 100644 index 000000000..bead3e83d --- /dev/null +++ b/src/components/PhotoSwipe/InfoDialog/FileNameEditForm.tsx @@ -0,0 +1,99 @@ +import React, { useState } from 'react'; +import constants from 'utils/strings/constants'; +import { Col, Form, FormControl } from 'react-bootstrap'; +import { FlexWrapper, IconButton, Value } from 'components/Container'; +import CloseIcon from '@mui/icons-material/Close'; +import TickIcon from '@mui/icons-material/Done'; +import { Formik } from 'formik'; +import * as Yup from 'yup'; +import { MAX_EDITED_FILE_NAME_LENGTH } from 'constants/file'; +import { SmallLoadingSpinner } from '../styledComponents/SmallLoadingSpinner'; + +export interface formValues { + filename: string; +} + +export const FileNameEditForm = ({ + filename, + saveEdits, + discardEdits, + extension, +}) => { + const [loading, setLoading] = useState(false); + + const onSubmit = async (values: formValues) => { + try { + setLoading(true); + await saveEdits(values.filename); + } finally { + setLoading(false); + } + }; + return ( + + initialValues={{ filename }} + validationSchema={Yup.object().shape({ + filename: Yup.string() + .required(constants.REQUIRED) + .max( + MAX_EDITED_FILE_NAME_LENGTH, + constants.FILE_NAME_CHARACTER_LIMIT + ), + })} + validateOnBlur={false} + onSubmit={onSubmit}> + {({ values, errors, handleChange, handleSubmit }) => ( +
+ + + + + {errors.filename} + + + {extension && ( + + + {`.${extension}`} + + + )} + + + + {loading ? ( + + ) : ( + + )} + + + + + + + +
+ )} + + ); +}; diff --git a/src/components/PhotoSwipe/InfoDialog/RenderCreationTime.tsx b/src/components/PhotoSwipe/InfoDialog/RenderCreationTime.tsx new file mode 100644 index 000000000..b27152d04 --- /dev/null +++ b/src/components/PhotoSwipe/InfoDialog/RenderCreationTime.tsx @@ -0,0 +1,121 @@ +import React, { useState } from 'react'; +import { updateFilePublicMagicMetadata } from 'services/fileService'; +import { EnteFile } from 'types/file'; +import constants from 'utils/strings/constants'; +import { + changeFileCreationTime, + formatDateTime, + updateExistingFilePubMetadata, +} from 'utils/file'; +import EditIcon from 'components/icons/EditIcon'; +import { IconButton, Label, Row, Value } from 'components/Container'; +import { logError } from 'utils/sentry'; +import CloseIcon from '@mui/icons-material/Close'; +import TickIcon from '@mui/icons-material/Done'; +import EnteDateTimePicker from 'components/EnteDateTimePicker'; +import { SmallLoadingSpinner } from '../styledComponents/SmallLoadingSpinner'; + +export function RenderCreationTime({ + shouldDisableEdits, + file, + scheduleUpdate, +}: { + shouldDisableEdits: boolean; + file: EnteFile; + scheduleUpdate: () => void; +}) { + const [loading, setLoading] = useState(false); + const originalCreationTime = new Date(file?.metadata.creationTime / 1000); + const [isInEditMode, setIsInEditMode] = useState(false); + + const [pickedTime, setPickedTime] = useState(originalCreationTime); + + const openEditMode = () => setIsInEditMode(true); + const closeEditMode = () => setIsInEditMode(false); + + const saveEdits = async () => { + try { + setLoading(true); + if (isInEditMode && file) { + const unixTimeInMicroSec = pickedTime.getTime() * 1000; + if (unixTimeInMicroSec === file?.metadata.creationTime) { + closeEditMode(); + return; + } + let updatedFile = await changeFileCreationTime( + file, + unixTimeInMicroSec + ); + updatedFile = ( + await updateFilePublicMagicMetadata([updatedFile]) + )[0]; + updateExistingFilePubMetadata(file, updatedFile); + scheduleUpdate(); + } + } catch (e) { + logError(e, 'failed to update creationTime'); + } finally { + closeEditMode(); + setLoading(false); + } + }; + const discardEdits = () => { + setPickedTime(originalCreationTime); + closeEditMode(); + }; + const handleChange = (newDate: Date) => { + if (newDate instanceof Date) { + setPickedTime(newDate); + } + }; + return ( + <> + + + + {isInEditMode ? ( + + ) : ( + formatDateTime(pickedTime) + )} + + {!shouldDisableEdits && ( + + {!isInEditMode ? ( + + + + ) : ( + <> + + {loading ? ( + + ) : ( + + )} + + + + + + )} + + )} + + + ); +} diff --git a/src/components/PhotoSwipe/InfoDialog/RenderFileName.tsx b/src/components/PhotoSwipe/InfoDialog/RenderFileName.tsx new file mode 100644 index 000000000..dda68be23 --- /dev/null +++ b/src/components/PhotoSwipe/InfoDialog/RenderFileName.tsx @@ -0,0 +1,103 @@ +import React, { useState } from 'react'; +import { updateFilePublicMagicMetadata } from 'services/fileService'; +import { EnteFile } from 'types/file'; +import constants from 'utils/strings/constants'; +import { + changeFileName, + splitFilenameAndExtension, + updateExistingFilePubMetadata, +} from 'utils/file'; +import EditIcon from 'components/icons/EditIcon'; +import { + FreeFlowText, + IconButton, + Label, + Row, + Value, +} from 'components/Container'; +import { logError } from 'utils/sentry'; +import { FileNameEditForm } from './FileNameEditForm'; + +export const getFileTitle = (filename, extension) => { + if (extension) { + return filename + '.' + extension; + } else { + return filename; + } +}; + +export function RenderFileName({ + shouldDisableEdits, + file, + scheduleUpdate, +}: { + shouldDisableEdits: boolean; + file: EnteFile; + scheduleUpdate: () => void; +}) { + const originalTitle = file?.metadata.title; + const [isInEditMode, setIsInEditMode] = useState(false); + const [originalFileName, extension] = + splitFilenameAndExtension(originalTitle); + const [filename, setFilename] = useState(originalFileName); + const openEditMode = () => setIsInEditMode(true); + const closeEditMode = () => setIsInEditMode(false); + + const saveEdits = async (newFilename: string) => { + try { + if (file) { + if (filename === newFilename) { + closeEditMode(); + return; + } + setFilename(newFilename); + const newTitle = getFileTitle(newFilename, extension); + let updatedFile = await changeFileName(file, newTitle); + updatedFile = ( + await updateFilePublicMagicMetadata([updatedFile]) + )[0]; + updateExistingFilePubMetadata(file, updatedFile); + scheduleUpdate(); + } + } catch (e) { + logError(e, 'failed to update file name'); + } finally { + closeEditMode(); + } + }; + return ( + <> + + + {!isInEditMode ? ( + <> + + + {getFileTitle(filename, extension)} + + + {!shouldDisableEdits && ( + + + + + + )} + + ) : ( + + )} + + + ); +} diff --git a/src/components/PhotoSwipe/InfoDialog/RenderInfoItem.tsx b/src/components/PhotoSwipe/InfoDialog/RenderInfoItem.tsx new file mode 100644 index 000000000..a538df52f --- /dev/null +++ b/src/components/PhotoSwipe/InfoDialog/RenderInfoItem.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { Label, Row, Value } from 'components/Container'; + +export const RenderInfoItem = (label: string, value: string | JSX.Element) => ( + + + {value} + +); diff --git a/src/components/PhotoSwipe/InfoDialog/index.tsx b/src/components/PhotoSwipe/InfoDialog/index.tsx new file mode 100644 index 000000000..793d88de4 --- /dev/null +++ b/src/components/PhotoSwipe/InfoDialog/index.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import constants from 'utils/strings/constants'; +import { formatDateTime } from 'utils/file'; +import { RenderFileName } from './RenderFileName'; +import { ExifData } from './ExifData'; +import { RenderCreationTime } from './RenderCreationTime'; +import { RenderInfoItem } from './RenderInfoItem'; +import DialogBoxBase from 'components/DialogBox/base'; +import DialogTitleWithCloseButton from 'components/DialogBox/titleWithCloseButton'; +import { DialogContent, Typography } from '@mui/material'; + +export function InfoModal({ + shouldDisableEdits, + showInfo, + handleCloseInfo, + items, + photoSwipe, + metadata, + exif, + scheduleUpdate, +}) { + return ( + + + {constants.INFO} + + + + {constants.METADATA} + + + {RenderInfoItem( + constants.FILE_ID, + items[photoSwipe?.getCurrentIndex()]?.id + )} + {metadata?.title && ( + + )} + {metadata?.creationTime && ( + + )} + {metadata?.modificationTime && + RenderInfoItem( + constants.UPDATED_ON, + formatDateTime(metadata.modificationTime / 1000) + )} + {metadata?.longitude > 0 && + metadata?.longitude > 0 && + RenderInfoItem( + constants.LOCATION, + + {constants.SHOW_MAP} + + )} + {exif && ( + <> + + + )} + + + ); +} diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx deleted file mode 100644 index e8831be91..000000000 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ /dev/null @@ -1,908 +0,0 @@ -import React, { useContext, useEffect, useRef, useState } from 'react'; -import Photoswipe from 'photoswipe'; -import PhotoswipeUIDefault from 'photoswipe/dist/photoswipe-ui-default'; -import classnames from 'classnames'; -import FavButton from 'components/FavButton'; -import { - addToFavorites, - removeFromFavorites, -} from 'services/collectionService'; -import { updateFilePublicMagicMetadata } from 'services/fileService'; -import { EnteFile } from 'types/file'; -import constants from 'utils/strings/constants'; -import exifr from 'exifr'; -import Modal from 'react-bootstrap/Modal'; -import Button from 'react-bootstrap/Button'; -import styled from 'styled-components'; -import events from './events'; -import { - changeFileCreationTime, - changeFileName, - downloadFile, - formatDateTime, - splitFilenameAndExtension, - updateExistingFilePubMetadata, -} from 'utils/file'; -import { Col, Form, FormCheck, FormControl } from 'react-bootstrap'; -import { prettyPrintExif } from 'utils/exif'; -import EditIcon from 'components/icons/EditIcon'; -import { - FlexWrapper, - FreeFlowText, - IconButton, - Label, - Row, - Value, -} from 'components/Container'; -import { livePhotoBtnHTML } from 'components/LivePhotoBtn'; -import { logError } from 'utils/sentry'; - -import CloseIcon from '@mui/icons-material/Close'; -import TickIcon from '@mui/icons-material/Done'; -import { Formik } from 'formik'; -import * as Yup from 'yup'; -import EnteSpinner from 'components/EnteSpinner'; -import EnteDateTimePicker from 'components/EnteDateTimePicker'; -import { MAX_EDITED_FILE_NAME_LENGTH, FILE_TYPE } from 'constants/file'; -import { sleep } from 'utils/common'; -import { playVideo, pauseVideo } from 'utils/photoFrame'; -import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery'; -import { GalleryContext } from 'pages/gallery'; -import { AppContext } from 'pages/_app'; - -const SmallLoadingSpinner = () => ( - -); -interface Iprops { - isOpen: boolean; - items: any[]; - currentIndex?: number; - onClose?: (needUpdate: boolean) => void; - gettingData: (instance: any, index: number, item: EnteFile) => void; - id?: string; - className?: string; - favItemIds: Set; - isSharedCollection: boolean; - isTrashCollection: boolean; - enableDownload: boolean; - isSourceLoaded: boolean; -} - -const LegendContainer = styled.div` - display: flex; - justify-content: space-between; -`; - -const Legend = styled.span` - font-size: 20px; - color: #ddd; - display: inline; -`; - -const Pre = styled.pre` - color: #aaa; - padding: 7px 15px; -`; - -const LivePhotoBtn = styled.button` - position: absolute; - bottom: 6vh; - right: 6vh; - height: 40px; - width: 80px; - background: #d7d7d7; - outline: none; - border: none; - border-radius: 10%; - z-index: 10; - cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')}; - } -`; - -const livePhotoDefaultOptions = { - click: () => {}, - hide: () => {}, - show: () => {}, - loading: false, - visible: false, -}; - -const renderInfoItem = (label: string, value: string | JSX.Element) => ( - - - {value} - -); - -function RenderCreationTime({ - shouldDisableEdits, - file, - scheduleUpdate, -}: { - shouldDisableEdits: boolean; - file: EnteFile; - scheduleUpdate: () => void; -}) { - const [loading, setLoading] = useState(false); - const originalCreationTime = new Date(file?.metadata.creationTime / 1000); - const [isInEditMode, setIsInEditMode] = useState(false); - - const [pickedTime, setPickedTime] = useState(originalCreationTime); - - const openEditMode = () => setIsInEditMode(true); - const closeEditMode = () => setIsInEditMode(false); - - const saveEdits = async () => { - try { - setLoading(true); - if (isInEditMode && file) { - const unixTimeInMicroSec = pickedTime.getTime() * 1000; - if (unixTimeInMicroSec === file?.metadata.creationTime) { - closeEditMode(); - return; - } - let updatedFile = await changeFileCreationTime( - file, - unixTimeInMicroSec - ); - updatedFile = ( - await updateFilePublicMagicMetadata([updatedFile]) - )[0]; - updateExistingFilePubMetadata(file, updatedFile); - scheduleUpdate(); - } - } catch (e) { - logError(e, 'failed to update creationTime'); - } finally { - closeEditMode(); - setLoading(false); - } - }; - const discardEdits = () => { - setPickedTime(originalCreationTime); - closeEditMode(); - }; - const handleChange = (newDate: Date) => { - if (newDate instanceof Date) { - setPickedTime(newDate); - } - }; - return ( - <> - - - - {isInEditMode ? ( - - ) : ( - formatDateTime(pickedTime) - )} - - {!shouldDisableEdits && ( - - {!isInEditMode ? ( - - - - ) : ( - <> - - {loading ? ( - - ) : ( - - )} - - - - - - )} - - )} - - - ); -} -const getFileTitle = (filename, extension) => { - if (extension) { - return filename + '.' + extension; - } else { - return filename; - } -}; -interface formValues { - filename: string; -} - -const FileNameEditForm = ({ filename, saveEdits, discardEdits, extension }) => { - const [loading, setLoading] = useState(false); - - const onSubmit = async (values: formValues) => { - try { - setLoading(true); - await saveEdits(values.filename); - } finally { - setLoading(false); - } - }; - return ( - - initialValues={{ filename }} - validationSchema={Yup.object().shape({ - filename: Yup.string() - .required(constants.REQUIRED) - .max( - MAX_EDITED_FILE_NAME_LENGTH, - constants.FILE_NAME_CHARACTER_LIMIT - ), - })} - validateOnBlur={false} - onSubmit={onSubmit}> - {({ values, errors, handleChange, handleSubmit }) => ( -
- - - - - {errors.filename} - - - {extension && ( - - - {`.${extension}`} - - - )} - - - - {loading ? ( - - ) : ( - - )} - - - - - - - -
- )} - - ); -}; - -function RenderFileName({ - shouldDisableEdits, - file, - scheduleUpdate, -}: { - shouldDisableEdits: boolean; - file: EnteFile; - scheduleUpdate: () => void; -}) { - const originalTitle = file?.metadata.title; - const [isInEditMode, setIsInEditMode] = useState(false); - const [originalFileName, extension] = - splitFilenameAndExtension(originalTitle); - const [filename, setFilename] = useState(originalFileName); - const openEditMode = () => setIsInEditMode(true); - const closeEditMode = () => setIsInEditMode(false); - - const saveEdits = async (newFilename: string) => { - try { - if (file) { - if (filename === newFilename) { - closeEditMode(); - return; - } - setFilename(newFilename); - const newTitle = getFileTitle(newFilename, extension); - let updatedFile = await changeFileName(file, newTitle); - updatedFile = ( - await updateFilePublicMagicMetadata([updatedFile]) - )[0]; - updateExistingFilePubMetadata(file, updatedFile); - scheduleUpdate(); - } - } catch (e) { - logError(e, 'failed to update file name'); - } finally { - closeEditMode(); - } - }; - return ( - <> - - - {!isInEditMode ? ( - <> - - - {getFileTitle(filename, extension)} - - - {!shouldDisableEdits && ( - - - - - - )} - - ) : ( - - )} - - - ); -} -function ExifData(props: { exif: any }) { - const { exif } = props; - const [showAll, setShowAll] = useState(false); - - const changeHandler = (e: React.ChangeEvent) => { - setShowAll(e.target.checked); - }; - - const renderAllValues = () =>
{exif.raw}
; - - const renderSelectedValues = () => ( - <> - {exif?.Make && - exif?.Model && - renderInfoItem(constants.DEVICE, `${exif.Make} ${exif.Model}`)} - {exif?.ImageWidth && - exif?.ImageHeight && - renderInfoItem( - constants.IMAGE_SIZE, - `${exif.ImageWidth} x ${exif.ImageHeight}` - )} - {exif?.Flash && renderInfoItem(constants.FLASH, exif.Flash)} - {exif?.FocalLength && - renderInfoItem( - constants.FOCAL_LENGTH, - exif.FocalLength.toString() - )} - {exif?.ApertureValue && - renderInfoItem( - constants.APERTURE, - exif.ApertureValue.toString() - )} - {exif?.ISOSpeedRatings && - renderInfoItem(constants.ISO, exif.ISOSpeedRatings.toString())} - - ); - - return ( - <> - - {constants.EXIF} - - - - {constants.SHOW_ALL} - - - - {showAll ? renderAllValues() : renderSelectedValues()} - - ); -} - -function InfoModal({ - shouldDisableEdits, - showInfo, - handleCloseInfo, - items, - photoSwipe, - metadata, - exif, - scheduleUpdate, -}) { - return ( - - - {constants.INFO} - - -
- {constants.METADATA} -
- {renderInfoItem( - constants.FILE_ID, - items[photoSwipe?.getCurrentIndex()]?.id - )} - {metadata?.title && ( - - )} - {metadata?.creationTime && ( - - )} - {metadata?.modificationTime && - renderInfoItem( - constants.UPDATED_ON, - formatDateTime(metadata.modificationTime / 1000) - )} - {metadata?.longitude > 0 && - metadata?.longitude > 0 && - renderInfoItem( - constants.LOCATION, - - {constants.SHOW_MAP} - - )} - {exif && ( - <> - - - )} -
- - - -
- ); -} - -function PhotoSwipe(props: Iprops) { - const pswpElement = useRef(); - const [photoSwipe, setPhotoSwipe] = - useState>(); - - const { isOpen, items, isSourceLoaded } = props; - const [isFav, setIsFav] = useState(false); - const [showInfo, setShowInfo] = useState(false); - const [metadata, setMetaData] = useState(null); - const [exif, setExif] = useState(null); - const [livePhotoBtnOptions, setLivePhotoBtnOptions] = useState( - livePhotoDefaultOptions - ); - const needUpdate = useRef(false); - const publicCollectionGalleryContext = useContext( - PublicCollectionGalleryContext - ); - const galleryContext = useContext(GalleryContext); - const appContext = useContext(AppContext); - - useEffect(() => { - if (!pswpElement) return; - if (isOpen) { - openPhotoSwipe(); - } - if (!isOpen) { - closePhotoSwipe(); - } - return () => { - closePhotoSwipe(); - }; - }, [isOpen]); - - useEffect(() => { - updateItems(items); - }, [items]); - - useEffect(() => { - if (photoSwipe) { - photoSwipe.options.arrowKeys = !showInfo; - photoSwipe.options.escKey = !showInfo; - } - }, [showInfo]); - - useEffect(() => { - if (!isOpen) return; - const item = items[photoSwipe?.getCurrentIndex()]; - if (item && item.metadata.fileType === FILE_TYPE.LIVE_PHOTO) { - const getVideoAndImage = () => { - const video = document.getElementById( - `live-photo-video-${item.id}` - ); - const image = document.getElementById( - `live-photo-image-${item.id}` - ); - return { video, image }; - }; - - const { video, image } = getVideoAndImage(); - - if (video && image) { - setLivePhotoBtnOptions({ - click: async () => { - await playVideo(video, image); - }, - hide: async () => { - await pauseVideo(video, image); - }, - show: async () => { - await playVideo(video, image); - }, - visible: true, - loading: false, - }); - } else { - setLivePhotoBtnOptions({ - ...livePhotoDefaultOptions, - visible: true, - loading: true, - }); - } - - const downloadLivePhotoBtn = document.getElementById( - `download-btn-${item.id}` - ) as HTMLButtonElement; - if (downloadLivePhotoBtn) { - const downloadLivePhoto = () => { - downloadFileHelper(photoSwipe.currItem); - }; - - downloadLivePhotoBtn.addEventListener( - 'click', - downloadLivePhoto - ); - return () => { - downloadLivePhotoBtn.removeEventListener( - 'click', - downloadLivePhoto - ); - setLivePhotoBtnOptions(livePhotoDefaultOptions); - }; - } - - return () => { - setLivePhotoBtnOptions(livePhotoDefaultOptions); - }; - } - }, [photoSwipe?.currItem, isOpen, isSourceLoaded]); - - function updateFavButton() { - setIsFav(isInFav(this?.currItem)); - } - - const openPhotoSwipe = () => { - const { items, currentIndex } = props; - const options = { - history: false, - maxSpreadZoom: 5, - index: currentIndex, - showHideOpacity: true, - getDoubleTapZoom(isMouseClick, item) { - if (isMouseClick) { - return 2.5; - } - // zoom to original if initial zoom is less than 0.7x, - // otherwise to 1.5x, to make sure that double-tap gesture always zooms image - return item.initialZoomLevel < 0.7 ? 1 : 1.5; - }, - getThumbBoundsFn: (index) => { - try { - const file = items[index]; - const ele = document.getElementById(`thumb-${file.id}`); - if (ele) { - const rect = ele.getBoundingClientRect(); - const pageYScroll = - window.pageYOffset || - document.documentElement.scrollTop; - return { - x: rect.left, - y: rect.top + pageYScroll, - w: rect.width, - }; - } - return null; - } catch (e) { - return null; - } - }, - }; - const photoSwipe = new Photoswipe( - pswpElement.current, - PhotoswipeUIDefault, - items, - options - ); - events.forEach((event) => { - const callback = props[event]; - if (callback || event === 'destroy') { - photoSwipe.listen(event, function (...args) { - if (callback) { - args.unshift(this); - callback(...args); - } - if (event === 'destroy') { - handleClose(); - } - if (event === 'close') { - handleClose(); - } - }); - } - }); - photoSwipe.listen('beforeChange', function () { - updateInfo.call(this); - updateFavButton.call(this); - }); - photoSwipe.listen('resize', checkExifAvailable); - photoSwipe.init(); - needUpdate.current = false; - setPhotoSwipe(photoSwipe); - }; - - const closePhotoSwipe = () => { - if (photoSwipe) photoSwipe.close(); - }; - - const handleClose = () => { - const { onClose } = props; - if (typeof onClose === 'function') { - onClose(needUpdate.current); - } - const videoTags = document.getElementsByTagName('video'); - for (const videoTag of videoTags) { - videoTag.pause(); - } - handleCloseInfo(); - // BE_AWARE: this will clear any notification set, even if they were not set in/by the photoswipe component - galleryContext.setNotificationAttributes(null); - }; - const isInFav = (file) => { - const { favItemIds } = props; - if (favItemIds && file) { - return favItemIds.has(file.id); - } - return false; - }; - - const onFavClick = async (file) => { - const { favItemIds } = props; - if (!isInFav(file)) { - favItemIds.add(file.id); - addToFavorites(file); - setIsFav(true); - } else { - favItemIds.delete(file.id); - removeFromFavorites(file); - setIsFav(false); - } - needUpdate.current = true; - }; - - const updateItems = (items = []) => { - if (photoSwipe) { - photoSwipe.items.length = 0; - items.forEach((item) => { - photoSwipe.items.push(item); - }); - photoSwipe.invalidateCurrItems(); - // photoSwipe.updateSize(true); - } - }; - - const checkExifAvailable = async () => { - setExif(null); - await sleep(100); - try { - const img: HTMLImageElement = document.querySelector( - '.pswp__img:not(.pswp__img--placeholder)' - ); - if (img) { - const exifData = await exifr.parse(img); - if (!exifData) { - return; - } - exifData.raw = prettyPrintExif(exifData); - setExif(exifData); - } - } catch (e) { - logError(e, 'exifr parsing failed'); - } - }; - - function updateInfo() { - const file: EnteFile = this?.currItem; - if (file?.metadata) { - setMetaData(file.metadata); - setExif(null); - checkExifAvailable(); - } - } - - const handleCloseInfo = () => { - setShowInfo(false); - }; - const handleOpenInfo = () => { - setShowInfo(true); - }; - - const downloadFileHelper = async (file) => { - appContext.startLoading(); - await downloadFile( - file, - publicCollectionGalleryContext.accessedThroughSharedURL, - publicCollectionGalleryContext.token, - publicCollectionGalleryContext.passwordToken - ); - - appContext.finishLoading(); - }; - const scheduleUpdate = () => (needUpdate.current = true); - const { id } = props; - let { className } = props; - className = classnames(['pswp', className]).trim(); - return ( - <> -