diff --git a/public/manifest.json b/public/manifest.json index cc017f39a..fd62a6939 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,38 +1,76 @@ { - "short_name": "ente", - "name": "ente | encrypted photo storage", - "icons": [ + "short_name": "ente", + "name": "ente | encrypted photo storage", + "icons": [ + { + "src": "/images/ente-192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "/images/ente-256.png", + "type": "image/png", + "sizes": "256x256" + }, + { + "src": "/images/ente-512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": "/", + "background_color": "#191919", + "display": "standalone", + "scope": "/", + "theme_color": "#111", + "description": "ente provides a simple way to back up your memories.", + "prefer_related_applications": true, + "related_applications": [ { - "src": "/images/ente-192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "/images/ente-256.png", - "type": "image/png", - "sizes": "256x256" - }, - { - "src": "/images/ente-512.png", - "type": "image/png", - "sizes": "512x512" + "platform": "play", + "url": "https://play.google.com/store/apps/details?id=io.ente.photos", + "id": "io.ente.photos" + }, { + "platform": "itunes", + "url": "https://apps.apple.com/in/app/ente-photos/id1542026904" } - ], - "start_url": "/", - "background_color": "#191919", - "display": "standalone", - "scope": "/", - "theme_color": "#111", - "description": "ente provides a simple way to back up your memories.", - "prefer_related_applications": true, - "related_applications": [ - { - "platform": "play", - "url": "https://play.google.com/store/apps/details?id=io.ente.photos", - "id": "io.ente.photos" - }, { - "platform": "itunes", - "url": "https://apps.apple.com/in/app/ente-photos/id1542026904" - } - ] + ], + "share_target": { + "action": "./share-target", + "method": "POST", + "enctype": "multipart/form-data", + "params": { + "files": [{ + "name": "files", + "accept": ["image/*", "video/*", "application/json"] + }] + } + }, + "screenshots": [ + { + "src": "/pwa/slide-1.jpg", + "type": "image/jpeg", + "sizes": "417x760" + }, + { + "src": "/pwa/slide-2.jpg", + "type": "image/jpeg", + "sizes": "378x690" + }, + { + "src": "/pwa/slide-3.jpg", + "type": "image/jpeg", + "sizes": "378x690" + }, + { + "src": "/pwa/slide-4.jpg", + "type": "image/jpeg", + "sizes": "378x690" + }, + { + "src": "/pwa/slide-5.jpg", + "type": "image/jpeg", + "sizes": "378x690" + } + ] } \ No newline at end of file diff --git a/public/pwa/slide-1.jpg b/public/pwa/slide-1.jpg new file mode 100644 index 000000000..72d7614a8 Binary files /dev/null and b/public/pwa/slide-1.jpg differ diff --git a/public/pwa/slide-2.jpg b/public/pwa/slide-2.jpg new file mode 100644 index 000000000..e60b7e15c Binary files /dev/null and b/public/pwa/slide-2.jpg differ diff --git a/public/pwa/slide-3.jpg b/public/pwa/slide-3.jpg new file mode 100644 index 000000000..bad30a595 Binary files /dev/null and b/public/pwa/slide-3.jpg differ diff --git a/public/pwa/slide-4.jpg b/public/pwa/slide-4.jpg new file mode 100644 index 000000000..1e6a057ed Binary files /dev/null and b/public/pwa/slide-4.jpg differ diff --git a/public/pwa/slide-5.jpg b/public/pwa/slide-5.jpg new file mode 100644 index 000000000..89daed6c1 Binary files /dev/null and b/public/pwa/slide-5.jpg differ diff --git a/src/components/PhotoFrame.tsx b/src/components/PhotoFrame.tsx index 33c72692a..e1d3d4c4c 100644 --- a/src/components/PhotoFrame.tsx +++ b/src/components/PhotoFrame.tsx @@ -7,7 +7,7 @@ import { SetFiles, setSearchStats, } from 'pages/gallery'; -import PreviewCard from 'pages/gallery/components/PreviewCard'; +import PreviewCard from './pages/gallery/PreviewCard'; import React, { useContext, useEffect, useRef, useState } from 'react'; import { Button } from 'react-bootstrap'; import { File } from 'services/fileService'; diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 24eeb6b98..1dc53f64d 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -21,7 +21,7 @@ import { File } from 'services/fileService'; import isElectron from 'is-electron'; import { Collection } from 'services/collectionService'; import { useRouter } from 'next/router'; -import LinkButton from 'pages/gallery/components/LinkButton'; +import LinkButton from './pages/gallery/LinkButton'; import { downloadApp } from 'utils/common'; import { logoutUser } from 'services/userService'; import { LogoImage } from 'pages/_app'; diff --git a/src/pages/gallery/components/AddCollectionButton.tsx b/src/components/pages/gallery/AddCollectionButton.tsx similarity index 100% rename from src/pages/gallery/components/AddCollectionButton.tsx rename to src/components/pages/gallery/AddCollectionButton.tsx diff --git a/src/pages/gallery/components/AlertBanner.tsx b/src/components/pages/gallery/AlertBanner.tsx similarity index 100% rename from src/pages/gallery/components/AlertBanner.tsx rename to src/components/pages/gallery/AlertBanner.tsx diff --git a/src/pages/gallery/components/ChoiceModal.tsx b/src/components/pages/gallery/ChoiceModal.tsx similarity index 100% rename from src/pages/gallery/components/ChoiceModal.tsx rename to src/components/pages/gallery/ChoiceModal.tsx diff --git a/src/pages/gallery/components/CollectionNamer.tsx b/src/components/pages/gallery/CollectionNamer.tsx similarity index 100% rename from src/pages/gallery/components/CollectionNamer.tsx rename to src/components/pages/gallery/CollectionNamer.tsx diff --git a/src/pages/gallery/components/CollectionOptions.tsx b/src/components/pages/gallery/CollectionOptions.tsx similarity index 100% rename from src/pages/gallery/components/CollectionOptions.tsx rename to src/components/pages/gallery/CollectionOptions.tsx diff --git a/src/pages/gallery/components/CollectionSelector.tsx b/src/components/pages/gallery/CollectionSelector.tsx similarity index 96% rename from src/pages/gallery/components/CollectionSelector.tsx rename to src/components/pages/gallery/CollectionSelector.tsx index 14663f00a..f6c912610 100644 --- a/src/pages/gallery/components/CollectionSelector.tsx +++ b/src/components/pages/gallery/CollectionSelector.tsx @@ -27,7 +27,7 @@ export type SetCollectionSelectorAttributes = React.Dispatch< interface Props { show: boolean; - onHide: () => void; + onHide: (closeBtnClick?: boolean) => void; setLoading: (value: boolean) => void; directlyShowNextModal: boolean; collectionsAndTheirLatestFile: CollectionAndItsLatestFile[]; @@ -82,7 +82,7 @@ function CollectionSelector({ return ( - + props.onHide(true)}> {attributes.title} ` border-radius: 20px; diff --git a/src/pages/gallery/components/PreviewCard.tsx b/src/components/pages/gallery/PreviewCard.tsx similarity index 99% rename from src/pages/gallery/components/PreviewCard.tsx rename to src/components/pages/gallery/PreviewCard.tsx index 175b9d128..2bc4a510d 100644 --- a/src/pages/gallery/components/PreviewCard.tsx +++ b/src/components/pages/gallery/PreviewCard.tsx @@ -4,7 +4,7 @@ import styled from 'styled-components'; import PlayCircleOutline from 'components/PlayCircleOutline'; import DownloadManager from 'services/downloadManager'; import useLongPress from 'utils/common/useLongPress'; -import { GalleryContext } from '..'; +import { GalleryContext } from 'pages/gallery'; interface IProps { file: File; diff --git a/src/pages/gallery/components/SelectedFileOptions.tsx b/src/components/pages/gallery/SelectedFileOptions.tsx similarity index 100% rename from src/pages/gallery/components/SelectedFileOptions.tsx rename to src/components/pages/gallery/SelectedFileOptions.tsx diff --git a/src/pages/gallery/components/Upload.tsx b/src/components/pages/gallery/Upload.tsx similarity index 92% rename from src/pages/gallery/components/Upload.tsx rename to src/components/pages/gallery/Upload.tsx index db0693a0d..ca7ad00ed 100644 --- a/src/pages/gallery/components/Upload.tsx +++ b/src/components/pages/gallery/Upload.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import UploadService, { FileWithCollection, UPLOAD_STAGES } from 'services/uploadService'; import { createAlbum } from 'services/collectionService'; import { File } from 'services/fileService'; @@ -9,7 +9,8 @@ import UploadProgress from './UploadProgress'; import ChoiceModal from './ChoiceModal'; import { SetCollectionNamerAttributes } from './CollectionNamer'; import { SetCollectionSelectorAttributes } from './CollectionSelector'; -import { SetLoading } from '..'; +import { SetLoading } from 'pages/gallery'; +import { AppContext } from 'pages/_app'; interface Props { syncWithRemote: () => Promise; @@ -22,6 +23,7 @@ interface Props { setLoading: SetLoading; setDialogMessage: SetDialogMessage; setUploadInProgress: any; + showCollectionSelector: () => void; } export enum UPLOAD_STRATEGY { @@ -43,18 +45,26 @@ export default function Upload(props: Props) { const [percentComplete, setPercentComplete] = useState(0); const [choiceModalView, setChoiceModalView] = useState(false); const [fileAnalysisResult, setFileAnalysisResult] = useState(null); + const appContext = useContext(AppContext); + useEffect(() => { if (props.acceptedFiles?.length > 0) { props.setLoading(true); - const fileAnalysisResult = analyseUploadFiles(); - if (!fileAnalysisResult) { - setFileAnalysisResult(fileAnalysisResult); - } props.setCollectionSelectorAttributes({ callback: uploadFilesToExistingCollection, showNextModal: nextModal.bind(null, fileAnalysisResult), title: 'upload to collection', }); + if (props.acceptedFiles[0]['path']) { + // File selection by drag and drop or selection of file. + const fileAnalysisResult = analyseUploadFiles(); + if (!fileAnalysisResult) { + setFileAnalysisResult(fileAnalysisResult); + } + } else { + // File selection by share target. + props.showCollectionSelector(); + } props.setLoading(false); } }, [props.acceptedFiles]); @@ -196,6 +206,7 @@ export default function Upload(props: Props) { setFileProgress, }, ); + appContext.resetFiles(); props.setUploadInProgress(false); } catch (err) { props.setBannerMessage(err.message); diff --git a/src/pages/gallery/components/UploadButton.tsx b/src/components/pages/gallery/UploadButton.tsx similarity index 100% rename from src/pages/gallery/components/UploadButton.tsx rename to src/components/pages/gallery/UploadButton.tsx diff --git a/src/pages/gallery/components/UploadProgress.tsx b/src/components/pages/gallery/UploadProgress.tsx similarity index 100% rename from src/pages/gallery/components/UploadProgress.tsx rename to src/components/pages/gallery/UploadProgress.tsx diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ec9126519..5890d0d94 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -152,6 +152,11 @@ const GlobalStyles = createGlobalStyle` background-size: 20px 20px; background-position: center; } + .share-btn{ + background: url('/share_icon.png') no-repeat; + background-size: 20px 20px; + background-position: center; + } .btn-success { background: #2dc262; border-color: #29a354; @@ -317,7 +322,7 @@ const FlexContainer = styled.div` text-align: center; `; -const OfflineContainer = styled.div` +export const MessageContainer = styled.div` background-color: #111; padding: 0; font-size: 14px; @@ -334,6 +339,8 @@ sentryInit(); type AppContextType = { showNavBar: (show: boolean) => void; + files: File[]; + resetFiles: () => void; } export const AppContext = createContext(null); @@ -345,6 +352,7 @@ export default function App({ Component, err }) { typeof window !== 'undefined' && !window.navigator.onLine, ); const [showNavbar, setShowNavBar] = useState(false); + const [files, setFiles] = useState(null); useEffect(() => { if ( @@ -357,6 +365,15 @@ export default function App({ Component, err }) { const wb = new Workbox('sw.js', { scope: '/' }); wb.register(); + if ('serviceWorker' in navigator) { + navigator.serviceWorker.onmessage = (event) => { + if (event.data.action === 'upload-files') { + const files = event.data.files; + setFiles(files); + } + }; + } + // if ('serviceWorker' in navigator) { // navigator.serviceWorker // .getRegistrations() @@ -373,6 +390,7 @@ export default function App({ Component, err }) { const setUserOnline = () => setOffline(false); const setUserOffline = () => setOffline(true); + const resetFiles = () => setFiles(null); useEffect(() => { console.log( @@ -424,9 +442,15 @@ export default function App({ Component, err }) { /> } - {offline && constants.OFFLINE_MSG} + {offline && constants.OFFLINE_MSG} + {files && + (router.pathname === '/gallery' ? + {constants.FILES_TO_BE_UPLOADED(files.length)} : + {constants.LOGIN_TO_UPLOAD_FILES(files.length)})} {loading ? ( diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 51f000a45..f614c075b 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -42,18 +42,18 @@ import { addFilesToCollection } from 'utils/collection'; import { errorCodes } from 'utils/common/errorUtil'; import SearchBar, { DateValue } from 'components/SearchBar'; import { Bbox } from 'services/searchService'; -import SelectedFileOptions from './components/SelectedFileOptions'; +import SelectedFileOptions from 'components/pages/gallery/SelectedFileOptions'; import CollectionSelector, { CollectionSelectorAttributes, -} from './components/CollectionSelector'; +} from 'components/pages/gallery/CollectionSelector'; import CollectionNamer, { CollectionNamerAttributes, -} from './components/CollectionNamer'; -import AlertBanner from './components/AlertBanner'; -import UploadButton from './components/UploadButton'; -import PlanSelector from './components/PlanSelector'; -import Upload from './components/Upload'; -import Collections from './components/Collections'; +} from 'components/pages/gallery/CollectionNamer'; +import AlertBanner from 'components/pages/gallery/AlertBanner'; +import UploadButton from 'components/pages/gallery/UploadButton'; +import PlanSelector from 'components/pages/gallery/PlanSelector'; +import Upload from 'components/pages/gallery/Upload'; +import Collections from 'components/pages/gallery/Collections'; import { AppContext } from 'pages/_app'; export enum FILE_TYPE { @@ -146,7 +146,7 @@ export default function Gallery() { const loadingBar = useRef(null); const [searchMode, setSearchMode] = useState(false); const [searchStats, setSearchStats] = useState(null); - const [syncInProgress, setSyncInProgress] = useState(false); + const [syncInProgress, setSyncInProgress] = useState(true); const [resync, setResync] = useState(false); const [deleted, setDeleted] = useState([]); const appContext = useContext(AppContext); @@ -180,7 +180,7 @@ export default function Gallery() { const favItemIds = await getFavItemIds(files); setFavItemIds(favItemIds); await checkSubscriptionPurchase(setDialogMessage, router); - await syncWithRemote(); + await syncWithRemote(true); setIsFirstLoad(false); setJustSignedUp(false); setIsFirstFetch(false); @@ -200,8 +200,8 @@ export default function Gallery() { ); useEffect(() => setCollectionNamerView(true), [collectionNamerAttributes]); - const syncWithRemote = async () => { - if (syncInProgress) { + const syncWithRemote = async (force = false) => { + if (syncInProgress && !force) { setResync(true); return; } @@ -335,6 +335,24 @@ export default function Gallery() { setSearch(search); setSearchStats(null); }; + + const getFilesToBeUploaded = () => { + if (syncInProgress) { + return []; + } + if (appContext.files) { + return appContext.files; + } + return acceptedFiles; + }; + + const closeCollectionSelector = (closeBtnClick?: boolean) => { + if (closeBtnClick === true) { + appContext.resetFiles(); + } + setCollectionSelectorView(false); + }; + return ( { + event.waitUntil(async function() { + const data = await event.request.formData(); + const client = await self.clients.get(event.resultingClientId || event.clientId); + const files = data.getAll('files'); + setTimeout(() => { + client.postMessage({ files, action: 'upload-files' }); + }, 1000); + }()); + return Response.redirect('./'); +}, 'POST'); + // Use a stale-while-revalidate strategy for all other requests. setDefaultHandler(new NetworkOnly()); diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index ea7999fe2..080ecd8aa 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -394,6 +394,8 @@ const englishConstants = { APERTURE: 'aperture', ISO: 'iso', SHOW_ALL: 'show all', + LOGIN_TO_UPLOAD_FILES: (count: number) => count === 1 ? `1 file received. login to upload` : `${count} files received. login to upload`, + FILES_TO_BE_UPLOADED: (count: number) => count === 1 ? `1 file received. Uploading in a jiffy` : `${count} files received. Uploading in a jiffy`, }; export default englishConstants;