Merge pull request #24 from ente-io/handle-B2-errors

Handle b2 errors
This commit is contained in:
Vishnu Mohandas 2021-02-22 22:57:41 +05:30 committed by GitHub
commit 40f793f3fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 204 additions and 105 deletions

View file

@ -0,0 +1,15 @@
import React from 'react';
import { Alert } from 'react-bootstrap';
import { ErrorBannerMessage } from 'utils/common/errorUtil';
export default function AlertBanner({ bannerErrorCode }) {
return (
<Alert
variant={'danger'}
style={{ display: bannerErrorCode ? 'block' : 'none' }}
>
{ErrorBannerMessage(bannerErrorCode)}
</Alert>
);
}

View file

@ -10,7 +10,8 @@ function CollectionDropZone({
collectionAndItsLatestFile,
setProgressView,
progressBarProps,
setErrorCode,
setBannerErrorCode,
setUploadErrors,
}) {
const upload = async (acceptedFiles) => {
try {
@ -23,14 +24,12 @@ function CollectionDropZone({
acceptedFiles,
collectionAndItsLatestFile,
token,
progressBarProps
progressBarProps,
setUploadErrors
);
refetchData();
} catch (err) {
if (err.response) {
setErrorCode(err.response.status);
}
} finally {
setBannerErrorCode(err.message);
setProgressView(false);
}
};

View file

@ -15,7 +15,8 @@ export default function CreateCollection(props) {
modalView,
closeModal,
closeUploadModal,
setErrorCode,
setUploadErrors,
setBannerErrorCode,
} = props;
const [albumName, setAlbumName] = useState('');
@ -67,20 +68,23 @@ export default function CreateCollection(props) {
acceptedFiles,
collectionAndItsLatestFile,
token,
progressBarProps
progressBarProps,
setUploadErrors
);
refetchData();
} catch (err) {
if (err.response) {
setErrorCode(err.response.status);
}
} finally {
setBannerErrorCode(err.message);
setProgressView(false);
}
};
return (
<Modal show={modalView} onHide={closeModal} centered backdrop="static"
style={{ background: 'rgba(0, 0, 0, 0.8)' }}>
<Modal
show={modalView}
onHide={closeModal}
centered
backdrop="static"
style={{ background: 'rgba(0, 0, 0, 0.8)' }}
>
<Modal.Header closeButton>
<Modal.Title>{constants.CREATE_COLLECTION}</Modal.Title>
</Modal.Header>

View file

@ -1,21 +0,0 @@
import React from "react";
import { Alert } from "react-bootstrap";
import constants from "utils/strings/constants";
export default function ErrorAlert({ errorCode }) {
let errorMessage;
switch (errorCode) {
case 402:
errorMessage = constants.SUBSCRIPTION_EXPIRED;
break;
case 426:
errorMessage = constants.STORAGE_QUOTA_EXCEEDED;
default:
errorMessage = errorCode;
}
return (
<Alert variant={'danger'} style={{ display: errorCode ? 'block' : 'none' }}>
{errorMessage}
</Alert>
)
}

View file

@ -4,6 +4,7 @@ import { getData, LS_KEYS } from 'utils/storage/localStorage';
import styled from 'styled-components';
import PlayCircleOutline from 'components/PlayCircleOutline';
import DownloadManager from 'services/downloadManager';
import { getToken } from 'utils/common/key';
interface IProps {
data: file;
@ -48,7 +49,7 @@ export default function PreviewCard(props: IProps) {
useEffect(() => {
if (data && !data.msrc) {
const main = async () => {
const token = getData(LS_KEYS.USER).token;
const token = getToken();
const url = await DownloadManager.getPreview(data);
setImgSrc(url);
data.msrc = url;

View file

@ -10,6 +10,7 @@ export default function Upload(props) {
);
const [fileCounter, setFileCounter] = useState({ current: 0, total: 0 });
const [percentComplete, setPercentComplete] = useState(0);
const [uploadErrors, setUploadErrors] = useState<Error[]>([]);
const init = () => {
setProgressView(false);
setUploadStage(UPLOAD_STAGES.START);
@ -26,12 +27,15 @@ export default function Upload(props) {
setFileCounter,
setUploadStage,
}}
setUploadErrors={setUploadErrors}
/>
<UploadProgress
now={percentComplete}
fileCounter={fileCounter}
uploadStage={uploadStage}
uploadErrors={uploadErrors}
show={progressView}
closeModal={() => setProgressView(false)}
onHide={init}
/>
</>

View file

@ -1,11 +1,13 @@
import React from 'react';
import { Alert, Modal, ProgressBar } from 'react-bootstrap';
import { Alert, Button, Modal, ProgressBar } from 'react-bootstrap';
import constants from 'utils/strings/constants';
export default function UploadProgress({
fileCounter,
uploadStage,
now,
uploadErrors,
closeModal,
...props
}) {
return (
@ -35,6 +37,33 @@ export default function UploadProgress({
<ProgressBar animated now={now} />
</>
)}
{uploadErrors.length > 0 && (
<>
<Alert variant="danger">
<div
style={{
overflow: 'auto',
height: '100px',
}}
>
{uploadErrors.map((error) => (
<li key={error.message}>{error.message}</li>
))}
</div>
</Alert>
</>
)}
{now === 100 && (
<Modal.Footer>
<Button
variant="dark"
style={{ width: '100%' }}
onClick={closeModal}
>
{constants.CLOSE}
</Button>
</Modal.Footer>
)}
</Modal.Body>
</Modal>
);

View file

@ -22,7 +22,7 @@ import {
getCollectionUpdationTime,
} from 'services/collectionService';
import constants from 'utils/strings/constants';
import ErrorAlert from './components/ErrorAlert';
import AlertBanner from './components/AlertBanner';
import { Alert } from 'react-bootstrap';
const DATE_CONTAINER_HEIGHT = 45;
@ -111,7 +111,7 @@ export default function Gallery(props) {
const [open, setOpen] = useState(false);
const [currentIndex, setCurrentIndex] = useState<number>(0);
const fetching: { [k: number]: boolean } = {};
const [errorCode, setErrorCode] = useState<number>(null);
const [bannerErrorCode, setBannerErrorCode] = useState<number>(null);
const [sinceTime, setSinceTime] = useState(0);
const [isFirstLoad, setIsFirstLoad] = useState(false);
@ -123,7 +123,7 @@ export default function Gallery(props) {
return;
}
const main = async () => {
setIsFirstLoad(await getCollectionUpdationTime() == 0);
setIsFirstLoad((await getCollectionUpdationTime()) == 0);
const data = await localFiles();
const collections = await getLocalCollections();
const collectionAndItsLatestFile = await getCollectionAndItsLatestFile(
@ -325,7 +325,7 @@ export default function Gallery(props) {
</Alert>
</div>
)}
<ErrorAlert errorCode={errorCode} />
<AlertBanner bannerErrorCode={bannerErrorCode} />
<Collections
collections={collections}
@ -338,7 +338,7 @@ export default function Gallery(props) {
showUploadModal={props.showUploadModal}
collectionAndItsLatestFile={collectionAndItsLatestFile}
refetchData={syncWithRemote}
setErrorCode={setErrorCode}
setBannerErrorCode={setBannerErrorCode}
/>
{filteredData.length ? (
<Container>
@ -413,7 +413,7 @@ export default function Gallery(props) {
<List
itemSize={(index) =>
timeStampList[index].itemType ===
ITEM_TYPE.TIME
ITEM_TYPE.TIME
? DATE_CONTAINER_HEIGHT
: IMAGE_CONTAINER_HEIGHT
}
@ -430,14 +430,14 @@ export default function Gallery(props) {
columns={
timeStampList[index]
.itemType ===
ITEM_TYPE.TIME
ITEM_TYPE.TIME
? 1
: columns
}
>
{timeStampList[index]
.itemType ===
ITEM_TYPE.TIME ? (
ITEM_TYPE.TIME ? (
<DateContainer>
{
timeStampList[
@ -456,7 +456,7 @@ export default function Gallery(props) {
index
]
.itemStartIndex +
idx
idx
);
}
)

View file

@ -107,7 +107,7 @@ const getCollections = async (
);
return await Promise.all(promises);
} catch (e) {
console.log('getCollections failed- ' + e);
console.log('getCollections failed- ', e.response);
}
};
@ -118,8 +118,8 @@ export const getLocalCollections = async (): Promise<collection[]> => {
};
export const getCollectionUpdationTime = async (): Promise<number> => {
return await localForage.getItem<number>(COLLECTION_UPDATION_TIME) ?? 0;
}
return (await localForage.getItem<number>(COLLECTION_UPDATION_TIME)) ?? 0;
};
export const syncCollections = async (token: string, key: string) => {
const localCollections = await getLocalCollections();
@ -268,7 +268,7 @@ const createCollection = async (
);
return response.data.collection;
} catch (e) {
console.log('create Collection failed ' + e);
console.log('create Collection failed ', e);
}
};
@ -326,7 +326,7 @@ const addToCollection = async (collection: collection, files: file[]) => {
{ 'X-Auth-Token': token }
);
} catch (e) {
console.log('Add to collection Failed ' + e);
console.log('Add to collection Failed ', e);
}
};
const removeFromCollection = async (collection: collection, files: file[]) => {
@ -349,7 +349,7 @@ const removeFromCollection = async (collection: collection, files: file[]) => {
{ 'X-Auth-Token': token }
);
} catch (e) {
console.log('remove from collection failed ' + e);
console.log('remove from collection failed ', e);
}
};

View file

@ -50,7 +50,7 @@ class DownloadManager {
}
return await this.thumbnailDownloads.get(file.id);
} catch (e) {
console.log('get preview Failed' + e);
console.log('get preview Failed' , e );
}
}
@ -72,7 +72,7 @@ class DownloadManager {
);
return URL.createObjectURL(new Blob([decrypted]));
} catch (e) {
console.log('get file failed ' + e);
console.log('get file failed ' , e );
}
})();
this.fileDownloads.set(file.id, download);

View file

@ -151,7 +151,7 @@ export const getFiles = async (
} while (resp.data.diff.length === limit);
return await Promise.all(promises);
} catch (e) {
console.log('Get files failed' + e);
console.log('Get files failed' , e );
}
};

View file

@ -5,6 +5,8 @@ import EXIF from 'exif-js';
import { fileAttribute } from './fileService';
import { collection, CollectionAndItsLatestFile } from './collectionService';
import { FILE_TYPE } from 'pages/gallery';
import { checkConnectivity } from 'utils/common/utilFunctions';
import { ErrorHandler } from 'utils/common/errorUtil';
const CryptoWorker: any =
typeof window !== 'undefined' &&
Comlink.wrap(new Worker('worker/crypto.worker.js', { type: 'module' }));
@ -77,17 +79,23 @@ class UploadService {
private metadataMap: Map<string, Object>;
private filesToBeUploaded: File[];
private progressBarProps;
private uploadErrors: Error[];
private setUploadErrors;
public async uploadFiles(
recievedFiles: File[],
collectionAndItsLatestFile: CollectionAndItsLatestFile,
token: string,
progressBarProps
progressBarProps,
setUploadErrors
) {
try {
checkConnectivity();
progressBarProps.setUploadStage(UPLOAD_STAGES.START);
this.filesCompleted = 0;
this.uploadErrors = [];
this.setUploadErrors = setUploadErrors;
this.metadataMap = new Map<string, object>();
this.progressBarProps = progressBarProps;
@ -111,14 +119,17 @@ class UploadService {
progressBarProps.setUploadStage(
UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES
);
for await (const rawFile of metadataFiles) {
for (const rawFile of metadataFiles) {
await this.seedMetadataMap(rawFile);
}
progressBarProps.setUploadStage(UPLOAD_STAGES.UPLOADING);
this.changeProgressBarProps();
try {
await this.fetchUploadURLs(token);
} catch (e) {
ErrorHandler(e);
}
const uploadProcesses = [];
for (let i = 0; i < Math.min(5, this.totalFileCount); i++) {
uploadProcesses.push(
@ -134,6 +145,7 @@ class UploadService {
progressBarProps.setUploadStage(UPLOAD_STAGES.FINISH);
progressBarProps.setPercentComplete(100);
} catch (e) {
this.filesToBeUploaded = [];
console.log(e);
throw e;
}
@ -159,18 +171,21 @@ class UploadService {
await this.uploadFile(uploadFile, token);
this.filesCompleted++;
this.changeProgressBarProps();
if (this.filesToBeUploaded.length > 0) {
await this.uploader(
worker,
this.filesToBeUploaded.pop(),
collection,
token
);
}
} catch (e) {
console.log(e);
throw e;
ErrorHandler(e);
const error = new Error(
`Uploading Failed for File - ${rawFile.name}`
);
this.uploadErrors.push(error);
this.setUploadErrors(this.uploadErrors);
}
if (this.filesToBeUploaded.length > 0) {
await this.uploader(
worker,
this.filesToBeUploaded.pop(),
collection,
token
);
}
}
@ -206,7 +221,6 @@ class UploadService {
recievedFile
);
const metadata = Object.assign(
this.metadataMap.get(recievedFile.name) ?? {},
{
title: recievedFile.name,
creationTime:
@ -215,7 +229,8 @@ class UploadService {
latitude: location?.latitude,
longitude: location?.latitude,
fileType,
}
},
this.metadataMap.get(recievedFile.name)
);
return {
filedata,
@ -223,7 +238,8 @@ class UploadService {
metadata,
};
} catch (e) {
console.log('error reading files ' + e);
console.log('error reading files ', e);
throw e;
}
}
private async encryptFile(
@ -265,7 +281,8 @@ class UploadService {
};
return result;
} catch (e) {
console.log('Error encrypting files ' + e);
console.log('Error encrypting files ', e);
throw e;
}
}
@ -290,7 +307,7 @@ class UploadService {
return file;
} catch (e) {
console.log('error uploading to bucket ' + e);
console.log('error uploading to bucket ', e);
throw e;
}
}
@ -320,7 +337,8 @@ class UploadService {
return response.data;
} catch (e) {
console.log('upload Files Failed ' + e);
console.log('upload Files Failed ', e);
throw e;
}
}
@ -339,40 +357,36 @@ class UploadService {
reader.readAsText(recievedFile);
}
);
if (!this.metadataMap.has(metadataJSON['title'])) {
return;
}
const metaDataObject = this.metadataMap.get(metadataJSON['title']);
const metaDataObject = {};
metaDataObject['creationTime'] =
metadataJSON['photoTakenTime']['timestamp'] * 1000000;
metaDataObject['modificationTime'] =
metadataJSON['modificationTime']['timestamp'] * 1000000;
var locationData = null;
if (
metaDataObject['latitude'] == null ||
(metaDataObject['latitude'] == 0.0 &&
metaDataObject['longitude'] == 0.0)
metadataJSON['geoData']['latitude'] != 0.0 ||
metadataJSON['geoData']['longitude'] != 0.0
) {
var locationData = null;
if (
metadataJSON['geoData']['latitude'] != 0.0 ||
metadataJSON['geoData']['longitude'] != 0.0
) {
locationData = metadataJSON['geoData'];
} else if (
metadataJSON['geoDataExif']['latitude'] != 0.0 ||
metadataJSON['geoDataExif']['longitude'] != 0.0
) {
locationData = metadataJSON['geoDataExif'];
}
if (locationData != null) {
metaDataObject['latitude'] = locationData['latitide'];
metaDataObject['longitude'] = locationData['longitude'];
}
locationData = metadataJSON['geoData'];
} else if (
metadataJSON['geoDataExif']['latitude'] != 0.0 ||
metadataJSON['geoDataExif']['longitude'] != 0.0
) {
locationData = metadataJSON['geoDataExif'];
}
if (locationData != null) {
metaDataObject['latitude'] = locationData['latitide'];
metaDataObject['longitude'] = locationData['longitude'];
}
this.metadataMap.set(metadataJSON['title'], metaDataObject);
} catch (e) {
console.log('error reading metaData Files ' + e);
const error = new Error(
`Error reading metaDataFile ${recievedFile.name}`
);
this.uploadErrors.push(error);
this.setUploadErrors(this.uploadErrors);
}
}
private async generateThumbnail(file: File): Promise<Uint8Array> {
@ -448,14 +462,18 @@ class UploadService {
quality
);
});
if (!thumbnailBlob) {
thumbnailBlob = file;
}
} while (
thumbnailBlob.size > MIN_THUMBNAIL_SIZE &&
attempts <= MAX_ATTEMPTS
);
const thumbnail = this.getUint8ArrayView(thumbnailBlob);
const thumbnail = await this.getUint8ArrayView(thumbnailBlob);
return thumbnail;
} catch (e) {
console.log('Error generating thumbnail ' + e);
console.log('Error generating thumbnail ', e);
throw e;
}
}
@ -477,7 +495,7 @@ class UploadService {
reader.readAsArrayBuffer(file);
});
} catch (e) {
console.log('error readinf file to bytearray ' + e);
console.log('error readinf file to bytearray ', e);
throw e;
}
}
@ -509,7 +527,7 @@ class UploadService {
}
return this.uploadURLFetchInProgress;
} catch (e) {
console.log('fetch upload-url failed ' + e);
console.log('fetch upload-url failed ', e);
throw e;
}
}
@ -525,7 +543,7 @@ class UploadService {
});
return fileUploadURL.objectKey;
} catch (e) {
console.log('putFile to dataStore failed ' + e);
console.log('putFile to dataStore failed ', e);
throw e;
}
}
@ -548,6 +566,7 @@ class UploadService {
};
} catch (e) {
console.log('error reading exif data');
throw e;
}
}
private getUNIXTime(exifData: any) {

View file

@ -0,0 +1,38 @@
import constants from 'utils/strings/constants';
export const errorCodes = {
ERR_STORAGE_LIMIT_EXCEEDED: '426',
ERR_NO_ACTIVE_SUBSCRIPTION: '402',
ERR_NO_INTERNET_CONNECTION: '1',
};
export function ErrorHandler(error) {
if (
error.response?.status.toString() ==
errorCodes.ERR_STORAGE_LIMIT_EXCEEDED ||
error.response?.status.toString() ==
errorCodes.ERR_NO_ACTIVE_SUBSCRIPTION
) {
throw new Error(error.response.status);
} else {
return;
}
}
export function ErrorBannerMessage(bannerErrorCode) {
let errorMessage;
switch (bannerErrorCode) {
case errorCodes.ERR_NO_ACTIVE_SUBSCRIPTION:
errorMessage = constants.SUBSCRIPTION_EXPIRED;
break;
case errorCodes.ERR_STORAGE_LIMIT_EXCEEDED:
errorMessage = constants.STORAGE_QUOTA_EXCEEDED;
break;
case errorCodes.ERR_NO_INTERNET_CONNECTION:
errorMessage = constants.NO_INTERNET_CONNECTION;
break;
default:
errorMessage = `Unknown Error Code - ${bannerErrorCode} Encountered`;
}
return errorMessage;
}

View file

@ -0,0 +1,9 @@
import {errorCodes} from './errorUtil';
export function checkConnectivity() {
if (navigator.onLine) {
return true;
} else {
throw new Error(errorCodes.ERR_NO_INTERNET_CONNECTION);
}
}

View file

@ -82,6 +82,8 @@ const englishConstants = {
ZOOM_IN_OUT: 'Zoom in/out',
PREVIOUS: 'Previous (arrow left)',
NEXT: 'Next (arrow right)',
NO_INTERNET_CONNECTION:
'You seem to be offline please check your internet connection and try again',
};
export default englishConstants;