fix build
This commit is contained in:
parent
3e339eee84
commit
8c6991df90
15 changed files with 2126 additions and 2045 deletions
|
@ -58,9 +58,9 @@
|
|||
"libsodium-wrappers": "^0.7.8",
|
||||
"localforage": "^1.9.0",
|
||||
"ml-matrix": "^6.8.2",
|
||||
"p-queue": "^7.1.0",
|
||||
"next": "^12.1.0",
|
||||
"next-transpile-modules": "^9.0.0",
|
||||
"p-queue": "^7.1.0",
|
||||
"photoswipe": "file:./thirdparty/photoswipe",
|
||||
"piexifjs": "^1.0.6",
|
||||
"react": "^17.0.2",
|
||||
|
@ -77,10 +77,10 @@
|
|||
"react-window": "^1.8.6",
|
||||
"scrypt-js": "^3.0.1",
|
||||
"similarity-transformation": "^0.0.1",
|
||||
"styled-components": "^5.3.5",
|
||||
"tesseract.js": "file:./thirdparty/tesseract",
|
||||
"transformation-matrix": "^2.10.0",
|
||||
"tsne-js": "^1.0.3",
|
||||
"styled-components": "^5.3.5",
|
||||
"workbox-precaching": "^6.1.5",
|
||||
"workbox-recipes": "^6.1.5",
|
||||
"workbox-routing": "^6.1.5",
|
||||
|
@ -101,8 +101,8 @@
|
|||
"@types/react-select": "^4.0.15",
|
||||
"@types/react-window": "^1.8.2",
|
||||
"@types/react-window-infinite-loader": "^1.0.3",
|
||||
"@types/wicg-file-system-access": "^2020.9.5",
|
||||
"@types/styled-components": "^5.1.25",
|
||||
"@types/wicg-file-system-access": "^2020.9.5",
|
||||
"@types/yup": "^0.29.7",
|
||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
||||
"@typescript-eslint/parser": "^4.25.0",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, useEffect, useContext, ChangeEvent } from 'react';
|
||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import { useRouter } from 'next/router';
|
||||
import { ComlinkWorker } from 'utils/crypto';
|
||||
import { ComlinkWorker } from 'utils/comlink';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { PAGES } from 'constants/pages';
|
||||
import * as Comlink from 'comlink';
|
||||
|
|
832
src/components/PhotoSwipe/PhotoSwipe-old.tsx
Normal file
832
src/components/PhotoSwipe/PhotoSwipe-old.tsx
Normal file
|
@ -0,0 +1,832 @@
|
|||
export {};
|
||||
// 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 { updatePublicMagicMetadata } 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 { logError } from 'utils/sentry';
|
||||
|
||||
// import CloseIcon from 'components/icons/CloseIcon';
|
||||
// import TickIcon from 'components/icons/TickIcon';
|
||||
// import {
|
||||
// PhotoPeopleList,
|
||||
// UnidentifiedFaces,
|
||||
// } from 'components/MachineLearning/PeopleList';
|
||||
// import { Formik } from 'formik';
|
||||
// import * as Yup from 'yup';
|
||||
// import EnteSpinner from 'components/EnteSpinner';
|
||||
// import EnteDateTimePicker from 'components/EnteDateTimePicker';
|
||||
// // import { AppContext } from 'pages/_app';
|
||||
|
||||
// import { MAX_EDITED_FILE_NAME_LENGTH } from 'constants/file';
|
||||
// import { sleep } from 'utils/common';
|
||||
// import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
|
||||
// import { GalleryContext } from 'pages/gallery';
|
||||
// import { ObjectLabelList } from 'components/MachineLearning/ObjectList';
|
||||
// import { WordList } from 'components/MachineLearning/WordList';
|
||||
// import MLServiceFileInfoButton from 'components/MachineLearning/MLServiceFileInfoButton';
|
||||
|
||||
// const SmallLoadingSpinner = () => (
|
||||
// <EnteSpinner
|
||||
// style={{
|
||||
// width: '20px',
|
||||
// height: '20px',
|
||||
// }}
|
||||
// />
|
||||
// );
|
||||
// interface Iprops {
|
||||
// isOpen: boolean;
|
||||
// items: EnteFile[];
|
||||
// currentIndex?: number;
|
||||
// onClose?: (needUpdate: boolean) => void;
|
||||
// gettingData: (instance: any, index: number, item: EnteFile) => void;
|
||||
// id?: string;
|
||||
// className?: string;
|
||||
// favItemIds: Set<number>;
|
||||
// isSharedCollection: boolean;
|
||||
// isTrashCollection: 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 renderInfoItem = (label: string, value: string | JSX.Element) => (
|
||||
// <Row>
|
||||
// <Label width="30%">{label}</Label>
|
||||
// <Value width="70%">{value}</Value>
|
||||
// </Row>
|
||||
// );
|
||||
|
||||
// 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 updatePublicMagicMetadata([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 (
|
||||
// <>
|
||||
// <Row>
|
||||
// <Label width="30%">{constants.CREATION_TIME}</Label>
|
||||
// <Value width={isInEditMode ? '50%' : '60%'}>
|
||||
// {isInEditMode ? (
|
||||
// <EnteDateTimePicker
|
||||
// loading={loading}
|
||||
// isInEditMode={isInEditMode}
|
||||
// pickedTime={pickedTime}
|
||||
// handleChange={handleChange}
|
||||
// />
|
||||
// ) : (
|
||||
// formatDateTime(pickedTime)
|
||||
// )}
|
||||
// </Value>
|
||||
// <Value
|
||||
// width={isInEditMode ? '20%' : '10%'}
|
||||
// style={{ cursor: 'pointer', marginLeft: '10px' }}>
|
||||
// {!shouldDisableEdits &&
|
||||
// (!isInEditMode ? (
|
||||
// <IconButton onClick={openEditMode}>
|
||||
// <EditIcon />
|
||||
// </IconButton>
|
||||
// ) : (
|
||||
// <>
|
||||
// <IconButton onClick={saveEdits}>
|
||||
// {loading ? (
|
||||
// <SmallLoadingSpinner />
|
||||
// ) : (
|
||||
// <TickIcon />
|
||||
// )}
|
||||
// </IconButton>
|
||||
// <IconButton onClick={discardEdits}>
|
||||
// <CloseIcon />
|
||||
// </IconButton>
|
||||
// </>
|
||||
// ))}
|
||||
// </Value>
|
||||
// </Row>
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
// 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 (
|
||||
// <Formik<formValues>
|
||||
// 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 }) => (
|
||||
// <Form noValidate onSubmit={handleSubmit}>
|
||||
// <Form.Row>
|
||||
// <Form.Group
|
||||
// bsPrefix="ente-form-group"
|
||||
// as={Col}
|
||||
// xs={extension ? 7 : 8}>
|
||||
// <Form.Control
|
||||
// as="textarea"
|
||||
// placeholder={constants.FILE_NAME}
|
||||
// value={values.filename}
|
||||
// onChange={handleChange('filename')}
|
||||
// isInvalid={Boolean(errors.filename)}
|
||||
// autoFocus
|
||||
// disabled={loading}
|
||||
// />
|
||||
// <FormControl.Feedback
|
||||
// type="invalid"
|
||||
// style={{ textAlign: 'center' }}>
|
||||
// {errors.filename}
|
||||
// </FormControl.Feedback>
|
||||
// </Form.Group>
|
||||
// {extension && (
|
||||
// <Form.Group
|
||||
// bsPrefix="ente-form-group"
|
||||
// as={Col}
|
||||
// xs={1}
|
||||
// controlId="formHorizontalFileName">
|
||||
// <FlexWrapper style={{ padding: '5px' }}>
|
||||
// {`.${extension}`}
|
||||
// </FlexWrapper>
|
||||
// </Form.Group>
|
||||
// )}
|
||||
// <Form.Group bsPrefix="ente-form-group" as={Col} xs={2}>
|
||||
// <Value width={'16.67%'}>
|
||||
// <IconButton type="submit" disabled={loading}>
|
||||
// {loading ? (
|
||||
// <SmallLoadingSpinner />
|
||||
// ) : (
|
||||
// <TickIcon />
|
||||
// )}
|
||||
// </IconButton>
|
||||
// <IconButton
|
||||
// onClick={discardEdits}
|
||||
// disabled={loading}>
|
||||
// <CloseIcon />
|
||||
// </IconButton>
|
||||
// </Value>
|
||||
// </Form.Group>
|
||||
// </Form.Row>
|
||||
// </Form>
|
||||
// )}
|
||||
// </Formik>
|
||||
// );
|
||||
// };
|
||||
|
||||
// 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 updatePublicMagicMetadata([updatedFile])
|
||||
// )[0];
|
||||
// updateExistingFilePubMetadata(file, updatedFile);
|
||||
// scheduleUpdate();
|
||||
// }
|
||||
// } catch (e) {
|
||||
// logError(e, 'failed to update file name');
|
||||
// } finally {
|
||||
// closeEditMode();
|
||||
// }
|
||||
// };
|
||||
// return (
|
||||
// <>
|
||||
// <Row>
|
||||
// <Label width="30%">{constants.FILE_NAME}</Label>
|
||||
// {!isInEditMode ? (
|
||||
// <>
|
||||
// <Value width="60%">
|
||||
// <FreeFlowText>
|
||||
// {getFileTitle(filename, extension)}
|
||||
// </FreeFlowText>
|
||||
// </Value>
|
||||
// {!shouldDisableEdits && (
|
||||
// <Value
|
||||
// width="10%"
|
||||
// style={{
|
||||
// cursor: 'pointer',
|
||||
// marginLeft: '10px',
|
||||
// }}>
|
||||
// <IconButton onClick={openEditMode}>
|
||||
// <EditIcon />
|
||||
// </IconButton>
|
||||
// </Value>
|
||||
// )}
|
||||
// </>
|
||||
// ) : (
|
||||
// <FileNameEditForm
|
||||
// extension={extension}
|
||||
// filename={filename}
|
||||
// saveEdits={saveEdits}
|
||||
// discardEdits={closeEditMode}
|
||||
// />
|
||||
// )}
|
||||
// </Row>
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
// function ExifData(props: { exif: any }) {
|
||||
// const { exif } = props;
|
||||
// const [showAll, setShowAll] = useState(false);
|
||||
|
||||
// const changeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
// setShowAll(e.target.checked);
|
||||
// };
|
||||
|
||||
// const renderAllValues = () => <Pre>{exif.raw}</Pre>;
|
||||
|
||||
// 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 (
|
||||
// <>
|
||||
// <LegendContainer>
|
||||
// <Legend>{constants.EXIF}</Legend>
|
||||
// <FormCheck>
|
||||
// <FormCheck.Label>
|
||||
// <FormCheck.Input onChange={changeHandler} />
|
||||
// {constants.SHOW_ALL}
|
||||
// </FormCheck.Label>
|
||||
// </FormCheck>
|
||||
// </LegendContainer>
|
||||
// {showAll ? renderAllValues() : renderSelectedValues()}
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
|
||||
// function InfoModal({
|
||||
// shouldDisableEdits,
|
||||
// showInfo,
|
||||
// handleCloseInfo,
|
||||
// items,
|
||||
// photoSwipe,
|
||||
// metadata,
|
||||
// exif,
|
||||
// scheduleUpdate,
|
||||
// }) {
|
||||
// // const appContext = useContext(AppContext);
|
||||
// const [updateMLDataIndex, setUpdateMLDataIndex] = useState(0);
|
||||
|
||||
// return (
|
||||
// <Modal show={showInfo} onHide={handleCloseInfo}>
|
||||
// <Modal.Header closeButton>
|
||||
// <Modal.Title>{constants.INFO}</Modal.Title>
|
||||
// </Modal.Header>
|
||||
// <Modal.Body>
|
||||
// <div>
|
||||
// <Legend>{constants.METADATA}</Legend>
|
||||
// </div>
|
||||
// {renderInfoItem(
|
||||
// constants.FILE_ID,
|
||||
// items[photoSwipe?.getCurrentIndex()]?.id
|
||||
// )}
|
||||
// {metadata?.title && (
|
||||
// <RenderFileName
|
||||
// shouldDisableEdits={shouldDisableEdits}
|
||||
// file={items[photoSwipe?.getCurrentIndex()]}
|
||||
// scheduleUpdate={scheduleUpdate}
|
||||
// />
|
||||
// )}
|
||||
// {metadata?.creationTime && (
|
||||
// <RenderCreationTime
|
||||
// shouldDisableEdits={shouldDisableEdits}
|
||||
// file={items[photoSwipe?.getCurrentIndex()]}
|
||||
// scheduleUpdate={scheduleUpdate}
|
||||
// />
|
||||
// )}
|
||||
// {metadata?.modificationTime &&
|
||||
// renderInfoItem(
|
||||
// constants.UPDATED_ON,
|
||||
// formatDateTime(metadata.modificationTime / 1000)
|
||||
// )}
|
||||
// {metadata?.longitude > 0 &&
|
||||
// metadata?.longitude > 0 &&
|
||||
// renderInfoItem(
|
||||
// constants.LOCATION,
|
||||
// <a
|
||||
// href={`https://www.openstreetmap.org/?mlat=${metadata.latitude}&mlon=${metadata.longitude}#map=15/${metadata.latitude}/${metadata.longitude}`}
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer">
|
||||
// {constants.SHOW_MAP}
|
||||
// </a>
|
||||
// )}
|
||||
// {/* {appContext.mlSearchEnabled && ( */}
|
||||
// <>
|
||||
// <div>
|
||||
// <Legend>{constants.PEOPLE}</Legend>
|
||||
// </div>
|
||||
// <PhotoPeopleList
|
||||
// file={items[photoSwipe?.getCurrentIndex()]}
|
||||
// updateMLDataIndex={updateMLDataIndex}
|
||||
// />
|
||||
// <div>
|
||||
// <Legend>{constants.UNIDENTIFIED_FACES}</Legend>
|
||||
// </div>
|
||||
// <UnidentifiedFaces
|
||||
// file={items[photoSwipe?.getCurrentIndex()]}
|
||||
// updateMLDataIndex={updateMLDataIndex}
|
||||
// />
|
||||
// <div>
|
||||
// <Legend>{constants.OBJECTS}</Legend>
|
||||
// <ObjectLabelList
|
||||
// file={items[photoSwipe?.getCurrentIndex()]}
|
||||
// updateMLDataIndex={updateMLDataIndex}
|
||||
// />
|
||||
// </div>
|
||||
// <div>
|
||||
// <Legend>{constants.TEXT}</Legend>
|
||||
// <WordList
|
||||
// file={items[photoSwipe?.getCurrentIndex()]}
|
||||
// updateMLDataIndex={updateMLDataIndex}
|
||||
// />
|
||||
// </div>
|
||||
// <MLServiceFileInfoButton
|
||||
// file={items[photoSwipe?.getCurrentIndex()]}
|
||||
// updateMLDataIndex={updateMLDataIndex}
|
||||
// setUpdateMLDataIndex={setUpdateMLDataIndex}
|
||||
// />
|
||||
// </>
|
||||
// {/* )} */}
|
||||
// {exif && (
|
||||
// <>
|
||||
// <ExifData exif={exif} />
|
||||
// </>
|
||||
// )}
|
||||
// </Modal.Body>
|
||||
// <Modal.Footer>
|
||||
// <Button variant="outline-secondary" onClick={handleCloseInfo}>
|
||||
// {constants.CLOSE}
|
||||
// </Button>
|
||||
// </Modal.Footer>
|
||||
// </Modal>
|
||||
// );
|
||||
// }
|
||||
|
||||
// function PhotoSwipe(props: Iprops) {
|
||||
// const pswpElement = useRef<HTMLDivElement>();
|
||||
// const [photoSwipe, setPhotoSwipe] = useState<Photoswipe<any>>();
|
||||
|
||||
// const { isOpen, items } = props;
|
||||
// const [isFav, setIsFav] = useState(false);
|
||||
// const [showInfo, setShowInfo] = useState(false);
|
||||
// const [metadata, setMetaData] = useState<EnteFile['metadata']>(null);
|
||||
// const [exif, setExif] = useState<any>(null);
|
||||
// const needUpdate = useRef(false);
|
||||
// const publicCollectionGalleryContext = useContext(
|
||||
// PublicCollectionGalleryContext
|
||||
// );
|
||||
// const galleryContext = useContext(GalleryContext);
|
||||
|
||||
// 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]);
|
||||
|
||||
// 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();
|
||||
// };
|
||||
// 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) => {
|
||||
// galleryContext.startLoading();
|
||||
// await downloadFile(
|
||||
// file,
|
||||
// publicCollectionGalleryContext.accessedThroughSharedURL,
|
||||
// publicCollectionGalleryContext.token
|
||||
// );
|
||||
|
||||
// galleryContext.finishLoading();
|
||||
// };
|
||||
// const scheduleUpdate = () => (needUpdate.current = true);
|
||||
// const { id } = props;
|
||||
// let { className } = props;
|
||||
// className = classnames(['pswp', className]).trim();
|
||||
// return (
|
||||
// <>
|
||||
// <div
|
||||
// id={id}
|
||||
// className={className}
|
||||
// tabIndex={Number('-1')}
|
||||
// role="dialog"
|
||||
// aria-hidden="true"
|
||||
// ref={pswpElement}>
|
||||
// <div className="pswp__bg" />
|
||||
// <div className="pswp__scroll-wrap">
|
||||
// <div className="pswp__container">
|
||||
// <div className="pswp__item" />
|
||||
// <div className="pswp__item" />
|
||||
// <div className="pswp__item" />
|
||||
// </div>
|
||||
// <div className="pswp__ui pswp__ui--hidden">
|
||||
// <div className="pswp__top-bar">
|
||||
// <div className="pswp__counter" />
|
||||
|
||||
// <button
|
||||
// className="pswp__button pswp__button--close"
|
||||
// title={constants.CLOSE}
|
||||
// />
|
||||
|
||||
// <button
|
||||
// className="pswp-custom download-btn"
|
||||
// title={constants.DOWNLOAD}
|
||||
// onClick={() =>
|
||||
// downloadFileHelper(photoSwipe.currItem)
|
||||
// }
|
||||
// />
|
||||
|
||||
// <button
|
||||
// className="pswp__button pswp__button--fs"
|
||||
// title={constants.TOGGLE_FULLSCREEN}
|
||||
// />
|
||||
// <button
|
||||
// className="pswp__button pswp__button--zoom"
|
||||
// title={constants.ZOOM_IN_OUT}
|
||||
// />
|
||||
// {!props.isSharedCollection &&
|
||||
// !props.isTrashCollection && (
|
||||
// <FavButton
|
||||
// size={44}
|
||||
// isClick={isFav}
|
||||
// onClick={() => {
|
||||
// onFavClick(photoSwipe?.currItem);
|
||||
// }}
|
||||
// />
|
||||
// )}
|
||||
// <button
|
||||
// className="pswp-custom info-btn"
|
||||
// title={constants.INFO}
|
||||
// onClick={handleOpenInfo}
|
||||
// />
|
||||
// <div className="pswp__preloader">
|
||||
// <div className="pswp__preloader__icn">
|
||||
// <div className="pswp__preloader__cut">
|
||||
// <div className="pswp__preloader__donut" />
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div className="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
|
||||
// <div className="pswp__share-tooltip" />
|
||||
// </div>
|
||||
// <button
|
||||
// className="pswp__button pswp__button--arrow--left"
|
||||
// title={constants.PREVIOUS}
|
||||
// />
|
||||
// <button
|
||||
// className="pswp__button pswp__button--arrow--right"
|
||||
// title={constants.NEXT}
|
||||
// />
|
||||
// <div className="pswp__caption">
|
||||
// <div />
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
// <InfoModal
|
||||
// shouldDisableEdits={props.isSharedCollection}
|
||||
// showInfo={showInfo}
|
||||
// handleCloseInfo={handleCloseInfo}
|
||||
// items={items}
|
||||
// photoSwipe={photoSwipe}
|
||||
// metadata={metadata}
|
||||
// exif={exif}
|
||||
// scheduleUpdate={scheduleUpdate}
|
||||
// />
|
||||
// </>
|
||||
// );
|
||||
// }
|
||||
|
||||
// export default PhotoSwipe;
|
|
@ -1,831 +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 { updatePublicMagicMetadata } 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 { logError } from 'utils/sentry';
|
||||
|
||||
import CloseIcon from 'components/icons/CloseIcon';
|
||||
import TickIcon from 'components/icons/TickIcon';
|
||||
import {
|
||||
PhotoPeopleList,
|
||||
UnidentifiedFaces,
|
||||
} from 'components/MachineLearning/PeopleList';
|
||||
import { Formik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import EnteSpinner from 'components/EnteSpinner';
|
||||
import EnteDateTimePicker from 'components/EnteDateTimePicker';
|
||||
// import { AppContext } from 'pages/_app';
|
||||
|
||||
import { MAX_EDITED_FILE_NAME_LENGTH } from 'constants/file';
|
||||
import { sleep } from 'utils/common';
|
||||
import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import { ObjectLabelList } from 'components/MachineLearning/ObjectList';
|
||||
import { WordList } from 'components/MachineLearning/WordList';
|
||||
import MLServiceFileInfoButton from 'components/MachineLearning/MLServiceFileInfoButton';
|
||||
|
||||
const SmallLoadingSpinner = () => (
|
||||
<EnteSpinner
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
interface Iprops {
|
||||
isOpen: boolean;
|
||||
items: EnteFile[];
|
||||
currentIndex?: number;
|
||||
onClose?: (needUpdate: boolean) => void;
|
||||
gettingData: (instance: any, index: number, item: EnteFile) => void;
|
||||
id?: string;
|
||||
className?: string;
|
||||
favItemIds: Set<number>;
|
||||
isSharedCollection: boolean;
|
||||
isTrashCollection: 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 renderInfoItem = (label: string, value: string | JSX.Element) => (
|
||||
<Row>
|
||||
<Label width="30%">{label}</Label>
|
||||
<Value width="70%">{value}</Value>
|
||||
</Row>
|
||||
);
|
||||
|
||||
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 updatePublicMagicMetadata([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 (
|
||||
<>
|
||||
<Row>
|
||||
<Label width="30%">{constants.CREATION_TIME}</Label>
|
||||
<Value width={isInEditMode ? '50%' : '60%'}>
|
||||
{isInEditMode ? (
|
||||
<EnteDateTimePicker
|
||||
loading={loading}
|
||||
isInEditMode={isInEditMode}
|
||||
pickedTime={pickedTime}
|
||||
handleChange={handleChange}
|
||||
/>
|
||||
) : (
|
||||
formatDateTime(pickedTime)
|
||||
)}
|
||||
</Value>
|
||||
<Value
|
||||
width={isInEditMode ? '20%' : '10%'}
|
||||
style={{ cursor: 'pointer', marginLeft: '10px' }}>
|
||||
{!shouldDisableEdits &&
|
||||
(!isInEditMode ? (
|
||||
<IconButton onClick={openEditMode}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<>
|
||||
<IconButton onClick={saveEdits}>
|
||||
{loading ? (
|
||||
<SmallLoadingSpinner />
|
||||
) : (
|
||||
<TickIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton onClick={discardEdits}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</>
|
||||
))}
|
||||
</Value>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
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 (
|
||||
<Formik<formValues>
|
||||
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 }) => (
|
||||
<Form noValidate onSubmit={handleSubmit}>
|
||||
<Form.Row>
|
||||
<Form.Group
|
||||
bsPrefix="ente-form-group"
|
||||
as={Col}
|
||||
xs={extension ? 7 : 8}>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
placeholder={constants.FILE_NAME}
|
||||
value={values.filename}
|
||||
onChange={handleChange('filename')}
|
||||
isInvalid={Boolean(errors.filename)}
|
||||
autoFocus
|
||||
disabled={loading}
|
||||
/>
|
||||
<FormControl.Feedback
|
||||
type="invalid"
|
||||
style={{ textAlign: 'center' }}>
|
||||
{errors.filename}
|
||||
</FormControl.Feedback>
|
||||
</Form.Group>
|
||||
{extension && (
|
||||
<Form.Group
|
||||
bsPrefix="ente-form-group"
|
||||
as={Col}
|
||||
xs={1}
|
||||
controlId="formHorizontalFileName">
|
||||
<FlexWrapper style={{ padding: '5px' }}>
|
||||
{`.${extension}`}
|
||||
</FlexWrapper>
|
||||
</Form.Group>
|
||||
)}
|
||||
<Form.Group bsPrefix="ente-form-group" as={Col} xs={2}>
|
||||
<Value width={'16.67%'}>
|
||||
<IconButton type="submit" disabled={loading}>
|
||||
{loading ? (
|
||||
<SmallLoadingSpinner />
|
||||
) : (
|
||||
<TickIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={discardEdits}
|
||||
disabled={loading}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Value>
|
||||
</Form.Group>
|
||||
</Form.Row>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
};
|
||||
|
||||
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 updatePublicMagicMetadata([updatedFile])
|
||||
)[0];
|
||||
updateExistingFilePubMetadata(file, updatedFile);
|
||||
scheduleUpdate();
|
||||
}
|
||||
} catch (e) {
|
||||
logError(e, 'failed to update file name');
|
||||
} finally {
|
||||
closeEditMode();
|
||||
}
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
<Label width="30%">{constants.FILE_NAME}</Label>
|
||||
{!isInEditMode ? (
|
||||
<>
|
||||
<Value width="60%">
|
||||
<FreeFlowText>
|
||||
{getFileTitle(filename, extension)}
|
||||
</FreeFlowText>
|
||||
</Value>
|
||||
{!shouldDisableEdits && (
|
||||
<Value
|
||||
width="10%"
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
marginLeft: '10px',
|
||||
}}>
|
||||
<IconButton onClick={openEditMode}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</Value>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<FileNameEditForm
|
||||
extension={extension}
|
||||
filename={filename}
|
||||
saveEdits={saveEdits}
|
||||
discardEdits={closeEditMode}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
}
|
||||
function ExifData(props: { exif: any }) {
|
||||
const { exif } = props;
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
|
||||
const changeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setShowAll(e.target.checked);
|
||||
};
|
||||
|
||||
const renderAllValues = () => <Pre>{exif.raw}</Pre>;
|
||||
|
||||
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 (
|
||||
<>
|
||||
<LegendContainer>
|
||||
<Legend>{constants.EXIF}</Legend>
|
||||
<FormCheck>
|
||||
<FormCheck.Label>
|
||||
<FormCheck.Input onChange={changeHandler} />
|
||||
{constants.SHOW_ALL}
|
||||
</FormCheck.Label>
|
||||
</FormCheck>
|
||||
</LegendContainer>
|
||||
{showAll ? renderAllValues() : renderSelectedValues()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoModal({
|
||||
shouldDisableEdits,
|
||||
showInfo,
|
||||
handleCloseInfo,
|
||||
items,
|
||||
photoSwipe,
|
||||
metadata,
|
||||
exif,
|
||||
scheduleUpdate,
|
||||
}) {
|
||||
// const appContext = useContext(AppContext);
|
||||
const [updateMLDataIndex, setUpdateMLDataIndex] = useState(0);
|
||||
|
||||
return (
|
||||
<Modal show={showInfo} onHide={handleCloseInfo}>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Title>{constants.INFO}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body>
|
||||
<div>
|
||||
<Legend>{constants.METADATA}</Legend>
|
||||
</div>
|
||||
{renderInfoItem(
|
||||
constants.FILE_ID,
|
||||
items[photoSwipe?.getCurrentIndex()]?.id
|
||||
)}
|
||||
{metadata?.title && (
|
||||
<RenderFileName
|
||||
shouldDisableEdits={shouldDisableEdits}
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
scheduleUpdate={scheduleUpdate}
|
||||
/>
|
||||
)}
|
||||
{metadata?.creationTime && (
|
||||
<RenderCreationTime
|
||||
shouldDisableEdits={shouldDisableEdits}
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
scheduleUpdate={scheduleUpdate}
|
||||
/>
|
||||
)}
|
||||
{metadata?.modificationTime &&
|
||||
renderInfoItem(
|
||||
constants.UPDATED_ON,
|
||||
formatDateTime(metadata.modificationTime / 1000)
|
||||
)}
|
||||
{metadata?.longitude > 0 &&
|
||||
metadata?.longitude > 0 &&
|
||||
renderInfoItem(
|
||||
constants.LOCATION,
|
||||
<a
|
||||
href={`https://www.openstreetmap.org/?mlat=${metadata.latitude}&mlon=${metadata.longitude}#map=15/${metadata.latitude}/${metadata.longitude}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
{constants.SHOW_MAP}
|
||||
</a>
|
||||
)}
|
||||
{/* {appContext.mlSearchEnabled && ( */}
|
||||
<>
|
||||
<div>
|
||||
<Legend>{constants.PEOPLE}</Legend>
|
||||
</div>
|
||||
<PhotoPeopleList
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
updateMLDataIndex={updateMLDataIndex}
|
||||
/>
|
||||
<div>
|
||||
<Legend>{constants.UNIDENTIFIED_FACES}</Legend>
|
||||
</div>
|
||||
<UnidentifiedFaces
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
updateMLDataIndex={updateMLDataIndex}
|
||||
/>
|
||||
<div>
|
||||
<Legend>{constants.OBJECTS}</Legend>
|
||||
<ObjectLabelList
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
updateMLDataIndex={updateMLDataIndex}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Legend>{constants.TEXT}</Legend>
|
||||
<WordList
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
updateMLDataIndex={updateMLDataIndex}
|
||||
/>
|
||||
</div>
|
||||
<MLServiceFileInfoButton
|
||||
file={items[photoSwipe?.getCurrentIndex()]}
|
||||
updateMLDataIndex={updateMLDataIndex}
|
||||
setUpdateMLDataIndex={setUpdateMLDataIndex}
|
||||
/>
|
||||
</>
|
||||
{/* )} */}
|
||||
{exif && (
|
||||
<>
|
||||
<ExifData exif={exif} />
|
||||
</>
|
||||
)}
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="outline-secondary" onClick={handleCloseInfo}>
|
||||
{constants.CLOSE}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function PhotoSwipe(props: Iprops) {
|
||||
const pswpElement = useRef<HTMLDivElement>();
|
||||
const [photoSwipe, setPhotoSwipe] = useState<Photoswipe<any>>();
|
||||
|
||||
const { isOpen, items } = props;
|
||||
const [isFav, setIsFav] = useState(false);
|
||||
const [showInfo, setShowInfo] = useState(false);
|
||||
const [metadata, setMetaData] = useState<EnteFile['metadata']>(null);
|
||||
const [exif, setExif] = useState<any>(null);
|
||||
const needUpdate = useRef(false);
|
||||
const publicCollectionGalleryContext = useContext(
|
||||
PublicCollectionGalleryContext
|
||||
);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
|
||||
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]);
|
||||
|
||||
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();
|
||||
};
|
||||
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) => {
|
||||
galleryContext.startLoading();
|
||||
await downloadFile(
|
||||
file,
|
||||
publicCollectionGalleryContext.accessedThroughSharedURL,
|
||||
publicCollectionGalleryContext.token
|
||||
);
|
||||
|
||||
galleryContext.finishLoading();
|
||||
};
|
||||
const scheduleUpdate = () => (needUpdate.current = true);
|
||||
const { id } = props;
|
||||
let { className } = props;
|
||||
className = classnames(['pswp', className]).trim();
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
id={id}
|
||||
className={className}
|
||||
tabIndex={Number('-1')}
|
||||
role="dialog"
|
||||
aria-hidden="true"
|
||||
ref={pswpElement}>
|
||||
<div className="pswp__bg" />
|
||||
<div className="pswp__scroll-wrap">
|
||||
<div className="pswp__container">
|
||||
<div className="pswp__item" />
|
||||
<div className="pswp__item" />
|
||||
<div className="pswp__item" />
|
||||
</div>
|
||||
<div className="pswp__ui pswp__ui--hidden">
|
||||
<div className="pswp__top-bar">
|
||||
<div className="pswp__counter" />
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--close"
|
||||
title={constants.CLOSE}
|
||||
/>
|
||||
|
||||
<button
|
||||
className="pswp-custom download-btn"
|
||||
title={constants.DOWNLOAD}
|
||||
onClick={() =>
|
||||
downloadFileHelper(photoSwipe.currItem)
|
||||
}
|
||||
/>
|
||||
|
||||
<button
|
||||
className="pswp__button pswp__button--fs"
|
||||
title={constants.TOGGLE_FULLSCREEN}
|
||||
/>
|
||||
<button
|
||||
className="pswp__button pswp__button--zoom"
|
||||
title={constants.ZOOM_IN_OUT}
|
||||
/>
|
||||
{!props.isSharedCollection &&
|
||||
!props.isTrashCollection && (
|
||||
<FavButton
|
||||
size={44}
|
||||
isClick={isFav}
|
||||
onClick={() => {
|
||||
onFavClick(photoSwipe?.currItem);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
className="pswp-custom info-btn"
|
||||
title={constants.INFO}
|
||||
onClick={handleOpenInfo}
|
||||
/>
|
||||
<div className="pswp__preloader">
|
||||
<div className="pswp__preloader__icn">
|
||||
<div className="pswp__preloader__cut">
|
||||
<div className="pswp__preloader__donut" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
|
||||
<div className="pswp__share-tooltip" />
|
||||
</div>
|
||||
<button
|
||||
className="pswp__button pswp__button--arrow--left"
|
||||
title={constants.PREVIOUS}
|
||||
/>
|
||||
<button
|
||||
className="pswp__button pswp__button--arrow--right"
|
||||
title={constants.NEXT}
|
||||
/>
|
||||
<div className="pswp__caption">
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<InfoModal
|
||||
shouldDisableEdits={props.isSharedCollection}
|
||||
showInfo={showInfo}
|
||||
handleCloseInfo={handleCloseInfo}
|
||||
items={items}
|
||||
photoSwipe={photoSwipe}
|
||||
metadata={metadata}
|
||||
exif={exif}
|
||||
scheduleUpdate={scheduleUpdate}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default PhotoSwipe;
|
496
src/components/SearchBar-old.tsx
Normal file
496
src/components/SearchBar-old.tsx
Normal file
|
@ -0,0 +1,496 @@
|
|||
export {};
|
||||
// import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
// import styled from 'styled-components';
|
||||
// import AsyncSelect from 'react-select/async';
|
||||
// import { components } from 'react-select';
|
||||
// import debounce from 'debounce-promise';
|
||||
// import {
|
||||
// getAllPeopleSuggestion,
|
||||
// getHolidaySuggestion,
|
||||
// getIndexStatusSuggestion,
|
||||
// getYearSuggestion,
|
||||
// parseHumanDate,
|
||||
// searchCollection,
|
||||
// searchFiles,
|
||||
// searchLocation,
|
||||
// searchText,
|
||||
// searchThing,
|
||||
// } from 'services/searchService';
|
||||
// import { getFormattedDate, isInsideBox } from 'utils/search';
|
||||
// import constants from 'utils/strings/constants';
|
||||
// import LocationIcon from './icons/LocationIcon';
|
||||
// import DateIcon from './icons/DateIcon';
|
||||
// import SearchIcon from './icons/SearchIcon';
|
||||
// import CloseIcon from './icons/CloseIcon';
|
||||
// import { Collection } from 'types/collection';
|
||||
// import CollectionIcon from './icons/CollectionIcon';
|
||||
|
||||
// import ImageIcon from './icons/ImageIcon';
|
||||
// import VideoIcon from './icons/VideoIcon';
|
||||
// import { IconButton, Row } from './Container';
|
||||
// import { EnteFile } from 'types/file';
|
||||
// import { Suggestion, SuggestionType, DateValue, Bbox } from 'types/search';
|
||||
// import { Search, SearchStats } from 'types/gallery';
|
||||
// import { FILE_TYPE } from 'constants/file';
|
||||
// import { GalleryContext } from 'pages/gallery';
|
||||
// import { AppContext } from 'pages/_app';
|
||||
// import { Col } from 'react-bootstrap';
|
||||
// import { Person, ThingClass, WordGroup } from 'types/machineLearning';
|
||||
// import { IndexStatus } from 'types/machineLearning/ui';
|
||||
// import { PeopleList } from './MachineLearning/PeopleList';
|
||||
// import ObjectIcon from './icons/ObjectIcon';
|
||||
// import TextIcon from './icons/TextIcon';
|
||||
|
||||
// const Wrapper = styled.div<{ isDisabled: boolean; isOpen: boolean }>`
|
||||
// position: fixed;
|
||||
// top: 0;
|
||||
// z-index: 1000;
|
||||
// display: ${({ isOpen }) => (isOpen ? 'flex' : 'none')};
|
||||
// width: 100%;
|
||||
// background: #111;
|
||||
// @media (min-width: 625px) {
|
||||
// display: flex;
|
||||
// width: calc(100vw - 140px);
|
||||
// margin: 0 70px;
|
||||
// }
|
||||
// align-items: center;
|
||||
// min-height: 64px;
|
||||
// transition: opacity 1s ease;
|
||||
// opacity: ${(props) => (props.isDisabled ? 0 : 1)};
|
||||
// margin-bottom: 10px;
|
||||
// `;
|
||||
|
||||
// const SearchButton = styled.div<{ isOpen: boolean }>`
|
||||
// display: none;
|
||||
// @media (max-width: 624px) {
|
||||
// display: ${({ isOpen }) => (!isOpen ? 'flex' : 'none')};
|
||||
// right: 80px;
|
||||
// cursor: pointer;
|
||||
// position: fixed;
|
||||
// top: 0;
|
||||
// z-index: 1000;
|
||||
// align-items: center;
|
||||
// min-height: 64px;
|
||||
// }
|
||||
// `;
|
||||
|
||||
// const SearchStatsContainer = styled.div`
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// color: #979797;
|
||||
// margin-bottom: 8px;
|
||||
// `;
|
||||
|
||||
// const SearchInput = styled.div`
|
||||
// width: 100%;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// max-width: 484px;
|
||||
// margin: auto;
|
||||
// `;
|
||||
|
||||
// const Legend = styled.span`
|
||||
// font-size: 20px;
|
||||
// color: #ddd;
|
||||
// display: inline;
|
||||
// padding: 8px 12px;
|
||||
// `;
|
||||
|
||||
// const Caption = styled.span`
|
||||
// font-size: 12px;
|
||||
// display: inline;
|
||||
// padding: 8px 12px;
|
||||
// `;
|
||||
|
||||
// const LegendRow = styled(Row)`
|
||||
// align-items: center;
|
||||
// justify-content: space-between;
|
||||
// margin-bottom: 0px;
|
||||
// `;
|
||||
|
||||
// interface Props {
|
||||
// isOpen: boolean;
|
||||
// isFirstFetch: boolean;
|
||||
// setOpen: (value: boolean) => void;
|
||||
// setSearch: (search: Search) => void;
|
||||
// searchStats: SearchStats;
|
||||
// collections: Collection[];
|
||||
// setActiveCollection: (id: number) => void;
|
||||
// files: EnteFile[];
|
||||
// }
|
||||
// export default function SearchBar(props: Props) {
|
||||
// const selectRef = useRef(null);
|
||||
// const [value, setValue] = useState<Suggestion>(null);
|
||||
// const appContext = useContext(AppContext);
|
||||
|
||||
// const galleryContext = useContext(GalleryContext);
|
||||
// const handleChange = (value) => {
|
||||
// setValue(value);
|
||||
// };
|
||||
|
||||
// // TODO: HACK as AsyncSelect does not support default options reloading on focus/click
|
||||
// // unwanted side effect: placeholder is not shown on focus/click
|
||||
// // https://github.com/JedWatson/react-select/issues/1879
|
||||
// // for correct fix AsyncSelect can be extended to support default options reloading on focus/click
|
||||
// const handleOnFocus = () => {
|
||||
// if (appContext.mlSearchEnabled) {
|
||||
// const emptySearch = ' ';
|
||||
// selectRef.current.state.inputValue = emptySearch;
|
||||
// selectRef.current.select.state.inputValue = emptySearch;
|
||||
// selectRef.current.handleInputChange(emptySearch);
|
||||
// }
|
||||
// };
|
||||
|
||||
// useEffect(() => search(value), [value]);
|
||||
|
||||
// // = =========================
|
||||
// // Functionality
|
||||
// // = =========================
|
||||
// const getAutoCompleteSuggestions = async (searchPhrase: string) => {
|
||||
// const options: Array<Suggestion> = [];
|
||||
// searchPhrase = searchPhrase.trim().toLowerCase();
|
||||
// if (appContext.mlSearchEnabled) {
|
||||
// options.push(await getIndexStatusSuggestion());
|
||||
// options.push(...(await getAllPeopleSuggestion()));
|
||||
// }
|
||||
// if (!searchPhrase?.length) {
|
||||
// return options;
|
||||
// }
|
||||
// options.push(...getHolidaySuggestion(searchPhrase));
|
||||
// options.push(...getYearSuggestion(searchPhrase));
|
||||
|
||||
// const searchedDates = parseHumanDate(searchPhrase);
|
||||
|
||||
// options.push(
|
||||
// ...searchedDates.map((searchedDate) => ({
|
||||
// type: SuggestionType.DATE,
|
||||
// value: searchedDate,
|
||||
// label: getFormattedDate(searchedDate),
|
||||
// }))
|
||||
// );
|
||||
|
||||
// const collectionResults = searchCollection(
|
||||
// searchPhrase,
|
||||
// props.collections
|
||||
// );
|
||||
// options.push(
|
||||
// ...collectionResults.map(
|
||||
// (searchResult) =>
|
||||
// ({
|
||||
// type: SuggestionType.COLLECTION,
|
||||
// value: searchResult.id,
|
||||
// label: searchResult.name,
|
||||
// } as Suggestion)
|
||||
// )
|
||||
// );
|
||||
// const fileResults = searchFiles(searchPhrase, props.files);
|
||||
// options.push(
|
||||
// ...fileResults.map((file) => ({
|
||||
// type:
|
||||
// file.type === FILE_TYPE.IMAGE
|
||||
// ? SuggestionType.IMAGE
|
||||
// : SuggestionType.VIDEO,
|
||||
// value: file.index,
|
||||
// label: file.title,
|
||||
// }))
|
||||
// );
|
||||
|
||||
// const locationResults = await searchLocation(searchPhrase);
|
||||
|
||||
// const filteredLocationWithFiles = locationResults.filter(
|
||||
// (locationResult) =>
|
||||
// props.files.find((file) =>
|
||||
// isInsideBox(file.metadata, locationResult.bbox)
|
||||
// )
|
||||
// );
|
||||
// options.push(
|
||||
// ...filteredLocationWithFiles.map(
|
||||
// (searchResult) =>
|
||||
// ({
|
||||
// type: SuggestionType.LOCATION,
|
||||
// value: searchResult.bbox,
|
||||
// label: searchResult.place,
|
||||
// } as Suggestion)
|
||||
// )
|
||||
// );
|
||||
// const thingResults = await searchThing(searchPhrase);
|
||||
|
||||
// options.push(
|
||||
// ...thingResults.map(
|
||||
// (searchResult) =>
|
||||
// ({
|
||||
// type: SuggestionType.THING,
|
||||
// value: searchResult,
|
||||
// label: searchResult.className,
|
||||
// } as Suggestion)
|
||||
// )
|
||||
// );
|
||||
|
||||
// const textResults = await searchText(searchPhrase);
|
||||
|
||||
// options.push(
|
||||
// ...textResults.map(
|
||||
// (searchResult) =>
|
||||
// ({
|
||||
// type: SuggestionType.TEXT,
|
||||
// value: searchResult,
|
||||
// label: searchResult.word,
|
||||
// } as Suggestion)
|
||||
// )
|
||||
// );
|
||||
// return options;
|
||||
// };
|
||||
|
||||
// const getOptions = debounce(getAutoCompleteSuggestions, 250);
|
||||
|
||||
// const search = (selectedOption: Suggestion) => {
|
||||
// // console.log('search...');
|
||||
// if (!selectedOption) {
|
||||
// return;
|
||||
// }
|
||||
// switch (selectedOption.type) {
|
||||
// case SuggestionType.DATE:
|
||||
// props.setSearch({
|
||||
// date: selectedOption.value as DateValue,
|
||||
// });
|
||||
// props.setOpen(true);
|
||||
// break;
|
||||
// case SuggestionType.LOCATION:
|
||||
// props.setSearch({
|
||||
// location: selectedOption.value as Bbox,
|
||||
// });
|
||||
// props.setOpen(true);
|
||||
// break;
|
||||
// case SuggestionType.COLLECTION:
|
||||
// props.setActiveCollection(selectedOption.value as number);
|
||||
// setValue(null);
|
||||
// break;
|
||||
// case SuggestionType.IMAGE:
|
||||
// case SuggestionType.VIDEO:
|
||||
// props.setSearch({ fileIndex: selectedOption.value as number });
|
||||
// setValue(null);
|
||||
// break;
|
||||
// case SuggestionType.PERSON:
|
||||
// props.setSearch({ person: selectedOption.value as Person });
|
||||
// props.setOpen(true);
|
||||
// break;
|
||||
// case SuggestionType.THING:
|
||||
// props.setSearch({ thing: selectedOption.value as ThingClass });
|
||||
// props.setOpen(true);
|
||||
// break;
|
||||
// case SuggestionType.TEXT:
|
||||
// props.setSearch({ text: selectedOption.value as WordGroup });
|
||||
// props.setOpen(true);
|
||||
// break;
|
||||
// }
|
||||
// };
|
||||
// const resetSearch = () => {
|
||||
// if (props.isOpen) {
|
||||
// galleryContext.startLoading();
|
||||
// props.setSearch({});
|
||||
// setTimeout(() => {
|
||||
// galleryContext.finishLoading();
|
||||
// }, 10);
|
||||
// props.setOpen(false);
|
||||
// setValue(null);
|
||||
// }
|
||||
// };
|
||||
|
||||
// // = =========================
|
||||
// // UI
|
||||
// // = =========================
|
||||
|
||||
// const getIconByType = (type: SuggestionType) => {
|
||||
// switch (type) {
|
||||
// case SuggestionType.DATE:
|
||||
// return <DateIcon />;
|
||||
// case SuggestionType.LOCATION:
|
||||
// return <LocationIcon />;
|
||||
// case SuggestionType.COLLECTION:
|
||||
// return <CollectionIcon />;
|
||||
// case SuggestionType.IMAGE:
|
||||
// return <ImageIcon />;
|
||||
// case SuggestionType.VIDEO:
|
||||
// return <VideoIcon />;
|
||||
// case SuggestionType.THING:
|
||||
// return <ObjectIcon />;
|
||||
// case SuggestionType.TEXT:
|
||||
// return <TextIcon />;
|
||||
// default:
|
||||
// return <SearchIcon />;
|
||||
// }
|
||||
// };
|
||||
|
||||
// const LabelWithIcon = (props: { type: SuggestionType; label: string }) => (
|
||||
// <div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
// <span style={{ paddingRight: '10px', paddingBottom: '4px' }}>
|
||||
// {getIconByType(props.type)}
|
||||
// </span>
|
||||
// <span>{props.label}</span>
|
||||
// </div>
|
||||
// );
|
||||
// const { Option, Control, Menu } = components;
|
||||
|
||||
// const OptionWithIcon = (props) =>
|
||||
// !props.data.hide && (
|
||||
// <Option {...props}>
|
||||
// <LabelWithIcon
|
||||
// type={props.data.type}
|
||||
// label={props.data.label}
|
||||
// />
|
||||
// </Option>
|
||||
// );
|
||||
// const ControlWithIcon = (props) => (
|
||||
// <Control {...props}>
|
||||
// <span
|
||||
// className="icon"
|
||||
// style={{
|
||||
// paddingLeft: '10px',
|
||||
// paddingBottom: '4px',
|
||||
// }}>
|
||||
// {getIconByType(props.getValue()[0]?.type)}
|
||||
// </span>
|
||||
// {props.children}
|
||||
// </Control>
|
||||
// );
|
||||
|
||||
// const CustomMenu = (props) => {
|
||||
// // console.log("props.selectProps.options: ", selectRef);
|
||||
// const peopleSuggestions = props.selectProps.options.filter(
|
||||
// (o) => o.type === SuggestionType.PERSON
|
||||
// );
|
||||
// const people = peopleSuggestions.map((o) => o.value);
|
||||
|
||||
// const indexStatusSuggestion = props.selectProps.options.filter(
|
||||
// (o) => o.type === SuggestionType.INDEX_STATUS
|
||||
// )[0] as Suggestion;
|
||||
|
||||
// const indexStatus = indexStatusSuggestion?.value as IndexStatus;
|
||||
|
||||
// return (
|
||||
// <Menu {...props}>
|
||||
// {appContext.mlSearchEnabled && (
|
||||
// <Col>
|
||||
// <LegendRow>
|
||||
// <Legend>{constants.PEOPLE}</Legend>
|
||||
// {indexStatus && (
|
||||
// <Caption>{indexStatusSuggestion.label}</Caption>
|
||||
// )}
|
||||
// </LegendRow>
|
||||
// {people && people.length > 0 && (
|
||||
// <Row>
|
||||
// <PeopleList
|
||||
// people={people}
|
||||
// maxRows={2}
|
||||
// onSelect={(person, index) => {
|
||||
// selectRef.current.blur();
|
||||
// setValue(peopleSuggestions[index]);
|
||||
// }}></PeopleList>
|
||||
// </Row>
|
||||
// )}
|
||||
// </Col>
|
||||
// )}
|
||||
// {props.children}
|
||||
// </Menu>
|
||||
// );
|
||||
// };
|
||||
|
||||
// const customStyles = {
|
||||
// control: (style, { isFocused }) => ({
|
||||
// ...style,
|
||||
// backgroundColor: '#282828',
|
||||
// color: '#d1d1d1',
|
||||
// borderColor: isFocused ? '#51cd7c' : '#444',
|
||||
// boxShadow: 'none',
|
||||
// ':hover': {
|
||||
// borderColor: '#51cd7c',
|
||||
// cursor: 'text',
|
||||
// '&>.icon': { color: '#51cd7c' },
|
||||
// },
|
||||
// }),
|
||||
// input: (style) => ({
|
||||
// ...style,
|
||||
// color: '#d1d1d1',
|
||||
// }),
|
||||
// menu: (style) => ({
|
||||
// ...style,
|
||||
// marginTop: '10px',
|
||||
// backgroundColor: '#282828',
|
||||
// }),
|
||||
// option: (style, { isFocused }) => ({
|
||||
// ...style,
|
||||
// backgroundColor: isFocused && '#343434',
|
||||
// }),
|
||||
// dropdownIndicator: (style) => ({
|
||||
// ...style,
|
||||
// display: 'none',
|
||||
// }),
|
||||
// indicatorSeparator: (style) => ({
|
||||
// ...style,
|
||||
// display: 'none',
|
||||
// }),
|
||||
// clearIndicator: (style) => ({
|
||||
// ...style,
|
||||
// display: 'none',
|
||||
// }),
|
||||
// singleValue: (style, state) => ({
|
||||
// ...style,
|
||||
// backgroundColor: '#282828',
|
||||
// color: '#d1d1d1',
|
||||
// display: state.selectProps.menuIsOpen ? 'none' : 'block',
|
||||
// }),
|
||||
// placeholder: (style) => ({
|
||||
// ...style,
|
||||
// color: '#686868',
|
||||
// wordSpacing: '2px',
|
||||
// whiteSpace: 'nowrap',
|
||||
// }),
|
||||
// };
|
||||
// return (
|
||||
// <>
|
||||
// {props.searchStats && (
|
||||
// <SearchStatsContainer>
|
||||
// {constants.SEARCH_STATS(props.searchStats)}
|
||||
// </SearchStatsContainer>
|
||||
// )}
|
||||
// <Wrapper isDisabled={props.isFirstFetch} isOpen={props.isOpen}>
|
||||
// <SearchInput>
|
||||
// <div
|
||||
// style={{
|
||||
// flex: 1,
|
||||
// margin: '10px',
|
||||
// }}>
|
||||
// <AsyncSelect
|
||||
// ref={selectRef}
|
||||
// value={value}
|
||||
// components={{
|
||||
// Menu: CustomMenu,
|
||||
// Option: OptionWithIcon,
|
||||
// Control: ControlWithIcon,
|
||||
// }}
|
||||
// placeholder={constants.SEARCH_HINT()}
|
||||
// loadOptions={getOptions}
|
||||
// onChange={handleChange}
|
||||
// onFocus={handleOnFocus}
|
||||
// isClearable
|
||||
// escapeClearsValue
|
||||
// styles={customStyles}
|
||||
// noOptionsMessage={() => null}
|
||||
// />
|
||||
// </div>
|
||||
// {props.isOpen && (
|
||||
// <IconButton onClick={() => resetSearch()}>
|
||||
// <CloseIcon />
|
||||
// </IconButton>
|
||||
// )}
|
||||
// </SearchInput>
|
||||
// </Wrapper>
|
||||
// <SearchButton
|
||||
// isOpen={props.isOpen}
|
||||
// onClick={() => !props.isFirstFetch && props.setOpen(true)}>
|
||||
// <SearchIcon />
|
||||
// </SearchButton>
|
||||
// </>
|
||||
// );
|
||||
// }
|
|
@ -1,495 +0,0 @@
|
|||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import AsyncSelect from 'react-select/async';
|
||||
import { components } from 'react-select';
|
||||
import debounce from 'debounce-promise';
|
||||
import {
|
||||
getAllPeopleSuggestion,
|
||||
getHolidaySuggestion,
|
||||
getIndexStatusSuggestion,
|
||||
getYearSuggestion,
|
||||
parseHumanDate,
|
||||
searchCollection,
|
||||
searchFiles,
|
||||
searchLocation,
|
||||
searchText,
|
||||
searchThing,
|
||||
} from 'services/searchService';
|
||||
import { getFormattedDate, isInsideBox } from 'utils/search';
|
||||
import constants from 'utils/strings/constants';
|
||||
import LocationIcon from './icons/LocationIcon';
|
||||
import DateIcon from './icons/DateIcon';
|
||||
import SearchIcon from './icons/SearchIcon';
|
||||
import CloseIcon from './icons/CloseIcon';
|
||||
import { Collection } from 'types/collection';
|
||||
import CollectionIcon from './icons/CollectionIcon';
|
||||
|
||||
import ImageIcon from './icons/ImageIcon';
|
||||
import VideoIcon from './icons/VideoIcon';
|
||||
import { IconButton, Row } from './Container';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { Suggestion, SuggestionType, DateValue, Bbox } from 'types/search';
|
||||
import { Search, SearchStats } from 'types/gallery';
|
||||
import { FILE_TYPE } from 'constants/file';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { Col } from 'react-bootstrap';
|
||||
import { Person, ThingClass, WordGroup } from 'types/machineLearning';
|
||||
import { IndexStatus } from 'types/machineLearning/ui';
|
||||
import { PeopleList } from './MachineLearning/PeopleList';
|
||||
import ObjectIcon from './icons/ObjectIcon';
|
||||
import TextIcon from './icons/TextIcon';
|
||||
|
||||
const Wrapper = styled.div<{ isDisabled: boolean; isOpen: boolean }>`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
display: ${({ isOpen }) => (isOpen ? 'flex' : 'none')};
|
||||
width: 100%;
|
||||
background: #111;
|
||||
@media (min-width: 625px) {
|
||||
display: flex;
|
||||
width: calc(100vw - 140px);
|
||||
margin: 0 70px;
|
||||
}
|
||||
align-items: center;
|
||||
min-height: 64px;
|
||||
transition: opacity 1s ease;
|
||||
opacity: ${(props) => (props.isDisabled ? 0 : 1)};
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
|
||||
const SearchButton = styled.div<{ isOpen: boolean }>`
|
||||
display: none;
|
||||
@media (max-width: 624px) {
|
||||
display: ${({ isOpen }) => (!isOpen ? 'flex' : 'none')};
|
||||
right: 80px;
|
||||
cursor: pointer;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
align-items: center;
|
||||
min-height: 64px;
|
||||
}
|
||||
`;
|
||||
|
||||
const SearchStatsContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #979797;
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
|
||||
const SearchInput = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 484px;
|
||||
margin: auto;
|
||||
`;
|
||||
|
||||
const Legend = styled.span`
|
||||
font-size: 20px;
|
||||
color: #ddd;
|
||||
display: inline;
|
||||
padding: 8px 12px;
|
||||
`;
|
||||
|
||||
const Caption = styled.span`
|
||||
font-size: 12px;
|
||||
display: inline;
|
||||
padding: 8px 12px;
|
||||
`;
|
||||
|
||||
const LegendRow = styled(Row)`
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0px;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
isFirstFetch: boolean;
|
||||
setOpen: (value: boolean) => void;
|
||||
setSearch: (search: Search) => void;
|
||||
searchStats: SearchStats;
|
||||
collections: Collection[];
|
||||
setActiveCollection: (id: number) => void;
|
||||
files: EnteFile[];
|
||||
}
|
||||
export default function SearchBar(props: Props) {
|
||||
const selectRef = useRef(null);
|
||||
const [value, setValue] = useState<Suggestion>(null);
|
||||
const appContext = useContext(AppContext);
|
||||
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const handleChange = (value) => {
|
||||
setValue(value);
|
||||
};
|
||||
|
||||
// TODO: HACK as AsyncSelect does not support default options reloading on focus/click
|
||||
// unwanted side effect: placeholder is not shown on focus/click
|
||||
// https://github.com/JedWatson/react-select/issues/1879
|
||||
// for correct fix AsyncSelect can be extended to support default options reloading on focus/click
|
||||
const handleOnFocus = () => {
|
||||
if (appContext.mlSearchEnabled) {
|
||||
const emptySearch = ' ';
|
||||
selectRef.current.state.inputValue = emptySearch;
|
||||
selectRef.current.select.state.inputValue = emptySearch;
|
||||
selectRef.current.handleInputChange(emptySearch);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => search(value), [value]);
|
||||
|
||||
// = =========================
|
||||
// Functionality
|
||||
// = =========================
|
||||
const getAutoCompleteSuggestions = async (searchPhrase: string) => {
|
||||
const options: Array<Suggestion> = [];
|
||||
searchPhrase = searchPhrase.trim().toLowerCase();
|
||||
if (appContext.mlSearchEnabled) {
|
||||
options.push(await getIndexStatusSuggestion());
|
||||
options.push(...(await getAllPeopleSuggestion()));
|
||||
}
|
||||
if (!searchPhrase?.length) {
|
||||
return options;
|
||||
}
|
||||
options.push(...getHolidaySuggestion(searchPhrase));
|
||||
options.push(...getYearSuggestion(searchPhrase));
|
||||
|
||||
const searchedDates = parseHumanDate(searchPhrase);
|
||||
|
||||
options.push(
|
||||
...searchedDates.map((searchedDate) => ({
|
||||
type: SuggestionType.DATE,
|
||||
value: searchedDate,
|
||||
label: getFormattedDate(searchedDate),
|
||||
}))
|
||||
);
|
||||
|
||||
const collectionResults = searchCollection(
|
||||
searchPhrase,
|
||||
props.collections
|
||||
);
|
||||
options.push(
|
||||
...collectionResults.map(
|
||||
(searchResult) =>
|
||||
({
|
||||
type: SuggestionType.COLLECTION,
|
||||
value: searchResult.id,
|
||||
label: searchResult.name,
|
||||
} as Suggestion)
|
||||
)
|
||||
);
|
||||
const fileResults = searchFiles(searchPhrase, props.files);
|
||||
options.push(
|
||||
...fileResults.map((file) => ({
|
||||
type:
|
||||
file.type === FILE_TYPE.IMAGE
|
||||
? SuggestionType.IMAGE
|
||||
: SuggestionType.VIDEO,
|
||||
value: file.index,
|
||||
label: file.title,
|
||||
}))
|
||||
);
|
||||
|
||||
const locationResults = await searchLocation(searchPhrase);
|
||||
|
||||
const filteredLocationWithFiles = locationResults.filter(
|
||||
(locationResult) =>
|
||||
props.files.find((file) =>
|
||||
isInsideBox(file.metadata, locationResult.bbox)
|
||||
)
|
||||
);
|
||||
options.push(
|
||||
...filteredLocationWithFiles.map(
|
||||
(searchResult) =>
|
||||
({
|
||||
type: SuggestionType.LOCATION,
|
||||
value: searchResult.bbox,
|
||||
label: searchResult.place,
|
||||
} as Suggestion)
|
||||
)
|
||||
);
|
||||
const thingResults = await searchThing(searchPhrase);
|
||||
|
||||
options.push(
|
||||
...thingResults.map(
|
||||
(searchResult) =>
|
||||
({
|
||||
type: SuggestionType.THING,
|
||||
value: searchResult,
|
||||
label: searchResult.className,
|
||||
} as Suggestion)
|
||||
)
|
||||
);
|
||||
|
||||
const textResults = await searchText(searchPhrase);
|
||||
|
||||
options.push(
|
||||
...textResults.map(
|
||||
(searchResult) =>
|
||||
({
|
||||
type: SuggestionType.TEXT,
|
||||
value: searchResult,
|
||||
label: searchResult.word,
|
||||
} as Suggestion)
|
||||
)
|
||||
);
|
||||
return options;
|
||||
};
|
||||
|
||||
const getOptions = debounce(getAutoCompleteSuggestions, 250);
|
||||
|
||||
const search = (selectedOption: Suggestion) => {
|
||||
// console.log('search...');
|
||||
if (!selectedOption) {
|
||||
return;
|
||||
}
|
||||
switch (selectedOption.type) {
|
||||
case SuggestionType.DATE:
|
||||
props.setSearch({
|
||||
date: selectedOption.value as DateValue,
|
||||
});
|
||||
props.setOpen(true);
|
||||
break;
|
||||
case SuggestionType.LOCATION:
|
||||
props.setSearch({
|
||||
location: selectedOption.value as Bbox,
|
||||
});
|
||||
props.setOpen(true);
|
||||
break;
|
||||
case SuggestionType.COLLECTION:
|
||||
props.setActiveCollection(selectedOption.value as number);
|
||||
setValue(null);
|
||||
break;
|
||||
case SuggestionType.IMAGE:
|
||||
case SuggestionType.VIDEO:
|
||||
props.setSearch({ fileIndex: selectedOption.value as number });
|
||||
setValue(null);
|
||||
break;
|
||||
case SuggestionType.PERSON:
|
||||
props.setSearch({ person: selectedOption.value as Person });
|
||||
props.setOpen(true);
|
||||
break;
|
||||
case SuggestionType.THING:
|
||||
props.setSearch({ thing: selectedOption.value as ThingClass });
|
||||
props.setOpen(true);
|
||||
break;
|
||||
case SuggestionType.TEXT:
|
||||
props.setSearch({ text: selectedOption.value as WordGroup });
|
||||
props.setOpen(true);
|
||||
break;
|
||||
}
|
||||
};
|
||||
const resetSearch = () => {
|
||||
if (props.isOpen) {
|
||||
galleryContext.startLoading();
|
||||
props.setSearch({});
|
||||
setTimeout(() => {
|
||||
galleryContext.finishLoading();
|
||||
}, 10);
|
||||
props.setOpen(false);
|
||||
setValue(null);
|
||||
}
|
||||
};
|
||||
|
||||
// = =========================
|
||||
// UI
|
||||
// = =========================
|
||||
|
||||
const getIconByType = (type: SuggestionType) => {
|
||||
switch (type) {
|
||||
case SuggestionType.DATE:
|
||||
return <DateIcon />;
|
||||
case SuggestionType.LOCATION:
|
||||
return <LocationIcon />;
|
||||
case SuggestionType.COLLECTION:
|
||||
return <CollectionIcon />;
|
||||
case SuggestionType.IMAGE:
|
||||
return <ImageIcon />;
|
||||
case SuggestionType.VIDEO:
|
||||
return <VideoIcon />;
|
||||
case SuggestionType.THING:
|
||||
return <ObjectIcon />;
|
||||
case SuggestionType.TEXT:
|
||||
return <TextIcon />;
|
||||
default:
|
||||
return <SearchIcon />;
|
||||
}
|
||||
};
|
||||
|
||||
const LabelWithIcon = (props: { type: SuggestionType; label: string }) => (
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<span style={{ paddingRight: '10px', paddingBottom: '4px' }}>
|
||||
{getIconByType(props.type)}
|
||||
</span>
|
||||
<span>{props.label}</span>
|
||||
</div>
|
||||
);
|
||||
const { Option, Control, Menu } = components;
|
||||
|
||||
const OptionWithIcon = (props) =>
|
||||
!props.data.hide && (
|
||||
<Option {...props}>
|
||||
<LabelWithIcon
|
||||
type={props.data.type}
|
||||
label={props.data.label}
|
||||
/>
|
||||
</Option>
|
||||
);
|
||||
const ControlWithIcon = (props) => (
|
||||
<Control {...props}>
|
||||
<span
|
||||
className="icon"
|
||||
style={{
|
||||
paddingLeft: '10px',
|
||||
paddingBottom: '4px',
|
||||
}}>
|
||||
{getIconByType(props.getValue()[0]?.type)}
|
||||
</span>
|
||||
{props.children}
|
||||
</Control>
|
||||
);
|
||||
|
||||
const CustomMenu = (props) => {
|
||||
// console.log("props.selectProps.options: ", selectRef);
|
||||
const peopleSuggestions = props.selectProps.options.filter(
|
||||
(o) => o.type === SuggestionType.PERSON
|
||||
);
|
||||
const people = peopleSuggestions.map((o) => o.value);
|
||||
|
||||
const indexStatusSuggestion = props.selectProps.options.filter(
|
||||
(o) => o.type === SuggestionType.INDEX_STATUS
|
||||
)[0] as Suggestion;
|
||||
|
||||
const indexStatus = indexStatusSuggestion?.value as IndexStatus;
|
||||
|
||||
return (
|
||||
<Menu {...props}>
|
||||
{appContext.mlSearchEnabled && (
|
||||
<Col>
|
||||
<LegendRow>
|
||||
<Legend>{constants.PEOPLE}</Legend>
|
||||
{indexStatus && (
|
||||
<Caption>{indexStatusSuggestion.label}</Caption>
|
||||
)}
|
||||
</LegendRow>
|
||||
{people && people.length > 0 && (
|
||||
<Row>
|
||||
<PeopleList
|
||||
people={people}
|
||||
maxRows={2}
|
||||
onSelect={(person, index) => {
|
||||
selectRef.current.blur();
|
||||
setValue(peopleSuggestions[index]);
|
||||
}}></PeopleList>
|
||||
</Row>
|
||||
)}
|
||||
</Col>
|
||||
)}
|
||||
{props.children}
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
const customStyles = {
|
||||
control: (style, { isFocused }) => ({
|
||||
...style,
|
||||
backgroundColor: '#282828',
|
||||
color: '#d1d1d1',
|
||||
borderColor: isFocused ? '#51cd7c' : '#444',
|
||||
boxShadow: 'none',
|
||||
':hover': {
|
||||
borderColor: '#51cd7c',
|
||||
cursor: 'text',
|
||||
'&>.icon': { color: '#51cd7c' },
|
||||
},
|
||||
}),
|
||||
input: (style) => ({
|
||||
...style,
|
||||
color: '#d1d1d1',
|
||||
}),
|
||||
menu: (style) => ({
|
||||
...style,
|
||||
marginTop: '10px',
|
||||
backgroundColor: '#282828',
|
||||
}),
|
||||
option: (style, { isFocused }) => ({
|
||||
...style,
|
||||
backgroundColor: isFocused && '#343434',
|
||||
}),
|
||||
dropdownIndicator: (style) => ({
|
||||
...style,
|
||||
display: 'none',
|
||||
}),
|
||||
indicatorSeparator: (style) => ({
|
||||
...style,
|
||||
display: 'none',
|
||||
}),
|
||||
clearIndicator: (style) => ({
|
||||
...style,
|
||||
display: 'none',
|
||||
}),
|
||||
singleValue: (style, state) => ({
|
||||
...style,
|
||||
backgroundColor: '#282828',
|
||||
color: '#d1d1d1',
|
||||
display: state.selectProps.menuIsOpen ? 'none' : 'block',
|
||||
}),
|
||||
placeholder: (style) => ({
|
||||
...style,
|
||||
color: '#686868',
|
||||
wordSpacing: '2px',
|
||||
whiteSpace: 'nowrap',
|
||||
}),
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{props.searchStats && (
|
||||
<SearchStatsContainer>
|
||||
{constants.SEARCH_STATS(props.searchStats)}
|
||||
</SearchStatsContainer>
|
||||
)}
|
||||
<Wrapper isDisabled={props.isFirstFetch} isOpen={props.isOpen}>
|
||||
<SearchInput>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
margin: '10px',
|
||||
}}>
|
||||
<AsyncSelect
|
||||
ref={selectRef}
|
||||
value={value}
|
||||
components={{
|
||||
Menu: CustomMenu,
|
||||
Option: OptionWithIcon,
|
||||
Control: ControlWithIcon,
|
||||
}}
|
||||
placeholder={constants.SEARCH_HINT()}
|
||||
loadOptions={getOptions}
|
||||
onChange={handleChange}
|
||||
onFocus={handleOnFocus}
|
||||
isClearable
|
||||
escapeClearsValue
|
||||
styles={customStyles}
|
||||
noOptionsMessage={() => null}
|
||||
/>
|
||||
</div>
|
||||
{props.isOpen && (
|
||||
<IconButton onClick={() => resetSearch()}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</SearchInput>
|
||||
</Wrapper>
|
||||
<SearchButton
|
||||
isOpen={props.isOpen}
|
||||
onClick={() => !props.isFirstFetch && props.setOpen(true)}>
|
||||
<SearchIcon />
|
||||
</SearchButton>
|
||||
</>
|
||||
);
|
||||
}
|
455
src/components/Sidebar-old.tsx
Normal file
455
src/components/Sidebar-old.tsx
Normal file
|
@ -0,0 +1,455 @@
|
|||
export {};
|
||||
// import React, { useContext, useEffect, useState } from 'react';
|
||||
|
||||
// import { slide as Menu } from 'react-burger-menu';
|
||||
// import constants from 'utils/strings/constants';
|
||||
// import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||
// import { getToken } from 'utils/common/key';
|
||||
// import { getEndpoint } from 'utils/common/apiUtil';
|
||||
// import { Button } from 'react-bootstrap';
|
||||
// import {
|
||||
// isSubscriptionActive,
|
||||
// getUserSubscription,
|
||||
// isOnFreePlan,
|
||||
// isSubscriptionCancelled,
|
||||
// isSubscribed,
|
||||
// convertToHumanReadable,
|
||||
// } from 'utils/billing';
|
||||
|
||||
// import isElectron from 'is-electron';
|
||||
// import { Collection } from 'types/collection';
|
||||
// import { useRouter } from 'next/router';
|
||||
// import LinkButton from './pages/gallery/LinkButton';
|
||||
// import { downloadApp } from 'utils/common';
|
||||
// import { getUserDetails, logoutUser } from 'services/userService';
|
||||
// import { LogoImage } from 'pages/_app';
|
||||
// import { SetDialogMessage } from './MessageDialog';
|
||||
// import EnteSpinner from './EnteSpinner';
|
||||
// import RecoveryKeyModal from './RecoveryKeyModal';
|
||||
// import TwoFactorModal from './TwoFactorModal';
|
||||
// import ExportModal from './ExportModal';
|
||||
// import { GalleryContext } from 'pages/gallery';
|
||||
// import InProgressIcon from './icons/InProgressIcon';
|
||||
// import exportService from 'services/exportService';
|
||||
// import { Subscription } from 'types/billing';
|
||||
// import { PAGES } from 'constants/pages';
|
||||
// import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
|
||||
// import FixLargeThumbnails from './FixLargeThumbnail';
|
||||
// import { AppContext } from 'pages/_app';
|
||||
// import { canEnableMlSearch } from 'utils/machineLearning/compatibility';
|
||||
// import { SetLoading } from 'types/gallery';
|
||||
// import mlIDbStorage from 'utils/storage/mlIDbStorage';
|
||||
// interface Props {
|
||||
// collections: Collection[];
|
||||
// setDialogMessage: SetDialogMessage;
|
||||
// setLoading: SetLoading;
|
||||
// }
|
||||
// export default function Sidebar(props: Props) {
|
||||
// const [usage, SetUsage] = useState<string>(null);
|
||||
// const [user, setUser] = useState(null);
|
||||
// const [subscription, setSubscription] = useState<Subscription>(null);
|
||||
// useEffect(() => {
|
||||
// setUser(getData(LS_KEYS.USER));
|
||||
// setSubscription(getUserSubscription());
|
||||
// }, []);
|
||||
// const [isOpen, setIsOpen] = useState(false);
|
||||
// const [recoverModalView, setRecoveryModalView] = useState(false);
|
||||
// const [twoFactorModalView, setTwoFactorModalView] = useState(false);
|
||||
// const [exportModalView, setExportModalView] = useState(false);
|
||||
// const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false);
|
||||
// const galleryContext = useContext(GalleryContext);
|
||||
// const appContext = useContext(AppContext);
|
||||
// const enableMlSearch = async () => {
|
||||
// await appContext.updateMlSearchEnabled(true);
|
||||
// };
|
||||
// const disableMlSearch = async () => {
|
||||
// await appContext.updateMlSearchEnabled(false);
|
||||
// };
|
||||
|
||||
// const clearMLDB = async () => {
|
||||
// await mlIDbStorage.clearMLDB();
|
||||
// };
|
||||
// useEffect(() => {
|
||||
// const main = async () => {
|
||||
// if (!isOpen) {
|
||||
// return;
|
||||
// }
|
||||
// const userDetails = await getUserDetails();
|
||||
// setUser({ ...user, email: userDetails.email });
|
||||
// SetUsage(convertToHumanReadable(userDetails.usage));
|
||||
// setSubscription(userDetails.subscription);
|
||||
// setData(LS_KEYS.USER, {
|
||||
// ...getData(LS_KEYS.USER),
|
||||
// email: userDetails.email,
|
||||
// });
|
||||
// setData(LS_KEYS.SUBSCRIPTION, userDetails.subscription);
|
||||
// };
|
||||
// main();
|
||||
// }, [isOpen]);
|
||||
|
||||
// function openFeedbackURL() {
|
||||
// const feedbackURL: string = `${getEndpoint()}/users/feedback?token=${encodeURIComponent(
|
||||
// getToken()
|
||||
// )}`;
|
||||
// const win = window.open(feedbackURL, '_blank');
|
||||
// win.focus();
|
||||
// }
|
||||
|
||||
// function initiateEmail(email: string) {
|
||||
// const a = document.createElement('a');
|
||||
// a.href = 'mailto:' + email;
|
||||
// a.rel = 'noreferrer noopener';
|
||||
// a.click();
|
||||
// }
|
||||
|
||||
// // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
// function exportFiles() {
|
||||
// if (isElectron()) {
|
||||
// setExportModalView(true);
|
||||
// } else {
|
||||
// props.setDialogMessage({
|
||||
// title: constants.DOWNLOAD_APP,
|
||||
// content: constants.DOWNLOAD_APP_MESSAGE(),
|
||||
// staticBackdrop: true,
|
||||
// proceed: {
|
||||
// text: constants.DOWNLOAD,
|
||||
// action: downloadApp,
|
||||
// variant: 'success',
|
||||
// },
|
||||
// close: {
|
||||
// text: constants.CLOSE,
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// const router = useRouter();
|
||||
// function onManageClick() {
|
||||
// setIsOpen(false);
|
||||
// galleryContext.showPlanSelectorModal();
|
||||
// }
|
||||
|
||||
// const Divider = () => (
|
||||
// <div
|
||||
// style={{
|
||||
// height: '1px',
|
||||
// marginTop: '40px',
|
||||
// background: '#242424',
|
||||
// width: '100%',
|
||||
// }}
|
||||
// />
|
||||
// );
|
||||
// return (
|
||||
// <Menu
|
||||
// isOpen={isOpen}
|
||||
// onStateChange={(state) => setIsOpen(state.isOpen)}
|
||||
// itemListElement="div">
|
||||
// <div
|
||||
// style={{
|
||||
// display: 'flex',
|
||||
// outline: 'none',
|
||||
// textAlign: 'center',
|
||||
// }}>
|
||||
// <LogoImage
|
||||
// style={{ height: '24px', padding: '3px' }}
|
||||
// alt="logo"
|
||||
// src="/icon.svg"
|
||||
// />
|
||||
// </div>
|
||||
// <div
|
||||
// style={{
|
||||
// outline: 'none',
|
||||
// color: 'rgb(45, 194, 98)',
|
||||
// fontSize: '16px',
|
||||
// }}>
|
||||
// {user?.email}
|
||||
// </div>
|
||||
// <div
|
||||
// style={{
|
||||
// flex: 1,
|
||||
// overflow: 'auto',
|
||||
// outline: 'none',
|
||||
// paddingTop: '0',
|
||||
// }}>
|
||||
// <div style={{ outline: 'none' }}>
|
||||
// <div style={{ display: 'flex' }}>
|
||||
// <h5 style={{ margin: '4px 0 12px 2px' }}>
|
||||
// {constants.SUBSCRIPTION_PLAN}
|
||||
// </h5>
|
||||
// </div>
|
||||
// <div style={{ color: '#959595' }}>
|
||||
// {isSubscriptionActive(subscription) ? (
|
||||
// isOnFreePlan(subscription) ? (
|
||||
// constants.FREE_SUBSCRIPTION_INFO(
|
||||
// subscription?.expiryTime
|
||||
// )
|
||||
// ) : isSubscriptionCancelled(subscription) ? (
|
||||
// constants.RENEWAL_CANCELLED_SUBSCRIPTION_INFO(
|
||||
// subscription?.expiryTime
|
||||
// )
|
||||
// ) : (
|
||||
// constants.RENEWAL_ACTIVE_SUBSCRIPTION_INFO(
|
||||
// subscription?.expiryTime
|
||||
// )
|
||||
// )
|
||||
// ) : (
|
||||
// <p>{constants.SUBSCRIPTION_EXPIRED}</p>
|
||||
// )}
|
||||
// <Button
|
||||
// variant="outline-success"
|
||||
// block
|
||||
// size="sm"
|
||||
// onClick={onManageClick}>
|
||||
// {isSubscribed(subscription)
|
||||
// ? constants.MANAGE
|
||||
// : constants.SUBSCRIBE}
|
||||
// </Button>
|
||||
// </div>
|
||||
// </div>
|
||||
// <div style={{ outline: 'none', marginTop: '30px' }} />
|
||||
// <div>
|
||||
// <h5 style={{ marginBottom: '12px' }}>
|
||||
// {constants.USAGE_DETAILS}
|
||||
// </h5>
|
||||
// <div style={{ color: '#959595' }}>
|
||||
// {usage ? (
|
||||
// constants.USAGE_INFO(
|
||||
// usage,
|
||||
// convertToHumanReadable(subscription?.storage)
|
||||
// )
|
||||
// ) : (
|
||||
// <div style={{ textAlign: 'center' }}>
|
||||
// <EnteSpinner
|
||||
// style={{
|
||||
// borderWidth: '2px',
|
||||
// width: '20px',
|
||||
// height: '20px',
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// )}
|
||||
// </div>
|
||||
// </div>
|
||||
// <Divider />
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => {
|
||||
// galleryContext.setActiveCollection(ARCHIVE_SECTION);
|
||||
// setIsOpen(false);
|
||||
// }}>
|
||||
// {constants.ARCHIVE}
|
||||
// </LinkButton>
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => {
|
||||
// galleryContext.setActiveCollection(TRASH_SECTION);
|
||||
// setIsOpen(false);
|
||||
// }}>
|
||||
// {constants.TRASH}
|
||||
// </LinkButton>
|
||||
// <>
|
||||
// <RecoveryKeyModal
|
||||
// show={recoverModalView}
|
||||
// onHide={() => setRecoveryModalView(false)}
|
||||
// somethingWentWrong={() =>
|
||||
// props.setDialogMessage({
|
||||
// title: constants.ERROR,
|
||||
// content:
|
||||
// constants.RECOVER_KEY_GENERATION_FAILED,
|
||||
// close: { variant: 'danger' },
|
||||
// })
|
||||
// }
|
||||
// />
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => setRecoveryModalView(true)}>
|
||||
// {constants.DOWNLOAD_RECOVERY_KEY}
|
||||
// </LinkButton>
|
||||
// </>
|
||||
// <>
|
||||
// <TwoFactorModal
|
||||
// show={twoFactorModalView}
|
||||
// onHide={() => setTwoFactorModalView(false)}
|
||||
// setDialogMessage={props.setDialogMessage}
|
||||
// closeSidebar={() => setIsOpen(false)}
|
||||
// setLoading={props.setLoading}
|
||||
// />
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => setTwoFactorModalView(true)}>
|
||||
// {constants.TWO_FACTOR}
|
||||
// </LinkButton>
|
||||
// </>
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => {
|
||||
// router.push(PAGES.CHANGE_PASSWORD);
|
||||
// }}>
|
||||
// {constants.CHANGE_PASSWORD}
|
||||
// </LinkButton>
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => {
|
||||
// router.push(PAGES.CHANGE_EMAIL);
|
||||
// }}>
|
||||
// {constants.UPDATE_EMAIL}
|
||||
// </LinkButton>
|
||||
// <Divider />
|
||||
// <>
|
||||
// <FixLargeThumbnails
|
||||
// isOpen={fixLargeThumbsView}
|
||||
// hide={() => setFixLargeThumbsView(false)}
|
||||
// show={() => setFixLargeThumbsView(true)}
|
||||
// />
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => setFixLargeThumbsView(true)}>
|
||||
// {constants.FIX_LARGE_THUMBNAILS}
|
||||
// </LinkButton>
|
||||
// </>
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={openFeedbackURL}>
|
||||
// {constants.REQUEST_FEATURE}
|
||||
// </LinkButton>
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => {
|
||||
// if (!appContext.mlSearchEnabled) {
|
||||
// if (!canEnableMlSearch()) {
|
||||
// props.setDialogMessage({
|
||||
// title: constants.ENABLE_ML_SEARCH,
|
||||
// content: constants.ML_SEARCH_NOT_COMPATIBLE,
|
||||
// close: { text: constants.OK },
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// props.setDialogMessage({
|
||||
// title: `${constants.CONFIRM} ${constants.ENABLE_ML_SEARCH}`,
|
||||
// content: constants.ENABLE_ML_SEARCH_MESSAGE,
|
||||
// staticBackdrop: true,
|
||||
// proceed: {
|
||||
// text: constants.ENABLE_ML_SEARCH,
|
||||
// action: enableMlSearch,
|
||||
// variant: 'success',
|
||||
// },
|
||||
// close: { text: constants.CANCEL },
|
||||
// });
|
||||
// } else {
|
||||
// disableMlSearch();
|
||||
// }
|
||||
// }}>
|
||||
// {appContext.mlSearchEnabled
|
||||
// ? constants.DISABLE_ML_SEARCH
|
||||
// : constants.ENABLE_ML_SEARCH}
|
||||
// </LinkButton>
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => {
|
||||
// if (!appContext.mlSearchEnabled) {
|
||||
// if (!canEnableMlSearch()) {
|
||||
// props.setDialogMessage({
|
||||
// title: constants.ENABLE_ML_SEARCH,
|
||||
// content: constants.ML_SEARCH_NOT_COMPATIBLE,
|
||||
// close: { text: constants.OK },
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
// props.setDialogMessage({
|
||||
// title: 'clear mb db',
|
||||
// content: 'clear mb db',
|
||||
// staticBackdrop: true,
|
||||
// proceed: {
|
||||
// text: 'clear',
|
||||
// action: clearMLDB,
|
||||
// variant: 'success',
|
||||
// },
|
||||
// close: { text: constants.CANCEL },
|
||||
// });
|
||||
// } else {
|
||||
// disableMlSearch();
|
||||
// }
|
||||
// }}>
|
||||
// {'clear ML db'}
|
||||
// </LinkButton>
|
||||
// {appContext.mlSearchEnabled && (
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => {
|
||||
// router.push(PAGES.ML_DEBUG);
|
||||
// }}>
|
||||
// {constants.ML_DEBUG}
|
||||
// </LinkButton>
|
||||
// )}
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() => initiateEmail('contact@ente.io')}>
|
||||
// {constants.SUPPORT}
|
||||
// </LinkButton>
|
||||
// <>
|
||||
// <ExportModal
|
||||
// show={exportModalView}
|
||||
// onHide={() => setExportModalView(false)}
|
||||
// usage={usage}
|
||||
// />
|
||||
// <LinkButton
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={exportFiles}>
|
||||
// <div style={{ display: 'flex' }}>
|
||||
// {constants.EXPORT}
|
||||
// <div style={{ width: '20px' }} />
|
||||
// {exportService.isExportInProgress() && (
|
||||
// <InProgressIcon />
|
||||
// )}
|
||||
// </div>
|
||||
// </LinkButton>
|
||||
// </>
|
||||
// <Divider />
|
||||
// <LinkButton
|
||||
// variant="danger"
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() =>
|
||||
// props.setDialogMessage({
|
||||
// title: `${constants.CONFIRM} ${constants.LOGOUT}`,
|
||||
// content: constants.LOGOUT_MESSAGE,
|
||||
// staticBackdrop: true,
|
||||
// proceed: {
|
||||
// text: constants.LOGOUT,
|
||||
// action: logoutUser,
|
||||
// variant: 'danger',
|
||||
// },
|
||||
// close: { text: constants.CANCEL },
|
||||
// })
|
||||
// }>
|
||||
// {constants.LOGOUT}
|
||||
// </LinkButton>
|
||||
// <LinkButton
|
||||
// variant="danger"
|
||||
// style={{ marginTop: '30px' }}
|
||||
// onClick={() =>
|
||||
// props.setDialogMessage({
|
||||
// title: `${constants.DELETE_ACCOUNT}`,
|
||||
// content: constants.DELETE_ACCOUNT_MESSAGE(),
|
||||
// staticBackdrop: true,
|
||||
// proceed: {
|
||||
// text: constants.DELETE_ACCOUNT,
|
||||
// action: () => {
|
||||
// initiateEmail('account-deletion@ente.io');
|
||||
// },
|
||||
// variant: 'danger',
|
||||
// },
|
||||
// close: { text: constants.CANCEL },
|
||||
// })
|
||||
// }>
|
||||
// {constants.DELETE_ACCOUNT}
|
||||
// </LinkButton>
|
||||
// <div
|
||||
// style={{
|
||||
// marginTop: '40px',
|
||||
// width: '100%',
|
||||
// }}
|
||||
// />
|
||||
// </div>
|
||||
// </Menu>
|
||||
// );
|
||||
// }
|
|
@ -1,454 +0,0 @@
|
|||
import React, { useContext, useEffect, useState } from 'react';
|
||||
|
||||
import { slide as Menu } from 'react-burger-menu';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||
import { getToken } from 'utils/common/key';
|
||||
import { getEndpoint } from 'utils/common/apiUtil';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import {
|
||||
isSubscriptionActive,
|
||||
getUserSubscription,
|
||||
isOnFreePlan,
|
||||
isSubscriptionCancelled,
|
||||
isSubscribed,
|
||||
convertToHumanReadable,
|
||||
} from 'utils/billing';
|
||||
|
||||
import isElectron from 'is-electron';
|
||||
import { Collection } from 'types/collection';
|
||||
import { useRouter } from 'next/router';
|
||||
import LinkButton from './pages/gallery/LinkButton';
|
||||
import { downloadApp } from 'utils/common';
|
||||
import { getUserDetails, logoutUser } from 'services/userService';
|
||||
import { LogoImage } from 'pages/_app';
|
||||
import { SetDialogMessage } from './MessageDialog';
|
||||
import EnteSpinner from './EnteSpinner';
|
||||
import RecoveryKeyModal from './RecoveryKeyModal';
|
||||
import TwoFactorModal from './TwoFactorModal';
|
||||
import ExportModal from './ExportModal';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import InProgressIcon from './icons/InProgressIcon';
|
||||
import exportService from 'services/exportService';
|
||||
import { Subscription } from 'types/billing';
|
||||
import { PAGES } from 'constants/pages';
|
||||
import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
|
||||
import FixLargeThumbnails from './FixLargeThumbnail';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { canEnableMlSearch } from 'utils/machineLearning/compatibility';
|
||||
import { SetLoading } from 'types/gallery';
|
||||
import mlIDbStorage from 'utils/storage/mlIDbStorage';
|
||||
interface Props {
|
||||
collections: Collection[];
|
||||
setDialogMessage: SetDialogMessage;
|
||||
setLoading: SetLoading;
|
||||
}
|
||||
export default function Sidebar(props: Props) {
|
||||
const [usage, SetUsage] = useState<string>(null);
|
||||
const [user, setUser] = useState(null);
|
||||
const [subscription, setSubscription] = useState<Subscription>(null);
|
||||
useEffect(() => {
|
||||
setUser(getData(LS_KEYS.USER));
|
||||
setSubscription(getUserSubscription());
|
||||
}, []);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [recoverModalView, setRecoveryModalView] = useState(false);
|
||||
const [twoFactorModalView, setTwoFactorModalView] = useState(false);
|
||||
const [exportModalView, setExportModalView] = useState(false);
|
||||
const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const appContext = useContext(AppContext);
|
||||
const enableMlSearch = async () => {
|
||||
await appContext.updateMlSearchEnabled(true);
|
||||
};
|
||||
const disableMlSearch = async () => {
|
||||
await appContext.updateMlSearchEnabled(false);
|
||||
};
|
||||
|
||||
const clearMLDB = async () => {
|
||||
await mlIDbStorage.clearMLDB();
|
||||
};
|
||||
useEffect(() => {
|
||||
const main = async () => {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
const userDetails = await getUserDetails();
|
||||
setUser({ ...user, email: userDetails.email });
|
||||
SetUsage(convertToHumanReadable(userDetails.usage));
|
||||
setSubscription(userDetails.subscription);
|
||||
setData(LS_KEYS.USER, {
|
||||
...getData(LS_KEYS.USER),
|
||||
email: userDetails.email,
|
||||
});
|
||||
setData(LS_KEYS.SUBSCRIPTION, userDetails.subscription);
|
||||
};
|
||||
main();
|
||||
}, [isOpen]);
|
||||
|
||||
function openFeedbackURL() {
|
||||
const feedbackURL: string = `${getEndpoint()}/users/feedback?token=${encodeURIComponent(
|
||||
getToken()
|
||||
)}`;
|
||||
const win = window.open(feedbackURL, '_blank');
|
||||
win.focus();
|
||||
}
|
||||
|
||||
function initiateEmail(email: string) {
|
||||
const a = document.createElement('a');
|
||||
a.href = 'mailto:' + email;
|
||||
a.rel = 'noreferrer noopener';
|
||||
a.click();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function exportFiles() {
|
||||
if (isElectron()) {
|
||||
setExportModalView(true);
|
||||
} else {
|
||||
props.setDialogMessage({
|
||||
title: constants.DOWNLOAD_APP,
|
||||
content: constants.DOWNLOAD_APP_MESSAGE(),
|
||||
staticBackdrop: true,
|
||||
proceed: {
|
||||
text: constants.DOWNLOAD,
|
||||
action: downloadApp,
|
||||
variant: 'success',
|
||||
},
|
||||
close: {
|
||||
text: constants.CLOSE,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
function onManageClick() {
|
||||
setIsOpen(false);
|
||||
galleryContext.showPlanSelectorModal();
|
||||
}
|
||||
|
||||
const Divider = () => (
|
||||
<div
|
||||
style={{
|
||||
height: '1px',
|
||||
marginTop: '40px',
|
||||
background: '#242424',
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<Menu
|
||||
isOpen={isOpen}
|
||||
onStateChange={(state) => setIsOpen(state.isOpen)}
|
||||
itemListElement="div">
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
outline: 'none',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
<LogoImage
|
||||
style={{ height: '24px', padding: '3px' }}
|
||||
alt="logo"
|
||||
src="/icon.svg"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
outline: 'none',
|
||||
color: 'rgb(45, 194, 98)',
|
||||
fontSize: '16px',
|
||||
}}>
|
||||
{user?.email}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
overflow: 'auto',
|
||||
outline: 'none',
|
||||
paddingTop: '0',
|
||||
}}>
|
||||
<div style={{ outline: 'none' }}>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<h5 style={{ margin: '4px 0 12px 2px' }}>
|
||||
{constants.SUBSCRIPTION_PLAN}
|
||||
</h5>
|
||||
</div>
|
||||
<div style={{ color: '#959595' }}>
|
||||
{isSubscriptionActive(subscription) ? (
|
||||
isOnFreePlan(subscription) ? (
|
||||
constants.FREE_SUBSCRIPTION_INFO(
|
||||
subscription?.expiryTime
|
||||
)
|
||||
) : isSubscriptionCancelled(subscription) ? (
|
||||
constants.RENEWAL_CANCELLED_SUBSCRIPTION_INFO(
|
||||
subscription?.expiryTime
|
||||
)
|
||||
) : (
|
||||
constants.RENEWAL_ACTIVE_SUBSCRIPTION_INFO(
|
||||
subscription?.expiryTime
|
||||
)
|
||||
)
|
||||
) : (
|
||||
<p>{constants.SUBSCRIPTION_EXPIRED}</p>
|
||||
)}
|
||||
<Button
|
||||
variant="outline-success"
|
||||
block
|
||||
size="sm"
|
||||
onClick={onManageClick}>
|
||||
{isSubscribed(subscription)
|
||||
? constants.MANAGE
|
||||
: constants.SUBSCRIBE}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ outline: 'none', marginTop: '30px' }} />
|
||||
<div>
|
||||
<h5 style={{ marginBottom: '12px' }}>
|
||||
{constants.USAGE_DETAILS}
|
||||
</h5>
|
||||
<div style={{ color: '#959595' }}>
|
||||
{usage ? (
|
||||
constants.USAGE_INFO(
|
||||
usage,
|
||||
convertToHumanReadable(subscription?.storage)
|
||||
)
|
||||
) : (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<EnteSpinner
|
||||
style={{
|
||||
borderWidth: '2px',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
galleryContext.setActiveCollection(ARCHIVE_SECTION);
|
||||
setIsOpen(false);
|
||||
}}>
|
||||
{constants.ARCHIVE}
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
galleryContext.setActiveCollection(TRASH_SECTION);
|
||||
setIsOpen(false);
|
||||
}}>
|
||||
{constants.TRASH}
|
||||
</LinkButton>
|
||||
<>
|
||||
<RecoveryKeyModal
|
||||
show={recoverModalView}
|
||||
onHide={() => setRecoveryModalView(false)}
|
||||
somethingWentWrong={() =>
|
||||
props.setDialogMessage({
|
||||
title: constants.ERROR,
|
||||
content:
|
||||
constants.RECOVER_KEY_GENERATION_FAILED,
|
||||
close: { variant: 'danger' },
|
||||
})
|
||||
}
|
||||
/>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => setRecoveryModalView(true)}>
|
||||
{constants.DOWNLOAD_RECOVERY_KEY}
|
||||
</LinkButton>
|
||||
</>
|
||||
<>
|
||||
<TwoFactorModal
|
||||
show={twoFactorModalView}
|
||||
onHide={() => setTwoFactorModalView(false)}
|
||||
setDialogMessage={props.setDialogMessage}
|
||||
closeSidebar={() => setIsOpen(false)}
|
||||
setLoading={props.setLoading}
|
||||
/>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => setTwoFactorModalView(true)}>
|
||||
{constants.TWO_FACTOR}
|
||||
</LinkButton>
|
||||
</>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
router.push(PAGES.CHANGE_PASSWORD);
|
||||
}}>
|
||||
{constants.CHANGE_PASSWORD}
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
router.push(PAGES.CHANGE_EMAIL);
|
||||
}}>
|
||||
{constants.UPDATE_EMAIL}
|
||||
</LinkButton>
|
||||
<Divider />
|
||||
<>
|
||||
<FixLargeThumbnails
|
||||
isOpen={fixLargeThumbsView}
|
||||
hide={() => setFixLargeThumbsView(false)}
|
||||
show={() => setFixLargeThumbsView(true)}
|
||||
/>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => setFixLargeThumbsView(true)}>
|
||||
{constants.FIX_LARGE_THUMBNAILS}
|
||||
</LinkButton>
|
||||
</>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={openFeedbackURL}>
|
||||
{constants.REQUEST_FEATURE}
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
if (!appContext.mlSearchEnabled) {
|
||||
if (!canEnableMlSearch()) {
|
||||
props.setDialogMessage({
|
||||
title: constants.ENABLE_ML_SEARCH,
|
||||
content: constants.ML_SEARCH_NOT_COMPATIBLE,
|
||||
close: { text: constants.OK },
|
||||
});
|
||||
return;
|
||||
}
|
||||
props.setDialogMessage({
|
||||
title: `${constants.CONFIRM} ${constants.ENABLE_ML_SEARCH}`,
|
||||
content: constants.ENABLE_ML_SEARCH_MESSAGE,
|
||||
staticBackdrop: true,
|
||||
proceed: {
|
||||
text: constants.ENABLE_ML_SEARCH,
|
||||
action: enableMlSearch,
|
||||
variant: 'success',
|
||||
},
|
||||
close: { text: constants.CANCEL },
|
||||
});
|
||||
} else {
|
||||
disableMlSearch();
|
||||
}
|
||||
}}>
|
||||
{appContext.mlSearchEnabled
|
||||
? constants.DISABLE_ML_SEARCH
|
||||
: constants.ENABLE_ML_SEARCH}
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
if (!appContext.mlSearchEnabled) {
|
||||
if (!canEnableMlSearch()) {
|
||||
props.setDialogMessage({
|
||||
title: constants.ENABLE_ML_SEARCH,
|
||||
content: constants.ML_SEARCH_NOT_COMPATIBLE,
|
||||
close: { text: constants.OK },
|
||||
});
|
||||
return;
|
||||
}
|
||||
props.setDialogMessage({
|
||||
title: 'clear mb db',
|
||||
content: 'clear mb db',
|
||||
staticBackdrop: true,
|
||||
proceed: {
|
||||
text: 'clear',
|
||||
action: clearMLDB,
|
||||
variant: 'success',
|
||||
},
|
||||
close: { text: constants.CANCEL },
|
||||
});
|
||||
} else {
|
||||
disableMlSearch();
|
||||
}
|
||||
}}>
|
||||
{'clear ML db'}
|
||||
</LinkButton>
|
||||
{appContext.mlSearchEnabled && (
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
router.push(PAGES.ML_DEBUG);
|
||||
}}>
|
||||
{constants.ML_DEBUG}
|
||||
</LinkButton>
|
||||
)}
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => initiateEmail('contact@ente.io')}>
|
||||
{constants.SUPPORT}
|
||||
</LinkButton>
|
||||
<>
|
||||
<ExportModal
|
||||
show={exportModalView}
|
||||
onHide={() => setExportModalView(false)}
|
||||
usage={usage}
|
||||
/>
|
||||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={exportFiles}>
|
||||
<div style={{ display: 'flex' }}>
|
||||
{constants.EXPORT}
|
||||
<div style={{ width: '20px' }} />
|
||||
{exportService.isExportInProgress() && (
|
||||
<InProgressIcon />
|
||||
)}
|
||||
</div>
|
||||
</LinkButton>
|
||||
</>
|
||||
<Divider />
|
||||
<LinkButton
|
||||
variant="danger"
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() =>
|
||||
props.setDialogMessage({
|
||||
title: `${constants.CONFIRM} ${constants.LOGOUT}`,
|
||||
content: constants.LOGOUT_MESSAGE,
|
||||
staticBackdrop: true,
|
||||
proceed: {
|
||||
text: constants.LOGOUT,
|
||||
action: logoutUser,
|
||||
variant: 'danger',
|
||||
},
|
||||
close: { text: constants.CANCEL },
|
||||
})
|
||||
}>
|
||||
{constants.LOGOUT}
|
||||
</LinkButton>
|
||||
<LinkButton
|
||||
variant="danger"
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() =>
|
||||
props.setDialogMessage({
|
||||
title: `${constants.DELETE_ACCOUNT}`,
|
||||
content: constants.DELETE_ACCOUNT_MESSAGE(),
|
||||
staticBackdrop: true,
|
||||
proceed: {
|
||||
text: constants.DELETE_ACCOUNT,
|
||||
action: () => {
|
||||
initiateEmail('account-deletion@ente.io');
|
||||
},
|
||||
variant: 'danger',
|
||||
},
|
||||
close: { text: constants.CANCEL },
|
||||
})
|
||||
}>
|
||||
{constants.DELETE_ACCOUNT}
|
||||
</LinkButton>
|
||||
<div
|
||||
style={{
|
||||
marginTop: '40px',
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Menu>
|
||||
);
|
||||
}
|
|
@ -1,73 +1,75 @@
|
|||
import CollectionShare from 'components/CollectionShare';
|
||||
import { SetDialogMessage } from 'components/MessageDialog';
|
||||
import NavigationButton, {
|
||||
SCROLL_DIRECTION,
|
||||
} from 'components/NavigationButton';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||
import { sortCollections } from 'services/collectionService';
|
||||
import { User } from 'types/user';
|
||||
// import CollectionShare from 'components/Collections/CollectionShare';
|
||||
// import { SetDialogMessage } from 'components/MessageDialog';
|
||||
// import NavigationButton, {
|
||||
// SCROLL_DIRECTION,
|
||||
// } from 'components/NavigationButton';
|
||||
// import React, { useEffect, useRef, useState } from 'react';
|
||||
// import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||
// import { sortCollections } from 'services/collectionService';
|
||||
// import { User } from 'types/user';
|
||||
import styled from 'styled-components';
|
||||
import { IMAGE_CONTAINER_MAX_WIDTH } from 'constants/gallery';
|
||||
import { Collection, CollectionAndItsLatestFile } from 'types/collection';
|
||||
import { getSelectedCollection } from 'utils/collection';
|
||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { SetCollectionNamerAttributes } from './CollectionNamer';
|
||||
import CollectionOptions from './CollectionOptions';
|
||||
import CollectionSort from './CollectionSort';
|
||||
import OptionIcon, { OptionIconWrapper } from './OptionIcon';
|
||||
import {
|
||||
ALL_SECTION,
|
||||
ARCHIVE_SECTION,
|
||||
CollectionType,
|
||||
COLLECTION_SORT_BY,
|
||||
TRASH_SECTION,
|
||||
} from 'constants/collection';
|
||||
// import { IMAGE_CONTAINER_MAX_WIDTH } from 'constants/gallery';
|
||||
// import { Collection, CollectionAndItsLatestFile } from 'types/collection';
|
||||
// import { getSelectedCollection } from 'utils/collection';
|
||||
// import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||
// import constants from 'utils/strings/constants';
|
||||
// import { SetCollectionNamerAttributes } from './CollectionNamer';
|
||||
// import CollectionOptions from './CollectionOptions';
|
||||
// import CollectionSort from './CollectionSort';
|
||||
// import OptionIcon, { OptionIconWrapper } from './OptionIcon';
|
||||
// import {
|
||||
// ALL_SECTION,
|
||||
// ARCHIVE_SECTION,
|
||||
// CollectionType,
|
||||
// COLLECTION_SORT_BY,
|
||||
// TRASH_SECTION,
|
||||
// } from 'constants/collection';
|
||||
|
||||
interface CollectionProps {
|
||||
collections: Collection[];
|
||||
collectionAndTheirLatestFile: CollectionAndItsLatestFile[];
|
||||
activeCollection?: number;
|
||||
setActiveCollection: (id?: number) => void;
|
||||
setDialogMessage: SetDialogMessage;
|
||||
syncWithRemote: () => Promise<void>;
|
||||
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||
startLoading: () => void;
|
||||
finishLoading: () => void;
|
||||
isInSearchMode: boolean;
|
||||
collectionFilesCount: Map<number, number>;
|
||||
}
|
||||
import { OptionIconWrapper } from './OptionIcon';
|
||||
|
||||
const CollectionContainer = styled.div`
|
||||
overflow-y: hidden;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
width: calc(100% - 80px);
|
||||
position: relative;
|
||||
padding: 0 24px;
|
||||
// interface CollectionProps {
|
||||
// collections: Collection[];
|
||||
// collectionAndTheirLatestFile: CollectionAndItsLatestFile[];
|
||||
// activeCollection?: number;
|
||||
// setActiveCollection: (id?: number) => void;
|
||||
// setDialogMessage: SetDialogMessage;
|
||||
// syncWithRemote: () => Promise<void>;
|
||||
// setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||
// startLoading: () => void;
|
||||
// finishLoading: () => void;
|
||||
// isInSearchMode: boolean;
|
||||
// collectionFilesCount: Map<number, number>;
|
||||
// }
|
||||
|
||||
@media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
|
||||
padding: 0 4px;
|
||||
}
|
||||
`;
|
||||
// const CollectionContainer = styled.div`
|
||||
// overflow-y: hidden;
|
||||
// height: 40px;
|
||||
// display: flex;
|
||||
// width: calc(100% - 80px);
|
||||
// position: relative;
|
||||
// padding: 0 24px;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
height: 70px;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
max-width: 100%;
|
||||
scroll-behavior: smooth;
|
||||
`;
|
||||
// @media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
|
||||
// padding: 0 4px;
|
||||
// }
|
||||
// `;
|
||||
|
||||
const CollectionBar = styled.div`
|
||||
width: 100%;
|
||||
margin: 10px auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
// const Wrapper = styled.div`
|
||||
// height: 70px;
|
||||
// flex: 1;
|
||||
// white-space: nowrap;
|
||||
// overflow: auto;
|
||||
// max-width: 100%;
|
||||
// scroll-behavior: smooth;
|
||||
// `;
|
||||
|
||||
// const CollectionBar = styled.div`
|
||||
// width: 100%;
|
||||
// margin: 10px auto;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: flex-start;
|
||||
// `;
|
||||
|
||||
export const Chip = styled.button<{ active: boolean }>`
|
||||
border-radius: 8px;
|
||||
|
@ -87,196 +89,196 @@ export const Chip = styled.button<{ active: boolean }>`
|
|||
}
|
||||
`;
|
||||
|
||||
const SectionChipCreater =
|
||||
({ activeCollection, clickHandler }) =>
|
||||
({ section, label }) =>
|
||||
(
|
||||
<Chip
|
||||
active={activeCollection === section}
|
||||
onClick={clickHandler(section)}>
|
||||
{label}
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '24px',
|
||||
}}
|
||||
/>
|
||||
</Chip>
|
||||
);
|
||||
const Hider = styled.div<{ hide: boolean }>`
|
||||
opacity: ${(props) => (props.hide ? '0' : '100')};
|
||||
height: ${(props) => (props.hide ? '0' : 'auto')};
|
||||
`;
|
||||
// const SectionChipCreater =
|
||||
// ({ activeCollection, clickHandler }) =>
|
||||
// ({ section, label }) =>
|
||||
// (
|
||||
// <Chip
|
||||
// active={activeCollection === section}
|
||||
// onClick={clickHandler(section)}>
|
||||
// {label}
|
||||
// <div
|
||||
// style={{
|
||||
// display: 'inline-block',
|
||||
// width: '24px',
|
||||
// }}
|
||||
// />
|
||||
// </Chip>
|
||||
// );
|
||||
// const Hider = styled.div<{ hide: boolean }>`
|
||||
// opacity: ${(props) => (props.hide ? '0' : '100')};
|
||||
// height: ${(props) => (props.hide ? '0' : 'auto')};
|
||||
// `;
|
||||
|
||||
export default function Collections(props: CollectionProps) {
|
||||
const { activeCollection, collections, setActiveCollection } = props;
|
||||
const [selectedCollectionID, setSelectedCollectionID] =
|
||||
useState<number>(null);
|
||||
const collectionWrapperRef = useRef<HTMLDivElement>(null);
|
||||
const collectionChipsRef = props.collections.reduce(
|
||||
(refMap, collection) => {
|
||||
refMap[collection.id] = React.createRef();
|
||||
return refMap;
|
||||
},
|
||||
{}
|
||||
);
|
||||
const [collectionShareModalView, setCollectionShareModalView] =
|
||||
useState(false);
|
||||
const [scrollObj, setScrollObj] = useState<{
|
||||
scrollLeft?: number;
|
||||
scrollWidth?: number;
|
||||
clientWidth?: number;
|
||||
}>({});
|
||||
const [collectionSortBy, setCollectionSortBy] =
|
||||
useState<COLLECTION_SORT_BY>(COLLECTION_SORT_BY.LATEST_FILE);
|
||||
// export default function Collections(props: CollectionProps) {
|
||||
// const { activeCollection, collections, setActiveCollection } = props;
|
||||
// const [selectedCollectionID, setSelectedCollectionID] =
|
||||
// useState<number>(null);
|
||||
// const collectionWrapperRef = useRef<HTMLDivElement>(null);
|
||||
// const collectionChipsRef = props.collections.reduce(
|
||||
// (refMap, collection) => {
|
||||
// refMap[collection.id] = React.createRef();
|
||||
// return refMap;
|
||||
// },
|
||||
// {}
|
||||
// );
|
||||
// const [collectionShareModalView, setCollectionShareModalView] =
|
||||
// useState(false);
|
||||
// const [scrollObj, setScrollObj] = useState<{
|
||||
// scrollLeft?: number;
|
||||
// scrollWidth?: number;
|
||||
// clientWidth?: number;
|
||||
// }>({});
|
||||
// const [collectionSortBy, setCollectionSortBy] =
|
||||
// useState<COLLECTION_SORT_BY>(COLLECTION_SORT_BY.LATEST_FILE);
|
||||
|
||||
const updateScrollObj = () => {
|
||||
if (collectionWrapperRef.current) {
|
||||
const { scrollLeft, scrollWidth, clientWidth } =
|
||||
collectionWrapperRef.current;
|
||||
setScrollObj({ scrollLeft, scrollWidth, clientWidth });
|
||||
}
|
||||
};
|
||||
// const updateScrollObj = () => {
|
||||
// if (collectionWrapperRef.current) {
|
||||
// const { scrollLeft, scrollWidth, clientWidth } =
|
||||
// collectionWrapperRef.current;
|
||||
// setScrollObj({ scrollLeft, scrollWidth, clientWidth });
|
||||
// }
|
||||
// };
|
||||
|
||||
useEffect(() => {
|
||||
updateScrollObj();
|
||||
}, [collectionWrapperRef.current, props.isInSearchMode, collections]);
|
||||
// useEffect(() => {
|
||||
// updateScrollObj();
|
||||
// }, [collectionWrapperRef.current, props.isInSearchMode, collections]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!collectionWrapperRef?.current) {
|
||||
return;
|
||||
}
|
||||
collectionWrapperRef.current.scrollLeft = 0;
|
||||
}, [collections]);
|
||||
// useEffect(() => {
|
||||
// if (!collectionWrapperRef?.current) {
|
||||
// return;
|
||||
// }
|
||||
// collectionWrapperRef.current.scrollLeft = 0;
|
||||
// }, [collections]);
|
||||
|
||||
useEffect(() => {
|
||||
collectionChipsRef[activeCollection]?.current.scrollIntoView({
|
||||
inline: 'center',
|
||||
});
|
||||
}, [activeCollection]);
|
||||
// useEffect(() => {
|
||||
// collectionChipsRef[activeCollection]?.current.scrollIntoView({
|
||||
// inline: 'center',
|
||||
// });
|
||||
// }, [activeCollection]);
|
||||
|
||||
const clickHandler = (collectionID?: number) => () => {
|
||||
setSelectedCollectionID(collectionID);
|
||||
setActiveCollection(collectionID ?? ALL_SECTION);
|
||||
};
|
||||
// const clickHandler = (collectionID?: number) => () => {
|
||||
// setSelectedCollectionID(collectionID);
|
||||
// setActiveCollection(collectionID ?? ALL_SECTION);
|
||||
// };
|
||||
|
||||
const user: User = getData(LS_KEYS.USER);
|
||||
// const user: User = getData(LS_KEYS.USER);
|
||||
|
||||
const collectionOptions = CollectionOptions({
|
||||
syncWithRemote: props.syncWithRemote,
|
||||
setCollectionNamerAttributes: props.setCollectionNamerAttributes,
|
||||
collections: props.collections,
|
||||
selectedCollectionID,
|
||||
setDialogMessage: props.setDialogMessage,
|
||||
startLoading: props.startLoading,
|
||||
finishLoading: props.finishLoading,
|
||||
showCollectionShareModal: setCollectionShareModalView.bind(null, true),
|
||||
redirectToAll: setActiveCollection.bind(null, ALL_SECTION),
|
||||
});
|
||||
// const collectionOptions = CollectionOptions({
|
||||
// syncWithRemote: props.syncWithRemote,
|
||||
// setCollectionNamerAttributes: props.setCollectionNamerAttributes,
|
||||
// collections: props.collections,
|
||||
// selectedCollectionID,
|
||||
// setDialogMessage: props.setDialogMessage,
|
||||
// startLoading: props.startLoading,
|
||||
// finishLoading: props.finishLoading,
|
||||
// showCollectionShareModal: setCollectionShareModalView.bind(null, true),
|
||||
// redirectToAll: setActiveCollection.bind(null, ALL_SECTION),
|
||||
// });
|
||||
|
||||
const scrollCollection = (direction: SCROLL_DIRECTION) => () => {
|
||||
collectionWrapperRef.current.scrollBy(250 * direction, 0);
|
||||
};
|
||||
const renderTooltip = (collectionID: number) => {
|
||||
const fileCount = props.collectionFilesCount?.get(collectionID) ?? 0;
|
||||
return (
|
||||
<Tooltip id="button-tooltip">
|
||||
{fileCount} {fileCount > 1 ? 'items' : 'item'}
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
// const scrollCollection = (direction: SCROLL_DIRECTION) => () => {
|
||||
// collectionWrapperRef.current.scrollBy(250 * direction, 0);
|
||||
// };
|
||||
// const renderTooltip = (collectionID: number) => {
|
||||
// const fileCount = props.collectionFilesCount?.get(collectionID) ?? 0;
|
||||
// return (
|
||||
// <Tooltip id="button-tooltip">
|
||||
// {fileCount} {fileCount > 1 ? 'items' : 'item'}
|
||||
// </Tooltip>
|
||||
// );
|
||||
// };
|
||||
|
||||
const SectionChip = SectionChipCreater({ activeCollection, clickHandler });
|
||||
// const SectionChip = SectionChipCreater({ activeCollection, clickHandler });
|
||||
|
||||
return (
|
||||
<Hider hide={props.isInSearchMode}>
|
||||
<CollectionShare
|
||||
show={collectionShareModalView}
|
||||
onHide={() => setCollectionShareModalView(false)}
|
||||
collection={getSelectedCollection(
|
||||
selectedCollectionID,
|
||||
props.collections
|
||||
)}
|
||||
syncWithRemote={props.syncWithRemote}
|
||||
/>
|
||||
<CollectionBar>
|
||||
<CollectionContainer>
|
||||
{scrollObj.scrollLeft > 0 && (
|
||||
<NavigationButton
|
||||
scrollDirection={SCROLL_DIRECTION.LEFT}
|
||||
onClick={scrollCollection(SCROLL_DIRECTION.LEFT)}
|
||||
/>
|
||||
)}
|
||||
<Wrapper
|
||||
ref={collectionWrapperRef}
|
||||
onScroll={updateScrollObj}>
|
||||
<SectionChip
|
||||
section={ALL_SECTION}
|
||||
label={constants.ALL}
|
||||
/>
|
||||
{sortCollections(
|
||||
collections,
|
||||
props.collectionAndTheirLatestFile,
|
||||
collectionSortBy
|
||||
).map((item) => (
|
||||
<OverlayTrigger
|
||||
key={item.id}
|
||||
placement="top"
|
||||
delay={{ show: 250, hide: 400 }}
|
||||
overlay={renderTooltip(item.id)}>
|
||||
<Chip
|
||||
ref={collectionChipsRef[item.id]}
|
||||
active={activeCollection === item.id}
|
||||
onClick={clickHandler(item.id)}>
|
||||
{item.name}
|
||||
{item.type !== CollectionType.favorites &&
|
||||
item.owner.id === user?.id ? (
|
||||
<OverlayTrigger
|
||||
rootClose
|
||||
trigger="click"
|
||||
placement="bottom"
|
||||
overlay={collectionOptions}>
|
||||
<OptionIcon
|
||||
onClick={() =>
|
||||
setSelectedCollectionID(
|
||||
item.id
|
||||
)
|
||||
}
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '24px',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Chip>
|
||||
</OverlayTrigger>
|
||||
))}
|
||||
<SectionChip
|
||||
section={ARCHIVE_SECTION}
|
||||
label={constants.ARCHIVE}
|
||||
/>
|
||||
<SectionChip
|
||||
section={TRASH_SECTION}
|
||||
label={constants.TRASH}
|
||||
/>
|
||||
</Wrapper>
|
||||
{scrollObj.scrollLeft <
|
||||
scrollObj.scrollWidth - scrollObj.clientWidth && (
|
||||
<NavigationButton
|
||||
scrollDirection={SCROLL_DIRECTION.RIGHT}
|
||||
onClick={scrollCollection(SCROLL_DIRECTION.RIGHT)}
|
||||
/>
|
||||
)}
|
||||
</CollectionContainer>
|
||||
<CollectionSort
|
||||
setCollectionSortBy={setCollectionSortBy}
|
||||
activeSortBy={collectionSortBy}
|
||||
/>
|
||||
</CollectionBar>
|
||||
</Hider>
|
||||
);
|
||||
}
|
||||
// return (
|
||||
// <Hider hide={props.isInSearchMode}>
|
||||
// <CollectionShare
|
||||
// show={collectionShareModalView}
|
||||
// onHide={() => setCollectionShareModalView(false)}
|
||||
// collection={getSelectedCollection(
|
||||
// selectedCollectionID,
|
||||
// props.collections
|
||||
// )}
|
||||
// syncWithRemote={props.syncWithRemote}
|
||||
// />
|
||||
// <CollectionBar>
|
||||
// <CollectionContainer>
|
||||
// {scrollObj.scrollLeft > 0 && (
|
||||
// <NavigationButton
|
||||
// scrollDirection={SCROLL_DIRECTION.LEFT}
|
||||
// onClick={scrollCollection(SCROLL_DIRECTION.LEFT)}
|
||||
// />
|
||||
// )}
|
||||
// <Wrapper
|
||||
// ref={collectionWrapperRef}
|
||||
// onScroll={updateScrollObj}>
|
||||
// <SectionChip
|
||||
// section={ALL_SECTION}
|
||||
// label={constants.ALL}
|
||||
// />
|
||||
// {sortCollections(
|
||||
// collections,
|
||||
// props.collectionAndTheirLatestFile,
|
||||
// collectionSortBy
|
||||
// ).map((item) => (
|
||||
// <OverlayTrigger
|
||||
// key={item.id}
|
||||
// placement="top"
|
||||
// delay={{ show: 250, hide: 400 }}
|
||||
// overlay={renderTooltip(item.id)}>
|
||||
// <Chip
|
||||
// ref={collectionChipsRef[item.id]}
|
||||
// active={activeCollection === item.id}
|
||||
// onClick={clickHandler(item.id)}>
|
||||
// {item.name}
|
||||
// {item.type !== CollectionType.favorites &&
|
||||
// item.owner.id === user?.id ? (
|
||||
// <OverlayTrigger
|
||||
// rootClose
|
||||
// trigger="click"
|
||||
// placement="bottom"
|
||||
// overlay={collectionOptions}>
|
||||
// <OptionIcon
|
||||
// onClick={() =>
|
||||
// setSelectedCollectionID(
|
||||
// item.id
|
||||
// )
|
||||
// }
|
||||
// />
|
||||
// </OverlayTrigger>
|
||||
// ) : (
|
||||
// <div
|
||||
// style={{
|
||||
// display: 'inline-block',
|
||||
// width: '24px',
|
||||
// }}
|
||||
// />
|
||||
// )}
|
||||
// </Chip>
|
||||
// </OverlayTrigger>
|
||||
// ))}
|
||||
// <SectionChip
|
||||
// section={ARCHIVE_SECTION}
|
||||
// label={constants.ARCHIVE}
|
||||
// />
|
||||
// <SectionChip
|
||||
// section={TRASH_SECTION}
|
||||
// label={constants.TRASH}
|
||||
// />
|
||||
// </Wrapper>
|
||||
// {scrollObj.scrollLeft <
|
||||
// scrollObj.scrollWidth - scrollObj.clientWidth && (
|
||||
// <NavigationButton
|
||||
// scrollDirection={SCROLL_DIRECTION.RIGHT}
|
||||
// onClick={scrollCollection(SCROLL_DIRECTION.RIGHT)}
|
||||
// />
|
||||
// )}
|
||||
// </CollectionContainer>
|
||||
// <CollectionSort
|
||||
// setCollectionSortBy={setCollectionSortBy}
|
||||
// activeSortBy={collectionSortBy}
|
||||
// />
|
||||
// </CollectionBar>
|
||||
// </Hider>
|
||||
// );
|
||||
// }
|
||||
|
|
|
@ -23,7 +23,8 @@ import {
|
|||
SceneDetectionMethod,
|
||||
} from 'types/machineLearning';
|
||||
import { CONCURRENCY } from 'utils/common/concurrency';
|
||||
import { ComlinkWorker, getDedicatedCryptoWorker } from 'utils/crypto';
|
||||
import { getDedicatedCryptoWorker } from 'utils/crypto';
|
||||
import { ComlinkWorker } from 'utils/comlink';
|
||||
import { logQueueStats } from 'utils/machineLearning';
|
||||
import arcfaceAlignmentService from './arcfaceAlignmentService';
|
||||
import arcfaceCropService from './arcfaceCropService';
|
||||
|
|
|
@ -21,8 +21,7 @@ class ReaderService {
|
|||
}
|
||||
fileContext.imageBitmap = await getLocalFileImageBitmap(
|
||||
fileContext.enteFile,
|
||||
fileContext.localFile,
|
||||
() => syncContext.getEnteWorker(fileContext.enteFile.id)
|
||||
fileContext.localFile
|
||||
);
|
||||
} else if (
|
||||
syncContext.config.imageSource === 'Original' &&
|
||||
|
|
|
@ -8,7 +8,7 @@ import Tesseract, { createWorker } from 'tesseract.js';
|
|||
import QueueProcessor from 'services/queueProcessor';
|
||||
import { CustomError } from 'utils/error';
|
||||
import { imageBitmapToBlob, resizeToSquare } from 'utils/image';
|
||||
import { getFileType } from 'services/upload/readFileService';
|
||||
import { getFileType } from 'services/typeDetectionService';
|
||||
import { FILE_TYPE } from 'constants/file';
|
||||
import { makeID } from 'utils/user';
|
||||
import {
|
||||
|
@ -129,7 +129,7 @@ class TesseractService implements TextDetectionService {
|
|||
[await imageBitmapToBlob(imageBitmap)],
|
||||
'text-detection-dummy-image'
|
||||
);
|
||||
const fileTypeInfo = await getFileType(new FileReader(), file);
|
||||
const fileTypeInfo = await getFileType(file);
|
||||
|
||||
if (
|
||||
fileTypeInfo.fileType !== FILE_TYPE.IMAGE &&
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Person, Thing } from 'types/machineLearning';
|
||||
import { Person, Thing, ThingClass, WordGroup } from 'types/machineLearning';
|
||||
import { IndexStatus } from 'types/machineLearning/ui';
|
||||
import { EnteFile } from 'types/file';
|
||||
|
||||
|
@ -39,6 +39,9 @@ export type Search = {
|
|||
location?: Bbox;
|
||||
collection?: number;
|
||||
file?: number;
|
||||
person?: Person;
|
||||
thing?: ThingClass;
|
||||
text?: WordGroup;
|
||||
};
|
||||
|
||||
export type SearchResultSummary = {
|
||||
|
|
|
@ -355,7 +355,7 @@ async function getOriginalConvertedFile(
|
|||
) {
|
||||
let fileBlob = await getOriginalImageFile(file, token, enteWorker, queue);
|
||||
if (needsConversionForPreview(file)) {
|
||||
fileBlob = await convertForPreview(file, fileBlob, enteWorker);
|
||||
fileBlob = await convertForPreview(file, fileBlob)[0];
|
||||
}
|
||||
|
||||
return fileBlob;
|
||||
|
@ -402,13 +402,11 @@ export async function getThumbnailImageBitmap(file: EnteFile, token: string) {
|
|||
|
||||
export async function getLocalFileImageBitmap(
|
||||
enteFile: EnteFile,
|
||||
localFile: globalThis.File,
|
||||
enteWorkerProvider?: () => Promise<any>
|
||||
localFile: globalThis.File
|
||||
) {
|
||||
let fileBlob = localFile as Blob;
|
||||
if (needsConversionForPreview(enteFile)) {
|
||||
const enteWorker = await enteWorkerProvider();
|
||||
fileBlob = await convertForPreview(enteFile, fileBlob, enteWorker);
|
||||
fileBlob = await convertForPreview(enteFile, fileBlob)[0];
|
||||
}
|
||||
return getImageBlobBitmap(fileBlob);
|
||||
}
|
||||
|
|
83
yarn.lock
83
yarn.lock
|
@ -2294,6 +2294,16 @@ bluebird@^3.5.5:
|
|||
resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz"
|
||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
||||
|
||||
blueimp-load-image@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/blueimp-load-image/-/blueimp-load-image-3.0.0.tgz#d71c39440a7d2f1a83e3e86a625e329116a51705"
|
||||
integrity sha512-Q9rFbd4ZUNvzSFmRXx9MoG0RwWwJeMjjEUbG7WIOJgUg22Jgkow0wL5b35B6qwiBscxACW9OHdrP5s2vQ3x8DQ==
|
||||
|
||||
bmp-js@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233"
|
||||
integrity sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==
|
||||
|
||||
body-parser@1.19.0:
|
||||
version "1.19.0"
|
||||
resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz"
|
||||
|
@ -2532,6 +2542,11 @@ colorette@^1.2.2, colorette@^1.3.0:
|
|||
resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz"
|
||||
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
|
||||
|
||||
colors@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
|
||||
|
||||
comlink@^4.3.0:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.npmjs.org/comlink/-/comlink-4.3.1.tgz"
|
||||
|
@ -3495,6 +3510,11 @@ file-selector@^0.2.2:
|
|||
dependencies:
|
||||
tslib "^2.0.3"
|
||||
|
||||
file-type@^12.4.1:
|
||||
version "12.4.2"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-12.4.2.tgz#a344ea5664a1d01447ee7fb1b635f72feb6169d9"
|
||||
integrity sha512-UssQP5ZgIOKelfsaB5CuGAL+Y+q7EmONuiwF3N5HAH0t27rvrttgi6Ra9k/+DVaY9UF6+ybxu5pOXLUdA8N7Vg==
|
||||
|
||||
file-type@^16.5.4:
|
||||
version "16.5.4"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-16.5.4.tgz#474fb4f704bee427681f98dd390058a172a6c2fd"
|
||||
|
@ -3901,6 +3921,11 @@ iconv-lite@0.4.24:
|
|||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
idb-keyval@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-3.2.0.tgz#cbbf354deb5684b6cdc84376294fc05932845bd6"
|
||||
integrity sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ==
|
||||
|
||||
idb@^6.0.0:
|
||||
version "6.1.3"
|
||||
resolved "https://registry.npmjs.org/idb/-/idb-6.1.3.tgz"
|
||||
|
@ -4216,7 +4241,18 @@ jest-worker@^24.9.0:
|
|||
merge-stream "^2.0.0"
|
||||
supports-color "^6.1.0"
|
||||
|
||||
jpeg-js@^0.4.1:
|
||||
jpeg-autorotate@^7.1.1:
|
||||
version "7.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jpeg-autorotate/-/jpeg-autorotate-7.1.1.tgz#c57905c6afd3b54373a6a1d0249ed6e07f7b043b"
|
||||
integrity sha512-ewTZTG/QWOM0D5h/yKcQ3QgyrnQYsr3qmcS+bqoAwgQAY1KBa31aJ+q+FlElaxo/rSYqfF1ixf+8EIgluBkgTg==
|
||||
dependencies:
|
||||
colors "^1.4.0"
|
||||
glob "^7.1.6"
|
||||
jpeg-js "^0.4.2"
|
||||
piexifjs "^1.0.6"
|
||||
yargs-parser "^20.2.1"
|
||||
|
||||
jpeg-js@^0.4.1, jpeg-js@^0.4.2:
|
||||
version "0.4.4"
|
||||
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
|
||||
integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
|
||||
|
@ -4904,6 +4940,11 @@ onetime@^5.1.0, onetime@^5.1.2:
|
|||
dependencies:
|
||||
mimic-fn "^2.1.0"
|
||||
|
||||
opencollective-postinstall@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259"
|
||||
integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==
|
||||
|
||||
opener@^1.5.1:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz"
|
||||
|
@ -5059,7 +5100,7 @@ peek-readable@^4.0.1:
|
|||
integrity sha512-7qmhptnR0WMSpxT5rMHG9bW/mYSR1uqaPFj2MHvT+y/aOUu6msJijpKt5SkTDKySwg65OWG2JwTMBlgcbwMHrQ==
|
||||
|
||||
"photoswipe@file:./thirdparty/photoswipe":
|
||||
version "4.1.4"
|
||||
version "4.1.6"
|
||||
|
||||
picocolors@^1.0.0:
|
||||
version "1.0.0"
|
||||
|
@ -5531,7 +5572,7 @@ regenerator-runtime@^0.11.0:
|
|||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz"
|
||||
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
|
||||
|
||||
regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
|
||||
regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7:
|
||||
version "0.13.9"
|
||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
|
||||
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
|
||||
|
@ -5600,6 +5641,11 @@ resolve-from@^4.0.0:
|
|||
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
|
||||
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
|
||||
|
||||
resolve-url@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||
integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==
|
||||
|
||||
resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz"
|
||||
|
@ -6243,8 +6289,27 @@ terser@^4.6.2:
|
|||
source-map "~0.6.1"
|
||||
source-map-support "~0.5.12"
|
||||
|
||||
tesseract.js-core@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tesseract.js-core/-/tesseract.js-core-2.2.0.tgz#6ef78051272a381969fac3e45a226e85022cffef"
|
||||
integrity sha512-a8L+OJTbUipBsEDsJhDPlnLB0TY1MkTZqw5dqUwmiDSjUzwvU7HWLg/2+WDRulKUi4LE+7PnHlaBlW0k+V0U0w==
|
||||
|
||||
"tesseract.js@file:./thirdparty/tesseract":
|
||||
version "0.0.0"
|
||||
version "2.1.5"
|
||||
dependencies:
|
||||
blueimp-load-image "^3.0.0"
|
||||
bmp-js "^0.1.0"
|
||||
file-type "^12.4.1"
|
||||
idb-keyval "^3.2.0"
|
||||
is-electron "^2.2.0"
|
||||
is-url "^1.2.4"
|
||||
jpeg-autorotate "^7.1.1"
|
||||
node-fetch "^2.6.0"
|
||||
opencollective-postinstall "^2.0.2"
|
||||
regenerator-runtime "^0.13.3"
|
||||
resolve-url "^0.2.1"
|
||||
tesseract.js-core "^2.2.0"
|
||||
zlibjs "^0.3.1"
|
||||
|
||||
text-table@^0.2.0:
|
||||
version "0.2.0"
|
||||
|
@ -6888,6 +6953,11 @@ yaml@^1.10.0:
|
|||
resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yargs-parser@^20.2.1:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs@~3.10.0:
|
||||
version "3.10.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
|
||||
|
@ -6910,3 +6980,8 @@ yup@^0.29.3:
|
|||
property-expr "^2.0.2"
|
||||
synchronous-promise "^2.0.13"
|
||||
toposort "^2.0.2"
|
||||
|
||||
zlibjs@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/zlibjs/-/zlibjs-0.3.1.tgz#50197edb28a1c42ca659cc8b4e6a9ddd6d444554"
|
||||
integrity sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==
|
||||
|
|
Loading…
Add table
Reference in a new issue