import React, { useEffect, useRef, useState } from 'react'; import { useRouter } from 'next/router'; import { clearKeys, getKey, SESSION_KEYS } from 'utils/storage/sessionStorage'; import { File, getLocalFiles, deleteFiles, syncFiles, } from 'services/fileService'; import styled from 'styled-components'; import LoadingBar from 'react-top-loading-bar'; import Collections from './components/Collections'; import Upload from './components/Upload'; import { Collection, syncCollections, CollectionAndItsLatestFile, getCollectionsAndTheirLatestFile, getFavItemIds, getLocalCollections, getNonEmptyCollections, } from 'services/collectionService'; import constants from 'utils/strings/constants'; import { Alert } from 'react-bootstrap'; import billingService from 'services/billingService'; import PlanSelector from './components/PlanSelector'; import { checkSubscriptionPurchase } from 'utils/billingUtil'; import FullScreenDropZone from 'components/FullScreenDropZone'; import Sidebar from 'components/Sidebar'; import UploadButton from './components/UploadButton'; import { checkConnectivity } from 'utils/common'; import { isFirstLogin, justSignedUp, setIsFirstLogin, setJustSignedUp, } from 'utils/storage'; import { isTokenValid, logoutUser } from 'services/userService'; import AlertBanner from './components/AlertBanner'; import MessageDialog, { MessageAttributes } from 'components/MessageDialog'; import { useDropzone } from 'react-dropzone'; import EnteSpinner from 'components/EnteSpinner'; import CollectionNamer, { CollectionNamerAttributes, } from './components/CollectionNamer'; import CollectionSelector, { CollectionSelectorAttributes, } from './components/CollectionSelector'; import { LoadingOverlay } from 'components/LoadingOverlay'; import PhotoFrame from 'components/PhotoFrame'; import { getSelectedFileIds } from 'utils/file'; import { addFilesToCollection } from 'utils/collection'; import SelectedFileOptions from './components/SelectedFileOptions'; import { errorCodes } from 'utils/common/errorUtil'; import SearchBar from 'components/SearchBar'; export enum FILE_TYPE { IMAGE, VIDEO, OTHERS, } export const DeadCenter = styled.div` flex: 1; display: flex; justify-content: center; align-items: center; color: #fff; text-align: center; flex-direction: column; `; export type selectedState = { [k: number]: boolean; count: number; }; export type SetFiles = React.Dispatch>; export type SetCollections = React.Dispatch>; export type SetLoading = React.Dispatch>; export default function Gallery() { const router = useRouter(); const [collections, setCollections] = useState([]); const [collectionsAndTheirLatestFile, setCollectionsAndTheirLatestFile] = useState([]); const [files, setFiles] = useState(null); const [favItemIds, setFavItemIds] = useState>(); const [bannerMessage, setBannerMessage] = useState(null); const [sinceTime, setSinceTime] = useState(0); const [isFirstLoad, setIsFirstLoad] = useState(false); const [isFirstFetch, setIsFirstFetch] = useState(false); const [selected, setSelected] = useState({ count: 0 }); const [dialogMessage, setDialogMessage] = useState(); const [dialogView, setDialogView] = useState(false); const [planModalView, setPlanModalView] = useState(false); const [loading, setLoading] = useState(false); const [collectionSelectorAttributes, setCollectionSelectorAttributes] = useState(null); const [collectionSelectorView, setCollectionSelectorView] = useState(false); const [collectionNamerAttributes, setCollectionNamerAttributes] = useState(null); const [collectionNamerView, setCollectionNamerView] = useState(false); const { getRootProps, getInputProps, open: openFileUploader, acceptedFiles, } = useDropzone({ noClick: true, noKeyboard: true, accept: 'image/*, video/*, application/json, ', }); const loadingBar = useRef(null); const [searchMode, setSearchMode] = useState(false); useEffect(() => { const key = getKey(SESSION_KEYS.ENCRYPTION_KEY); if (!key) { router.push('/'); return; } const main = async () => { setIsFirstLoad(isFirstLogin()); setIsFirstFetch(false); if (justSignedUp()) { setPlanModalView(true); } setIsFirstLogin(false); const files = await getLocalFiles(); const collections = await getLocalCollections(); const nonEmptyCollections = getNonEmptyCollections( collections, files ); const collectionsAndTheirLatestFile = await getCollectionsAndTheirLatestFile( nonEmptyCollections, files ); setFiles(files); setCollections(nonEmptyCollections); setCollectionsAndTheirLatestFile(collectionsAndTheirLatestFile); const favItemIds = await getFavItemIds(files); setFavItemIds(favItemIds); await checkSubscriptionPurchase(setDialogMessage, router); await syncWithRemote(); setIsFirstLoad(false); setJustSignedUp(false); setIsFirstFetch(false); }; main(); }, []); useEffect(() => setDialogView(true), [dialogMessage]); useEffect( () => setCollectionSelectorView(true), [collectionSelectorAttributes] ); useEffect(() => setCollectionNamerView(true), [collectionNamerAttributes]); const syncWithRemote = async () => { try { checkConnectivity(); if (!(await isTokenValid())) { throw new Error(errorCodes.ERR_SESSION_EXPIRED); } loadingBar.current?.continuousStart(); await billingService.updatePlans(); await billingService.syncSubscription(); const collections = await syncCollections(); const { files, isUpdated } = await syncFiles(collections); const nonEmptyCollections = getNonEmptyCollections( collections, files ); const collectionAndItsLatestFile = await getCollectionsAndTheirLatestFile( nonEmptyCollections, files ); const favItemIds = await getFavItemIds(files); setCollections(nonEmptyCollections); if (isUpdated) { setFiles(files); } setCollectionsAndTheirLatestFile(collectionAndItsLatestFile); setFavItemIds(favItemIds); setSinceTime(new Date().getTime()); } catch (e) { switch (e.message) { case errorCodes.ERR_SESSION_EXPIRED: setBannerMessage(constants.SESSION_EXPIRED_MESSAGE); setDialogMessage({ title: constants.SESSION_EXPIRED, content: constants.SESSION_EXPIRED_MESSAGE, staticBackdrop: true, proceed: { text: constants.LOGIN, action: logoutUser, variant: 'primary', }, nonClosable: true, }); break; case errorCodes.ERR_NO_INTERNET_CONNECTION: setBannerMessage(constants.NO_INTERNET_CONNECTION); break; case errorCodes.ERR_KEY_MISSING: clearKeys(); router.push('/credentials'); break; } } finally { loadingBar.current?.complete(); } }; const clearSelection = function () { setSelected({ count: 0 }); }; const selectCollection = (id?: number) => { const href = `/gallery?collection=${id || ''}`; router.push(href, undefined, { shallow: true }); }; if (!files) { return
; } const addToCollectionHelper = ( collectionName: string, collection: Collection ) => { loadingBar.current?.continuousStart(); addFilesToCollection( setCollectionSelectorView, selected, files, clearSelection, syncWithRemote, selectCollection, collectionName, collection ); }; const showCreateCollectionModal = () => setCollectionNamerAttributes({ title: constants.CREATE_COLLECTION, buttonText: constants.CREATE, autoFilledName: '', callback: (collectionName) => addToCollectionHelper(collectionName, null), }); const deleteFileHelper = () => { loadingBar.current?.continuousStart(); deleteFiles( getSelectedFileIds(selected), clearSelection, syncWithRemote ); }; const updateFiles = (files: File[]) => { setFiles(files); setSinceTime(new Date().getTime()); selectCollection(null); }; return ( {loading && ( )} {isFirstLoad && (
{constants.INITIAL_LOAD_DELAY_WARNING}
)} setPlanModalView(false)} setDialogMessage={setDialogMessage} setLoading={setLoading} /> setDialogView(false)} attributes={dialogMessage} /> setPlanModalView(true)} /> {selected.count > 0 && ( )}
); }