diff --git a/src/pages/gallery/components/AlertBanner.tsx b/src/pages/gallery/components/AlertBanner.tsx new file mode 100644 index 000000000..eae6dbb89 --- /dev/null +++ b/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 ( + + {ErrorBannerMessage(bannerErrorCode)} + + ); +} diff --git a/src/pages/gallery/components/CollectionDropZone.tsx b/src/pages/gallery/components/CollectionDropZone.tsx index e7fc6ca23..5f3baed8e 100644 --- a/src/pages/gallery/components/CollectionDropZone.tsx +++ b/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); } }; diff --git a/src/pages/gallery/components/CreateCollection.tsx b/src/pages/gallery/components/CreateCollection.tsx index 4f0c7022f..4cb755f51 100644 --- a/src/pages/gallery/components/CreateCollection.tsx +++ b/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 ( - + {constants.CREATE_COLLECTION} diff --git a/src/pages/gallery/components/ErrorAlert.tsx b/src/pages/gallery/components/ErrorAlert.tsx deleted file mode 100644 index 707b43e94..000000000 --- a/src/pages/gallery/components/ErrorAlert.tsx +++ /dev/null @@ -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 ( - - {errorMessage} - - ) -} diff --git a/src/pages/gallery/components/PreviewCard.tsx b/src/pages/gallery/components/PreviewCard.tsx index f0d30782f..4abac69f7 100644 --- a/src/pages/gallery/components/PreviewCard.tsx +++ b/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; diff --git a/src/pages/gallery/components/Upload.tsx b/src/pages/gallery/components/Upload.tsx index 0f2634478..b591e4d61 100644 --- a/src/pages/gallery/components/Upload.tsx +++ b/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([]); const init = () => { setProgressView(false); setUploadStage(UPLOAD_STAGES.START); @@ -26,12 +27,15 @@ export default function Upload(props) { setFileCounter, setUploadStage, }} + setUploadErrors={setUploadErrors} /> setProgressView(false)} onHide={init} /> > diff --git a/src/pages/gallery/components/UploadProgress.tsx b/src/pages/gallery/components/UploadProgress.tsx index 0824e2691..7f08714ba 100644 --- a/src/pages/gallery/components/UploadProgress.tsx +++ b/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({ > )} + {uploadErrors.length > 0 && ( + <> + + + {uploadErrors.map((error) => ( + {error.message} + ))} + + + > + )} + {now === 100 && ( + + + {constants.CLOSE} + + + )} ); diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index b95726dbc..1f916fb5f 100644 --- a/src/pages/gallery/index.tsx +++ b/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(0); const fetching: { [k: number]: boolean } = {}; - const [errorCode, setErrorCode] = useState(null); + const [bannerErrorCode, setBannerErrorCode] = useState(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) { )} - + {filteredData.length ? ( @@ -413,7 +413,7 @@ export default function Gallery(props) { 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 ? ( { timeStampList[ @@ -456,7 +456,7 @@ export default function Gallery(props) { index ] .itemStartIndex + - idx + idx ); } ) diff --git a/src/services/collectionService.ts b/src/services/collectionService.ts index 3e9730740..b75812b59 100644 --- a/src/services/collectionService.ts +++ b/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 => { }; export const getCollectionUpdationTime = async (): Promise => { - return await localForage.getItem(COLLECTION_UPDATION_TIME) ?? 0; -} + return (await localForage.getItem(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); } }; diff --git a/src/services/downloadManager.ts b/src/services/downloadManager.ts index 3f475e1bf..2a701efb6 100644 --- a/src/services/downloadManager.ts +++ b/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); diff --git a/src/services/fileService.ts b/src/services/fileService.ts index 47c088105..31d88995d 100644 --- a/src/services/fileService.ts +++ b/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 ); } }; diff --git a/src/services/uploadService.ts b/src/services/uploadService.ts index 59a7e50aa..58445746d 100644 --- a/src/services/uploadService.ts +++ b/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; 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(); 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 { @@ -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) { diff --git a/src/utils/common/errorUtil.ts b/src/utils/common/errorUtil.ts new file mode 100644 index 000000000..3439001c2 --- /dev/null +++ b/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; +} diff --git a/src/utils/common/utilFunctions.ts b/src/utils/common/utilFunctions.ts new file mode 100644 index 000000000..f83f1292f --- /dev/null +++ b/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); + } +} diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index a13f97e60..45801fa2b 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/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;