commit
c88d26205f
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
BIN
public/pwa/slide-1.jpg
Normal file
BIN
public/pwa/slide-1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 203 KiB |
BIN
public/pwa/slide-2.jpg
Normal file
BIN
public/pwa/slide-2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
BIN
public/pwa/slide-3.jpg
Normal file
BIN
public/pwa/slide-3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 159 KiB |
BIN
public/pwa/slide-4.jpg
Normal file
BIN
public/pwa/slide-4.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 115 KiB |
BIN
public/pwa/slide-5.jpg
Normal file
BIN
public/pwa/slide-5.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 112 KiB |
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 (
|
||||
<Modal {...props} size="xl" centered>
|
||||
<Modal.Header closeButton>
|
||||
<Modal.Header closeButton onHide={() => props.onHide(true)}>
|
||||
<Modal.Title>{attributes.title}</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body
|
|
@ -21,7 +21,7 @@ import { reverseString } from 'utils/common';
|
|||
import { SetDialogMessage } from 'components/MessageDialog';
|
||||
import ArrowEast from 'components/ArrowEast';
|
||||
import LinkButton from './LinkButton';
|
||||
import { DeadCenter, SetLoading } from '..';
|
||||
import { DeadCenter, SetLoading } from 'pages/gallery';
|
||||
|
||||
export const PlanIcon = styled.div<{ selected: boolean }>`
|
||||
border-radius: 20px;
|
|
@ -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;
|
|
@ -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<void>;
|
||||
|
@ -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<AnalysisResult>(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);
|
|
@ -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<AppContextType>(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<File[]>(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 }) {
|
|||
/>
|
||||
</FlexContainer>
|
||||
</Navbar>}
|
||||
<OfflineContainer>{offline && constants.OFFLINE_MSG}</OfflineContainer>
|
||||
<MessageContainer>{offline && constants.OFFLINE_MSG}</MessageContainer>
|
||||
{files &&
|
||||
(router.pathname === '/gallery' ?
|
||||
<MessageContainer>{constants.FILES_TO_BE_UPLOADED(files.length)}</MessageContainer> :
|
||||
<MessageContainer>{constants.LOGIN_TO_UPLOAD_FILES(files.length)}</MessageContainer>)}
|
||||
<AppContext.Provider value={{
|
||||
showNavBar,
|
||||
files,
|
||||
resetFiles,
|
||||
}}>
|
||||
{loading ? (
|
||||
<Container>
|
||||
|
|
|
@ -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<number[]>([]);
|
||||
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 (
|
||||
<GalleryContext.Provider value={defaultGalleryContext}>
|
||||
<FullScreenDropZone
|
||||
|
@ -393,7 +411,7 @@ export default function Gallery() {
|
|||
/>
|
||||
<CollectionSelector
|
||||
show={collectionSelectorView}
|
||||
onHide={setCollectionSelectorView.bind(null, false)}
|
||||
onHide={closeCollectionSelector}
|
||||
setLoading={setLoading}
|
||||
collectionsAndTheirLatestFile={collectionsAndTheirLatestFile}
|
||||
directlyShowNextModal={
|
||||
|
@ -404,11 +422,10 @@ export default function Gallery() {
|
|||
<Upload
|
||||
syncWithRemote={syncWithRemote}
|
||||
setBannerMessage={setBannerMessage}
|
||||
acceptedFiles={acceptedFiles}
|
||||
acceptedFiles={getFilesToBeUploaded()}
|
||||
existingFiles={files}
|
||||
setCollectionSelectorAttributes={
|
||||
setCollectionSelectorAttributes
|
||||
}
|
||||
showCollectionSelector={setCollectionSelectorView.bind(null, true)}
|
||||
setCollectionSelectorAttributes={setCollectionSelectorAttributes}
|
||||
closeCollectionSelector={setCollectionSelectorView.bind(
|
||||
null,
|
||||
false,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { precacheAndRoute } from 'workbox-precaching';
|
||||
import { setDefaultHandler } from 'workbox-routing';
|
||||
import { registerRoute, setDefaultHandler } from 'workbox-routing';
|
||||
import { NetworkOnly } from 'workbox-strategies';
|
||||
import { pageCache, offlineFallback } from 'workbox-recipes';
|
||||
|
||||
|
@ -7,6 +7,18 @@ pageCache();
|
|||
|
||||
precacheAndRoute(self.__WB_MANIFEST);
|
||||
|
||||
registerRoute('/share-target', async ({ event }) => {
|
||||
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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue