Forráskód Böngészése

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

Handle b2 errors
Vishnu Mohandas 4 éve
szülő
commit
40f793f3fd

+ 15 - 0
src/pages/gallery/components/AlertBanner.tsx

@@ -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>
+    );
+}

+ 5 - 6
src/pages/gallery/components/CollectionDropZone.tsx

@@ -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);
         }
     };

+ 12 - 8
src/pages/gallery/components/CreateCollection.tsx

@@ -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>

+ 0 - 21
src/pages/gallery/components/ErrorAlert.tsx

@@ -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>
-    )
-}

+ 2 - 1
src/pages/gallery/components/PreviewCard.tsx

@@ -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;

+ 4 - 0
src/pages/gallery/components/Upload.tsx

@@ -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}
             />
         </>

+ 30 - 1
src/pages/gallery/components/UploadProgress.tsx

@@ -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>
     );

+ 9 - 9
src/pages/gallery/index.tsx

@@ -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
                                                                 );
                                                             }
                                                         )

+ 6 - 6
src/services/collectionService.ts

@@ -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);
     }
 };
 

+ 2 - 2
src/services/downloadManager.ts

@@ -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);

+ 1 - 1
src/services/fileService.ts

@@ -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 );
     }
 };
 

+ 69 - 50
src/services/uploadService.ts

@@ -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) {

+ 38 - 0
src/utils/common/errorUtil.ts

@@ -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;
+}

+ 9 - 0
src/utils/common/utilFunctions.ts

@@ -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);
+    }
+}

+ 2 - 0
src/utils/strings/englishConstants.tsx

@@ -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;