Browse Source

formatted source files

Abhinav-grd 4 years ago
parent
commit
ed049bac9a
100 changed files with 2334 additions and 1715 deletions
  1. 1 1
      .prettierrc.json
  2. 1 2
      src/components/AddToCollectionBtn.tsx
  3. 49 39
      src/components/ChangeEmail.tsx
  4. 7 12
      src/components/CollectionShare.tsx
  5. 13 12
      src/components/Container.ts
  6. 1 2
      src/components/DeleteBtn.tsx
  7. 24 11
      src/components/EnteCard.tsx
  8. 61 23
      src/components/ExportFinished.tsx
  9. 58 18
      src/components/ExportInProgress.tsx
  10. 9 8
      src/components/ExportInit.tsx
  11. 98 44
      src/components/ExportModal.tsx
  12. 2 1
      src/components/FavButton.tsx
  13. 8 4
      src/components/FlashMessageBar.tsx
  14. 5 9
      src/components/FullScreenDropZone.tsx
  15. 2 5
      src/components/IncognitoWarning.tsx
  16. 10 13
      src/components/Login.tsx
  17. 21 18
      src/components/MessageDialog.tsx
  18. 24 10
      src/components/NavigationButton.tsx
  19. 136 80
      src/components/PhotoFrame.tsx
  20. 9 11
      src/components/PhotoSwipe/PhotoSwipe.tsx
  21. 6 9
      src/components/RecoveryKeyModal.tsx
  22. 23 23
      src/components/SearchBar.tsx
  23. 8 11
      src/components/SetPasswordForm.tsx
  24. 67 50
      src/components/Sidebar.tsx
  25. 109 111
      src/components/SignUp.tsx
  26. 16 12
      src/components/SingleInputForm.tsx
  27. 8 6
      src/components/SubmitButton.tsx
  28. 72 37
      src/components/TwoFactorModal.tsx
  29. 34 17
      src/components/VerifyTwoFactor.tsx
  30. 9 3
      src/components/icons/AddIcon.tsx
  31. 1 2
      src/components/icons/ArrowEast.tsx
  32. 1 2
      src/components/icons/CloudUpload.tsx
  33. 1 2
      src/components/icons/CrossIcon.tsx
  34. 1 2
      src/components/icons/DateIcon.tsx
  35. 1 2
      src/components/icons/DeleteIcon.tsx
  36. 3 6
      src/components/icons/ExpandLess.tsx
  37. 3 6
      src/components/icons/ExpandMore.tsx
  38. 1 4
      src/components/icons/FolderIcon.tsx
  39. 7 7
      src/components/icons/InProgressIcon.tsx
  40. 1 2
      src/components/icons/LocationIcon.tsx
  41. 1 2
      src/components/icons/NavigateNext.tsx
  42. 1 2
      src/components/icons/PlayCircleOutline.tsx
  43. 1 2
      src/components/icons/SadFace.tsx
  44. 1 2
      src/components/icons/SearchIcon.tsx
  45. 3 3
      src/components/icons/Visibility.tsx
  46. 6 3
      src/components/icons/VisibilityOff.tsx
  47. 5 3
      src/components/icons/power_settings.tsx
  48. 10 10
      src/components/pages/gallery/AlertBanner.tsx
  49. 5 10
      src/components/pages/gallery/ChoiceModal.tsx
  50. 5 10
      src/components/pages/gallery/CollectionNamer.tsx
  51. 8 11
      src/components/pages/gallery/CollectionOptions.tsx
  52. 1 1
      src/components/pages/gallery/CollectionSelector.tsx
  53. 45 31
      src/components/pages/gallery/Collections.tsx
  54. 2 3
      src/components/pages/gallery/LinkButton.tsx
  55. 6 5
      src/components/pages/gallery/OptionIcon.tsx
  56. 89 81
      src/components/pages/gallery/PlanSelector.tsx
  57. 2 3
      src/components/pages/gallery/PreviewCard.tsx
  58. 30 20
      src/components/pages/gallery/SelectedFileOptions.tsx
  59. 31 23
      src/components/pages/gallery/Upload.tsx
  60. 1 2
      src/components/pages/gallery/UploadButton.tsx
  61. 150 114
      src/components/pages/gallery/UploadProgress.tsx
  62. 54 37
      src/pages/_app.tsx
  63. 20 9
      src/pages/_document.tsx
  64. 2 1
      src/pages/api/[...all].ts
  65. 38 38
      src/pages/change-email/index.tsx
  66. 3 2
      src/pages/change-password/index.tsx
  67. 12 14
      src/pages/credentials/index.tsx
  68. 6 6
      src/pages/gallery/index.tsx
  69. 3 3
      src/pages/generate/index.tsx
  70. 74 57
      src/pages/index.tsx
  71. 15 10
      src/pages/login/index.tsx
  72. 5 10
      src/pages/recover/index.tsx
  73. 6 5
      src/pages/signup/index.tsx
  74. 7 11
      src/pages/two-factor/recover/index.tsx
  75. 92 34
      src/pages/two-factor/setup/index.tsx
  76. 8 6
      src/pages/two-factor/verify/index.tsx
  77. 16 7
      src/pages/verify/index.tsx
  78. 19 11
      src/serviceWorker.js
  79. 12 10
      src/services/HTTPService.ts
  80. 18 16
      src/services/billingService.ts
  81. 53 45
      src/services/collectionService.ts
  82. 12 12
      src/services/downloadManager.ts
  83. 103 58
      src/services/exportService.ts
  84. 40 21
      src/services/fileService.ts
  85. 3 3
      src/services/motionPhotoService.ts
  86. 7 4
      src/services/searchService.ts
  87. 159 127
      src/services/uploadService.ts
  88. 91 71
      src/services/userService.ts
  89. 2 1
      src/types.ts
  90. 15 14
      src/utils/billingUtil.ts
  91. 2 2
      src/utils/collection/index.ts
  92. 10 3
      src/utils/common/apiUtil.ts
  93. 5 6
      src/utils/common/errorUtil.ts
  94. 6 2
      src/utils/common/index.ts
  95. 4 3
      src/utils/common/key.ts
  96. 33 18
      src/utils/crypto/index.ts
  97. 36 37
      src/utils/crypto/libsodium.ts
  98. 5 5
      src/utils/export/index.ts
  99. 18 14
      src/utils/file/index.ts
  100. 6 4
      src/utils/search/index.ts

+ 1 - 1
.prettierrc.json

@@ -1,6 +1,6 @@
 {
     "tabWidth": 4,
-    "trailingComma": "all",
+    "trailingComma": "es5",
     "singleQuote": true,
     "jsxBracketSameLine": true
 }

+ 1 - 2
src/components/AddToCollectionBtn.tsx

@@ -25,8 +25,7 @@ export default function AddToCollectionBtn(props) {
                 stroke="currentColor"
                 strokeWidth="2"
                 strokeLinecap="round"
-                strokeLinejoin="round"
-            >
+                strokeLinejoin="round">
                 <line x1="12" y1="5" x2="12" y2="19" />
                 <line x1="5" y1="12" x2="19" y2="12" />
             </svg>

+ 49 - 39
src/components/ChangeEmail.tsx

@@ -1,4 +1,3 @@
-
 import { Formik, FormikHelpers } from 'formik';
 import React, { useContext, useEffect, useRef, useState } from 'react';
 import { Button, Col, Form, FormControl } from 'react-bootstrap';
@@ -13,10 +12,10 @@ import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
 
 interface formValues {
     email: string;
-    ott?:string;
+    ott?: string;
 }
 
-const EmailRow =styled.div`
+const EmailRow = styled.div`
     display: flex;
     flex-wrap: wrap;
     border: 1px solid grey;
@@ -26,15 +25,15 @@ const EmailRow =styled.div`
     color: #fff;
 `;
 
-interface Props{
-showMessage:(value:boolean)=>void;
-setEmail:(email:string)=>void;
+interface Props {
+    showMessage: (value: boolean) => void;
+    setEmail: (email: string) => void;
 }
-function ChangeEmailForm(props:Props) {
-    const [loading, setLoading]=useState(false);
-    const [ottInputVisible, setShowOttInputVisibility]=useState(false);
+function ChangeEmailForm(props: Props) {
+    const [loading, setLoading] = useState(false);
+    const [ottInputVisible, setShowOttInputVisibility] = useState(false);
     const emailInputElement = useRef(null);
-    const ottInputRef=useRef(null);
+    const ottInputRef = useRef(null);
     const appContext = useContext(AppContext);
 
     useEffect(() => {
@@ -43,14 +42,16 @@ function ChangeEmailForm(props:Props) {
         }, 250);
     }, []);
 
-    useEffect(()=>{
+    useEffect(() => {
         if (!ottInputVisible) {
             props.showMessage(false);
         }
     }, [ottInputVisible]);
 
-    const requestOTT= async( { email }: formValues,
-        { setFieldError }: FormikHelpers<formValues>)=>{
+    const requestOTT = async (
+        { email }: formValues,
+        { setFieldError }: FormikHelpers<formValues>
+    ) => {
         try {
             setLoading(true);
             await getOTTForEmailChange(email);
@@ -66,14 +67,18 @@ function ChangeEmailForm(props:Props) {
         setLoading(false);
     };
 
-
-    const requestEmailChange= async( { email, ott }: formValues,
-        { setFieldError }: FormikHelpers<formValues>)=>{
+    const requestEmailChange = async (
+        { email, ott }: formValues,
+        { setFieldError }: FormikHelpers<formValues>
+    ) => {
         try {
             setLoading(true);
             await changeEmail(email, ott);
             setData(LS_KEYS.USER, { ...getData(LS_KEYS.USER), email });
-            appContext.setDisappearingFlashMessage({ message: constants.EMAIL_UDPATE_SUCCESSFUL, severity: 'success' });
+            appContext.setDisappearingFlashMessage({
+                message: constants.EMAIL_UDPATE_SUCCESSFUL,
+                severity: 'success',
+            });
             router.push('/gallery');
         } catch (e) {
             setFieldError('ott', `${constants.INCORRECT_CODE}`);
@@ -91,18 +96,11 @@ function ChangeEmailForm(props:Props) {
             })}
             validateOnChange={false}
             validateOnBlur={false}
-            onSubmit={!ottInputVisible?requestOTT:requestEmailChange}
-        >
-            {({
-                values,
-                errors,
-                touched,
-                handleChange,
-                handleSubmit,
-            }) => (
+            onSubmit={!ottInputVisible ? requestOTT : requestEmailChange}>
+            {({ values, errors, touched, handleChange, handleSubmit }) => (
                 <Form noValidate onSubmit={handleSubmit}>
-                    {!ottInputVisible
-                        ? <Form.Group controlId="formBasicEmail">
+                    {!ottInputVisible ? (
+                        <Form.Group controlId="formBasicEmail">
                             <Form.Control
                                 ref={emailInputElement}
                                 type="email"
@@ -110,7 +108,7 @@ function ChangeEmailForm(props:Props) {
                                 value={values.email}
                                 onChange={handleChange('email')}
                                 isInvalid={Boolean(
-                                    touched.email && errors.email,
+                                    touched.email && errors.email
                                 )}
                                 autoFocus
                                 disabled={loading}
@@ -119,13 +117,16 @@ function ChangeEmailForm(props:Props) {
                                 {errors.email}
                             </FormControl.Feedback>
                         </Form.Group>
-                        : <>
+                    ) : (
+                        <>
                             <EmailRow>
-                                <Col xs="8">
-                                    {values.email}
-                                </Col>
-                                <Col xs ="4" >
-                                    <Button variant="link" onClick={()=>setShowOttInputVisibility(false)}>
+                                <Col xs="8">{values.email}</Col>
+                                <Col xs="4">
+                                    <Button
+                                        variant="link"
+                                        onClick={() =>
+                                            setShowOttInputVisibility(false)
+                                        }>
                                         {constants.CHANGE}
                                     </Button>
                                 </Col>
@@ -138,7 +139,7 @@ function ChangeEmailForm(props:Props) {
                                     value={values.ott}
                                     onChange={handleChange('ott')}
                                     isInvalid={Boolean(
-                                        touched.ott && errors.ott,
+                                        touched.ott && errors.ott
                                     )}
                                     disabled={loading}
                                 />
@@ -146,14 +147,23 @@ function ChangeEmailForm(props:Props) {
                                     {errors.ott}
                                 </FormControl.Feedback>
                             </Form.Group>
-                        </>}
+                        </>
+                    )}
 
                     <SubmitButton
-                        buttonText={!ottInputVisible?constants.SEND_OTT:constants.VERIFY}
+                        buttonText={
+                            !ottInputVisible
+                                ? constants.SEND_OTT
+                                : constants.VERIFY
+                        }
                         loading={loading}
                     />
                     <br />
-                    <Button block variant="link" className="text-center" onClick={router.back}>
+                    <Button
+                        block
+                        variant="link"
+                        className="text-center"
+                        onClick={router.back}>
                         {constants.GO_BACK}
                     </Button>
                 </Form>

+ 7 - 12
src/components/CollectionShare.tsx

@@ -34,7 +34,7 @@ function CollectionShare(props: Props) {
     const [loading, setLoading] = useState(false);
     const collectionShare = async (
         { email }: formValues,
-        { resetForm, setFieldError }: FormikHelpers<formValues>,
+        { resetForm, setFieldError }: FormikHelpers<formValues>
     ) => {
         try {
             setLoading(true);
@@ -89,8 +89,7 @@ function CollectionShare(props: Props) {
                         fontSize: '1.2em',
                         fontWeight: 900,
                     }}
-                    onClick={() => collectionUnshare(sharee)}
-                >
+                    onClick={() => collectionUnshare(sharee)}>
                     -
                 </Button>
             </td>
@@ -100,8 +99,7 @@ function CollectionShare(props: Props) {
         <MessageDialog
             show={props.show}
             onHide={props.onHide}
-            attributes={{ title: constants.SHARE_COLLECTION }}
-        >
+            attributes={{ title: constants.SHARE_COLLECTION }}>
             <DeadCenter style={{ width: '85%', margin: 'auto' }}>
                 <h6>{constants.SHARE_WITH_PEOPLE}</h6>
                 <p />
@@ -114,8 +112,7 @@ function CollectionShare(props: Props) {
                     })}
                     validateOnChange={false}
                     validateOnBlur={false}
-                    onSubmit={collectionShare}
-                >
+                    onSubmit={collectionShare}>
                     {({
                         values,
                         errors,
@@ -128,15 +125,14 @@ function CollectionShare(props: Props) {
                                 <Form.Group
                                     as={Col}
                                     xs={10}
-                                    controlId="formHorizontalEmail"
-                                >
+                                    controlId="formHorizontalEmail">
                                     <Form.Control
                                         type="email"
                                         placeholder={constants.ENTER_EMAIL}
                                         value={values.email}
                                         onChange={handleChange('email')}
                                         isInvalid={Boolean(
-                                            touched.email && errors.email,
+                                            touched.email && errors.email
                                         )}
                                         autoFocus
                                         disabled={loading}
@@ -148,8 +144,7 @@ function CollectionShare(props: Props) {
                                 <Form.Group
                                     as={Col}
                                     xs={2}
-                                    controlId="formHorizontalEmail"
-                                >
+                                    controlId="formHorizontalEmail">
                                     <SubmitButton
                                         loading={loading}
                                         inline

+ 13 - 12
src/components/Container.ts

@@ -31,26 +31,27 @@ export const IconButton = styled.button`
     align-items: center;
     justify-content: center;
 
-    &:focus, &:hover {
+    &:focus,
+    &:hover {
         background-color: rgba(255, 255, 255, 0.2);
     }
 `;
 
 export const Row = styled.div`
-    display:flex;
-    align-items:center;
-    margin-bottom:20px;
-    flex:1
+    display: flex;
+    align-items: center;
+    margin-bottom: 20px;
+    flex: 1;
 `;
 
-export const Label = styled.div <{ width?: string }> `
-    width:${(props) => props.width ?? '70%'};
+export const Label = styled.div<{ width?: string }>`
+    width: ${(props) => props.width ?? '70%'};
 `;
-export const Value = styled.div <{ width?: string }> `
-    display:flex;
-    justify-content:flex-start;
-    align-items:center;
-    width:${(props) => props.width ?? '30%'};
+export const Value = styled.div<{ width?: string }>`
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+    width: ${(props) => props.width ?? '30%'};
     text-align: center;
     color: #ddd;
 `;

+ 1 - 2
src/components/DeleteBtn.tsx

@@ -20,8 +20,7 @@ export default function DeleteBtn(props) {
                 xmlns="http://www.w3.org/2000/svg"
                 height={props.height}
                 viewBox={props.viewBox}
-                width={props.width}
-            >
+                width={props.width}>
                 <path d="M0 0h24v24H0z" fill="none" />
                 <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" />
             </svg>

+ 24 - 11
src/components/EnteCard.tsx

@@ -1,26 +1,39 @@
 import React from 'react';
 import { Card } from 'react-bootstrap';
 
-type Size='sm'|'md'|'lg';
+type Size = 'sm' | 'md' | 'lg';
 
-const EnteCard=({ size, children, style }:{size:Size, children?:any, style?:any})=>{
-    let minWidth:string;
-    let padding:string;
+const EnteCard = ({
+    size,
+    children,
+    style,
+}: {
+    size: Size;
+    children?: any;
+    style?: any;
+}) => {
+    let minWidth: string;
+    let padding: string;
     switch (size) {
         case 'sm':
-            minWidth='320px';
-            padding='0px';
+            minWidth = '320px';
+            padding = '0px';
             break;
         case 'md':
-            minWidth='460px';
-            padding='10px';
+            minWidth = '460px';
+            padding = '10px';
             break;
 
-        default: minWidth='480px';
-            padding='10px';
+        default:
+            minWidth = '480px';
+            padding = '10px';
             break;
     }
-    return (<Card style={{ minWidth, padding, ...style }} className="text-center">{children}</Card>);
+    return (
+        <Card style={{ minWidth, padding, ...style }} className="text-center">
+            {children}
+        </Card>
+    );
 };
 
 export default EnteCard;

+ 61 - 23
src/components/ExportFinished.tsx

@@ -6,14 +6,13 @@ import constants from 'utils/strings/constants';
 import { Label, Row, Value } from './Container';
 import { ComfySpan } from './ExportInProgress';
 
-
 interface Props {
-    show: boolean
-    onHide: () => void
-    exportFolder: string
-    exportSize: string
-    lastExportTime: number
-    exportStats: ExportStats
+    show: boolean;
+    onHide: () => void;
+    exportFolder: string;
+    exportSize: string;
+    lastExportTime: number;
+    exportStats: ExportStats;
     updateExportFolder: (newFolder: string) => void;
     exportFiles: () => void;
     retryFailed: () => void;
@@ -23,30 +22,69 @@ export default function ExportFinished(props: Props) {
     const totalFiles = props.exportStats.failed + props.exportStats.success;
     return (
         <>
-            <div style={{ borderBottom: '1px solid #444', marginBottom: '20px', padding: '0 5%' }}>
+            <div
+                style={{
+                    borderBottom: '1px solid #444',
+                    marginBottom: '20px',
+                    padding: '0 5%',
+                }}>
                 <Row>
                     <Label width="40%">{constants.LAST_EXPORT_TIME}</Label>
-                    <Value width="60%">{formatDateTime(props.lastExportTime)}</Value>
-                </Row>
-                <Row>
-                    <Label width="60%">{constants.SUCCESSFULLY_EXPORTED_FILES}</Label>
-                    <Value width="35%"><ComfySpan>{props.exportStats.success} / {totalFiles}</ComfySpan></Value>
+                    <Value width="60%">
+                        {formatDateTime(props.lastExportTime)}
+                    </Value>
                 </Row>
-                {props.exportStats.failed>0 &&
                 <Row>
-                    <Label width="60%">{constants.FAILED_EXPORTED_FILES}</Label>
+                    <Label width="60%">
+                        {constants.SUCCESSFULLY_EXPORTED_FILES}
+                    </Label>
                     <Value width="35%">
-                        <ComfySpan>{props.exportStats.failed} / {totalFiles}</ComfySpan>
+                        <ComfySpan>
+                            {props.exportStats.success} / {totalFiles}
+                        </ComfySpan>
                     </Value>
-                </Row>}
+                </Row>
+                {props.exportStats.failed > 0 && (
+                    <Row>
+                        <Label width="60%">
+                            {constants.FAILED_EXPORTED_FILES}
+                        </Label>
+                        <Value width="35%">
+                            <ComfySpan>
+                                {props.exportStats.failed} / {totalFiles}
+                            </ComfySpan>
+                        </Value>
+                    </Row>
+                )}
             </div>
-            <div style={{ width: '100%', display: 'flex', justifyContent: 'space-around' }}>
-                <Button block variant={'outline-secondary'} onClick={props.onHide}>{constants.CLOSE}</Button>
+            <div
+                style={{
+                    width: '100%',
+                    display: 'flex',
+                    justifyContent: 'space-around',
+                }}>
+                <Button
+                    block
+                    variant={'outline-secondary'}
+                    onClick={props.onHide}>
+                    {constants.CLOSE}
+                </Button>
                 <div style={{ width: '30px' }} />
-                {props.exportStats.failed !== 0
-                    ? <Button block variant={'outline-danger'} onClick={props.retryFailed}>{constants.RETRY_EXPORT_}</Button>
-                    : <Button block variant={'outline-success'} onClick={props.exportFiles}>{constants.EXPORT_AGAIN}</Button>
-                }
+                {props.exportStats.failed !== 0 ? (
+                    <Button
+                        block
+                        variant={'outline-danger'}
+                        onClick={props.retryFailed}>
+                        {constants.RETRY_EXPORT_}
+                    </Button>
+                ) : (
+                    <Button
+                        block
+                        variant={'outline-success'}
+                        onClick={props.exportFiles}>
+                        {constants.EXPORT_AGAIN}
+                    </Button>
+                )}
             </div>
         </>
     );

+ 58 - 18
src/components/ExportInProgress.tsx

@@ -5,42 +5,82 @@ import styled from 'styled-components';
 import constants from 'utils/strings/constants';
 
 export const ComfySpan = styled.span`
-    word-spacing:1rem;
-    color:#ddd;
+    word-spacing: 1rem;
+    color: #ddd;
 `;
 
 interface Props {
-    show: boolean
-    onHide: () => void
-    exportFolder: string
-    exportSize: string
-    exportStage: ExportStage
-    exportProgress: ExportProgress
+    show: boolean;
+    onHide: () => void;
+    exportFolder: string;
+    exportSize: string;
+    exportStage: ExportStage;
+    exportProgress: ExportProgress;
     resumeExport: () => void;
-    cancelExport: () => void
+    cancelExport: () => void;
     pauseExport: () => void;
 }
 export default function ExportInProgress(props: Props) {
     return (
         <>
-            <div style={{ marginBottom: '30px', padding: '0 5%', display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
+            <div
+                style={{
+                    marginBottom: '30px',
+                    padding: '0 5%',
+                    display: 'flex',
+                    alignItems: 'center',
+                    flexDirection: 'column',
+                }}>
                 <div style={{ marginBottom: '10px' }}>
-                    <ComfySpan> {props.exportProgress.current} / {props.exportProgress.total} </ComfySpan> <span style={{ marginLeft: '10px' }}> files exported {props.exportStage === ExportStage.PAUSED && `(paused)`}</span>
+                    <ComfySpan>
+                        {' '}
+                        {props.exportProgress.current} /{' '}
+                        {props.exportProgress.total}{' '}
+                    </ComfySpan>{' '}
+                    <span style={{ marginLeft: '10px' }}>
+                        {' '}
+                        files exported{' '}
+                        {props.exportStage === ExportStage.PAUSED && `(paused)`}
+                    </span>
                 </div>
                 <div style={{ width: '100%', marginBottom: '30px' }}>
                     <ProgressBar
-                        now={Math.round(props.exportProgress.current * 100 / props.exportProgress.total)}
+                        now={Math.round(
+                            (props.exportProgress.current * 100) /
+                                props.exportProgress.total
+                        )}
                         animated={!(props.exportStage === ExportStage.PAUSED)}
                         variant="upload-progress-bar"
                     />
                 </div>
-                <div style={{ width: '100%', display: 'flex', justifyContent: 'space-around' }}>
-                    {props.exportStage === ExportStage.PAUSED
-                        ? <Button block variant={'outline-secondary'} onClick={props.resumeExport}>{constants.RESUME}</Button>
-                        : <Button block variant={'outline-secondary'} onClick={props.pauseExport}>{constants.PAUSE}</Button>
-                    }
+                <div
+                    style={{
+                        width: '100%',
+                        display: 'flex',
+                        justifyContent: 'space-around',
+                    }}>
+                    {props.exportStage === ExportStage.PAUSED ? (
+                        <Button
+                            block
+                            variant={'outline-secondary'}
+                            onClick={props.resumeExport}>
+                            {constants.RESUME}
+                        </Button>
+                    ) : (
+                        <Button
+                            block
+                            variant={'outline-secondary'}
+                            onClick={props.pauseExport}>
+                            {constants.PAUSE}
+                        </Button>
+                    )}
                     <div style={{ width: '30px' }} />
-                    <Button block variant={'outline-danger'} onClick={props.cancelExport}>{constants.CANCEL}</Button>
+                    <Button
+                        block
+                        variant={'outline-danger'}
+                        onClick={props.cancelExport}>
+                        {constants.CANCEL}
+                    </Button>
                 </div>
             </div>
         </>

+ 9 - 8
src/components/ExportInit.tsx

@@ -4,18 +4,18 @@ import { Button } from 'react-bootstrap';
 import constants from 'utils/strings/constants';
 
 interface Props {
-    show: boolean
-    onHide: () => void
+    show: boolean;
+    onHide: () => void;
     updateExportFolder: (newFolder: string) => void;
-    exportFolder: string
-    startExport: () => void
+    exportFolder: string;
+    startExport: () => void;
     exportSize: string;
-    selectExportDirectory: () => void
+    selectExportDirectory: () => void;
 }
 export default function ExportInit(props: Props) {
     return (
         <>
-            <DeadCenter >
+            <DeadCenter>
                 <Button
                     variant="outline-success"
                     size="lg"
@@ -26,8 +26,9 @@ export default function ExportInit(props: Props) {
                         flex: 1,
                         whiteSpace: 'nowrap',
                     }}
-                    onClick={props.startExport}
-                >{constants.START}</Button>
+                    onClick={props.startExport}>
+                    {constants.START}
+                </Button>
             </DeadCenter>
         </>
     );

+ 98 - 44
src/components/ExportModal.tsx

@@ -1,7 +1,12 @@
 import isElectron from 'is-electron';
 import React, { useEffect, useState } from 'react';
 import { Button } from 'react-bootstrap';
-import exportService, { ExportProgress, ExportStage, ExportStats, ExportType } from 'services/exportService';
+import exportService, {
+    ExportProgress,
+    ExportStage,
+    ExportStats,
+    ExportType,
+} from 'services/exportService';
 import { getLocalFiles } from 'services/fileService';
 import styled from 'styled-components';
 import { sleep } from 'utils/common';
@@ -18,38 +23,44 @@ import MessageDialog from './MessageDialog';
 
 const FolderIconWrapper = styled.div`
     width: 15%;
-    margin-left: 10px; 
-    cursor: pointer; 
+    margin-left: 10px;
+    cursor: pointer;
     padding: 3px;
     border: 1px solid #444;
-    border-radius:15%;
-    &:hover{
-        background-color:#444;
+    border-radius: 15%;
+    &:hover {
+        background-color: #444;
     }
 `;
 
-const ExportFolderPathContainer =styled.span`
-    white-space: nowrap;                   
+const ExportFolderPathContainer = styled.span`
+    white-space: nowrap;
     overflow: hidden;
-    text-overflow: ellipsis;  
+    text-overflow: ellipsis;
     width: 200px;
-    
+
     /* Beginning of string */
     direction: rtl;
     text-align: left;
 `;
 
 interface Props {
-    show: boolean
-    onHide: () => void
-    usage: string
+    show: boolean;
+    onHide: () => void;
+    usage: string;
 }
 export default function ExportModal(props: Props) {
     const [exportStage, setExportStage] = useState(ExportStage.INIT);
     const [exportFolder, setExportFolder] = useState('');
     const [exportSize, setExportSize] = useState('');
-    const [exportProgress, setExportProgress] = useState<ExportProgress>({ current: 0, total: 0 });
-    const [exportStats, setExportStats] = useState<ExportStats>({ failed: 0, success: 0 });
+    const [exportProgress, setExportProgress] = useState<ExportProgress>({
+        current: 0,
+        total: 0,
+    });
+    const [exportStats, setExportStats] = useState<ExportStats>({
+        failed: 0,
+        success: 0,
+    });
     const [lastExportTime, setLastExportTime] = useState(0);
 
     // ====================
@@ -64,7 +75,9 @@ export default function ExportModal(props: Props) {
         exportService.ElectronAPIs.registerStopExportListener(stopExport);
         exportService.ElectronAPIs.registerPauseExportListener(pauseExport);
         exportService.ElectronAPIs.registerResumeExportListener(resumeExport);
-        exportService.ElectronAPIs.registerRetryFailedExportListener(retryFailedExport);
+        exportService.ElectronAPIs.registerRetryFailedExportListener(
+            retryFailedExport
+        );
     }, []);
 
     useEffect(() => {
@@ -76,7 +89,10 @@ export default function ExportModal(props: Props) {
             setExportStage(exportInfo?.stage ?? ExportStage.INIT);
             setLastExportTime(exportInfo?.lastAttemptTimestamp);
             setExportProgress(exportInfo?.progress ?? { current: 0, total: 0 });
-            setExportStats({ success: exportInfo?.exportedFiles?.length ?? 0, failed: exportInfo?.failedFiles?.length ?? 0 });
+            setExportStats({
+                success: exportInfo?.exportedFiles?.length ?? 0,
+                failed: exportInfo?.failedFiles?.length ?? 0,
+            });
             if (exportInfo?.stage === ExportStage.INPROGRESS) {
                 resumeExport();
             }
@@ -96,10 +112,22 @@ export default function ExportModal(props: Props) {
                 const failedFilesCnt = exportRecord.failedFiles.length;
                 const syncedFilesCnt = localFiles.length;
                 if (syncedFilesCnt > exportedFileCnt + failedFilesCnt) {
-                    updateExportProgress({ current: exportedFileCnt + failedFilesCnt, total: syncedFilesCnt });
-                    const exportFileUIDs = new Set([...exportRecord.exportedFiles, ...exportRecord.failedFiles]);
-                    const unExportedFiles = localFiles.filter((file) => !exportFileUIDs.has(getExportRecordFileUID(file)));
-                    exportService.addFilesQueuedRecord(exportFolder, unExportedFiles);
+                    updateExportProgress({
+                        current: exportedFileCnt + failedFilesCnt,
+                        total: syncedFilesCnt,
+                    });
+                    const exportFileUIDs = new Set([
+                        ...exportRecord.exportedFiles,
+                        ...exportRecord.failedFiles,
+                    ]);
+                    const unExportedFiles = localFiles.filter(
+                        (file) =>
+                            !exportFileUIDs.has(getExportRecordFileUID(file))
+                    );
+                    exportService.addFilesQueuedRecord(
+                        exportFolder,
+                        unExportedFiles
+                    );
                     updateExportStage(ExportStage.PAUSED);
                 }
             }
@@ -107,7 +135,6 @@ export default function ExportModal(props: Props) {
         main();
     }, [props.show]);
 
-
     useEffect(() => {
         setExportSize(props.usage);
     }, [props.usage]);
@@ -162,7 +189,10 @@ export default function ExportModal(props: Props) {
     const startExport = async () => {
         await preExportRun();
         updateExportProgress({ current: 0, total: 0 });
-        const { paused } = await exportService.exportFiles(updateExportProgress, ExportType.NEW);
+        const { paused } = await exportService.exportFiles(
+            updateExportProgress,
+            ExportType.NEW
+        );
         await postExportRun(paused);
     };
 
@@ -184,13 +214,15 @@ export default function ExportModal(props: Props) {
         const pausedStageProgress = exportRecord.progress;
         setExportProgress(pausedStageProgress);
 
-        const updateExportStatsWithOffset = ((progress: ExportProgress) => updateExportProgress(
-            {
+        const updateExportStatsWithOffset = (progress: ExportProgress) =>
+            updateExportProgress({
                 current: pausedStageProgress.current + progress.current,
                 total: pausedStageProgress.current + progress.total,
-            },
-        ));
-        const { paused } = await exportService.exportFiles(updateExportStatsWithOffset, ExportType.PENDING);
+            });
+        const { paused } = await exportService.exportFiles(
+            updateExportStatsWithOffset,
+            ExportType.PENDING
+        );
 
         await postExportRun(paused);
     };
@@ -199,7 +231,10 @@ export default function ExportModal(props: Props) {
         await preExportRun();
         updateExportProgress({ current: 0, total: exportStats.failed });
 
-        const { paused } = await exportService.exportFiles(updateExportProgress, ExportType.RETRY_FAILED);
+        const { paused } = await exportService.exportFiles(
+            updateExportProgress,
+            ExportType.RETRY_FAILED
+        );
         await postExportRun(paused);
     };
 
@@ -224,7 +259,8 @@ export default function ExportModal(props: Props) {
         switch (exportStage) {
             case ExportStage.INIT:
                 return (
-                    <ExportInit {...props}
+                    <ExportInit
+                        {...props}
                         exportFolder={exportFolder}
                         exportSize={exportSize}
                         updateExportFolder={updateExportFolder}
@@ -235,7 +271,8 @@ export default function ExportModal(props: Props) {
             case ExportStage.INPROGRESS:
             case ExportStage.PAUSED:
                 return (
-                    <ExportInProgress {...props}
+                    <ExportInProgress
+                        {...props}
                         exportFolder={exportFolder}
                         exportSize={exportSize}
                         exportStage={exportStage}
@@ -259,7 +296,8 @@ export default function ExportModal(props: Props) {
                     />
                 );
 
-            default: return (<></>);
+            default:
+                return <></>;
         }
     };
 
@@ -269,34 +307,50 @@ export default function ExportModal(props: Props) {
             onHide={props.onHide}
             attributes={{
                 title: constants.EXPORT_DATA,
-            }}
-        >
-            <div style={{ borderBottom: '1px solid #444', marginBottom: '20px', padding: '0 5%', width: '450px' }}>
+            }}>
+            <div
+                style={{
+                    borderBottom: '1px solid #444',
+                    marginBottom: '20px',
+                    padding: '0 5%',
+                    width: '450px',
+                }}>
                 <Row>
                     <Label width="40%">{constants.DESTINATION}</Label>
                     <Value width="60%">
-                        {!exportFolder
-                            ? (<Button variant={'outline-success'} size={'sm'} onClick={selectExportDirectory}>{constants.SELECT_FOLDER}</Button>)
-                            : (<>
+                        {!exportFolder ? (
+                            <Button
+                                variant={'outline-success'}
+                                size={'sm'}
+                                onClick={selectExportDirectory}>
+                                {constants.SELECT_FOLDER}
+                            </Button>
+                        ) : (
+                            <>
                                 {/* <span style={{ overflow: 'hidden', direction: 'rtl', height: '1.5rem', width: '90%', whiteSpace: 'nowrap' }}> */}
                                 <ExportFolderPathContainer>
                                     {exportFolder}
                                 </ExportFolderPathContainer>
                                 {/* </span> */}
-                                {(exportStage === ExportStage.FINISHED || exportStage === ExportStage.INIT) && (
-                                    <FolderIconWrapper onClick={selectExportDirectory} >
+                                {(exportStage === ExportStage.FINISHED ||
+                                    exportStage === ExportStage.INIT) && (
+                                    <FolderIconWrapper
+                                        onClick={selectExportDirectory}>
                                         <FolderIcon />
                                     </FolderIconWrapper>
                                 )}
-                            </>)
-                        }
+                            </>
+                        )}
                     </Value>
                 </Row>
                 <Row>
-                    <Label width="40%">{constants.TOTAL_EXPORT_SIZE} </Label><Value width="60%">{exportSize ? `${exportSize}` : <InProgressIcon />}</Value>
+                    <Label width="40%">{constants.TOTAL_EXPORT_SIZE} </Label>
+                    <Value width="60%">
+                        {exportSize ? `${exportSize}` : <InProgressIcon />}
+                    </Value>
                 </Row>
             </div>
             <ExportDynamicState />
-        </MessageDialog >
+        </MessageDialog>
     );
 }

+ 2 - 1
src/components/FavButton.tsx

@@ -12,7 +12,8 @@ const HeartUI = styled.button<{
     cursor: pointer;
     background-size: cover;
     border: none;
-    ${({ isClick, size }) => isClick &&
+    ${({ isClick, size }) =>
+        isClick &&
         `background-position: -${
             28 * size
         }px;transition: background 1s steps(28);`}

+ 8 - 4
src/components/FlashMessageBar.tsx

@@ -2,15 +2,19 @@ import { FlashMessage } from 'pages/_app';
 import React from 'react';
 import Alert from 'react-bootstrap/Alert';
 
-
-export default function FlashMessageBar({ flashMessage, onClose }: { flashMessage: FlashMessage, onClose: () => void }) {
+export default function FlashMessageBar({
+    flashMessage,
+    onClose,
+}: {
+    flashMessage: FlashMessage;
+    onClose: () => void;
+}) {
     return (
         <Alert
             className="flash-message text-center"
             variant={flashMessage.severity}
             dismissible
-            onClose={onClose}
-        >
+            onClose={onClose}>
             <div style={{ maxWidth: '1024px', width: '80%', margin: 'auto' }}>
                 {flashMessage.message}
             </div>

+ 5 - 9
src/components/FullScreenDropZone.tsx

@@ -5,9 +5,9 @@ import CrossIcon from './icons/CrossIcon';
 
 const CloseButtonWrapper = styled.div`
     position: absolute;
-    top:10px;
-    right:10px;
-    cursor:pointer;
+    top: 10px;
+    right: 10px;
+    cursor: pointer;
 `;
 const DropDiv = styled.div`
     flex: 1;
@@ -62,14 +62,10 @@ export default function FullScreenDropZone(props: Props) {
                     e.preventDefault();
                     props.showCollectionSelector();
                 },
-            })}
-        >
+            })}>
             <input {...props.getInputProps()} />
             {isDragActive && (
-                <Overlay
-                    onDrop={onDragLeave}
-                    onDragLeave={onDragLeave}
-                >
+                <Overlay onDrop={onDragLeave} onDragLeave={onDragLeave}>
                     <CloseButtonWrapper onClick={onDragLeave}>
                         <CrossIcon />
                     </CloseButtonWrapper>

+ 2 - 5
src/components/IncognitoWarning.tsx

@@ -11,11 +11,8 @@ export default function IncognitoWarning() {
                 title: constants.LOCAL_STORAGE_NOT_ACCESSIBLE,
                 staticBackdrop: true,
                 nonClosable: true,
-            }}
-        >
-            <div>
-                {constants.LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE}
-            </div>
+            }}>
+            <div>{constants.LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE}</div>
         </MessageDialog>
     );
 }

+ 10 - 13
src/components/Login.tsx

@@ -17,7 +17,7 @@ interface formValues {
 }
 
 interface LoginProps {
-    signUp: () => void
+    signUp: () => void;
 }
 
 export default function Login(props: LoginProps) {
@@ -39,7 +39,7 @@ export default function Login(props: LoginProps) {
 
     const loginUser = async (
         { email }: formValues,
-        { setFieldError }: FormikHelpers<formValues>,
+        { setFieldError }: FormikHelpers<formValues>
     ) => {
         try {
             setWaiting(true);
@@ -73,15 +73,8 @@ export default function Login(props: LoginProps) {
                 })}
                 validateOnChange={false}
                 validateOnBlur={false}
-                onSubmit={loginUser}
-            >
-                {({
-                    values,
-                    errors,
-                    touched,
-                    handleChange,
-                    handleSubmit,
-                }) => (
+                onSubmit={loginUser}>
+                {({ values, errors, touched, handleChange, handleSubmit }) => (
                     <Form noValidate onSubmit={handleSubmit}>
                         <Form.Group controlId="formBasicEmail">
                             <Form.Control
@@ -91,7 +84,7 @@ export default function Login(props: LoginProps) {
                                 value={values.email}
                                 onChange={handleChange('email')}
                                 isInvalid={Boolean(
-                                    touched.email && errors.email,
+                                    touched.email && errors.email
                                 )}
                                 autoFocus
                                 disabled={loading}
@@ -105,7 +98,11 @@ export default function Login(props: LoginProps) {
                             loading={waiting}
                         />
                         <br />
-                        <Button block variant="link" className="text-center" onClick={props.signUp}>
+                        <Button
+                            block
+                            variant="link"
+                            className="text-center"
+                            onClick={props.signUp}>
                             {constants.NO_ACCOUNT}
                         </Button>
                     </Form>

+ 21 - 18
src/components/MessageDialog.tsx

@@ -7,7 +7,7 @@ export interface MessageAttributes {
     staticBackdrop?: boolean;
     nonClosable?: boolean;
     content?: any;
-    close?: { text?: string; variant?: string, action?: () => void };
+    close?: { text?: string; variant?: string; action?: () => void };
     proceed?: {
         text: string;
         action: () => void;
@@ -38,21 +38,21 @@ export default function MessageDialog({
             {...props}
             onHide={attributes.nonClosable ? () => null : props.onHide}
             centered
-            backdrop={attributes.staticBackdrop ? 'static' : 'true'}
-        >
+            backdrop={attributes.staticBackdrop ? 'static' : 'true'}>
             <Modal.Header
                 style={{ borderBottom: 'none' }}
-                closeButton={!attributes.nonClosable}
-            >
+                closeButton={!attributes.nonClosable}>
                 {attributes.title && (
-                    <Modal.Title>
-                        {attributes.title}
-                    </Modal.Title>
+                    <Modal.Title>{attributes.title}</Modal.Title>
                 )}
             </Modal.Header>
             {(children || attributes?.content) && (
                 <Modal.Body style={{ borderTop: '1px solid #444' }}>
-                    {children || <p style={{ fontSize: '1.25rem', marginBottom: 0 }}>{attributes.content}</p>}
+                    {children || (
+                        <p style={{ fontSize: '1.25rem', marginBottom: 0 }}>
+                            {attributes.content}
+                        </p>
+                    )}
                 </Modal.Body>
             )}
             {(attributes.close || attributes.proceed) && (
@@ -61,13 +61,16 @@ export default function MessageDialog({
                         style={{
                             display: 'flex',
                             flexWrap: 'wrap',
-                        }}
-                    >
+                        }}>
                         {attributes.close && (
                             <Button
-                                variant={`outline-${attributes.close?.variant ?? 'secondary'}`}
+                                variant={`outline-${
+                                    attributes.close?.variant ?? 'secondary'
+                                }`}
                                 onClick={() => {
-                                    attributes.close?.action ? attributes.close?.action() : props.onHide();
+                                    attributes.close?.action
+                                        ? attributes.close?.action()
+                                        : props.onHide();
                                 }}
                                 style={{
                                     padding: '6px 3em',
@@ -75,14 +78,15 @@ export default function MessageDialog({
                                     marginBottom: '20px',
                                     flex: 1,
                                     whiteSpace: 'nowrap',
-                                }}
-                            >
+                                }}>
                                 {attributes.close?.text ?? constants.OK}
                             </Button>
                         )}
                         {attributes.proceed && (
                             <Button
-                                variant={`outline-${attributes.proceed?.variant ?? 'primary'}`}
+                                variant={`outline-${
+                                    attributes.proceed?.variant ?? 'primary'
+                                }`}
                                 onClick={() => {
                                     attributes.proceed.action();
                                     props.onHide();
@@ -94,8 +98,7 @@ export default function MessageDialog({
                                     flex: 1,
                                     whiteSpace: 'nowrap',
                                 }}
-                                disabled={attributes.proceed.disabled}
-                            >
+                                disabled={attributes.proceed.disabled}>
                                 {attributes.proceed.text}
                             </Button>
                         )}

+ 24 - 10
src/components/NavigationButton.tsx

@@ -15,11 +15,17 @@ const Wrapper = styled.button<{ direction: SCROLL_DIRECTION }>`
     color: #eee;
     z-index: 1;
     position: absolute;
-    ${(props) => (props.direction === SCROLL_DIRECTION.LEFT ? 'margin-right: 10px;' : 'margin-left: 10px;')}
-    ${(props) => (props.direction === SCROLL_DIRECTION.LEFT ? 'left: 0;' : 'right: 0;')}
+    ${(props) =>
+        props.direction === SCROLL_DIRECTION.LEFT
+            ? 'margin-right: 10px;'
+            : 'margin-left: 10px;'}
+    ${(props) =>
+        props.direction === SCROLL_DIRECTION.LEFT ? 'left: 0;' : 'right: 0;'}
 
     & > svg {
-        ${(props) => props.direction === SCROLL_DIRECTION.LEFT && 'transform:rotate(180deg);'}
+        ${(props) =>
+            props.direction === SCROLL_DIRECTION.LEFT &&
+            'transform:rotate(180deg);'}
         border-radius: 50%;
         height: 30px;
         width: 30px;
@@ -30,25 +36,33 @@ const Wrapper = styled.button<{ direction: SCROLL_DIRECTION }>`
     }
 
     &:hover {
-        color:#fff;
+        color: #fff;
     }
 
     &::after {
         content: ' ';
-        background: linear-gradient(to ${(props) => (props.direction === SCROLL_DIRECTION.LEFT ? 'right' : 'left')}, #191919 5%, rgba(255, 255, 255, 0) 80%);
+        background: linear-gradient(
+            to
+                ${(props) =>
+                    props.direction === SCROLL_DIRECTION.LEFT
+                        ? 'right'
+                        : 'left'},
+            #191919 5%,
+            rgba(255, 255, 255, 0) 80%
+        );
         position: absolute;
         top: 0;
         width: 40px;
         height: 40px;
-        ${(props) => (props.direction === SCROLL_DIRECTION.LEFT ? 'left: 40px;' : 'right: 40px;')}
+        ${(props) =>
+            props.direction === SCROLL_DIRECTION.LEFT
+                ? 'left: 40px;'
+                : 'right: 40px;'}
     }
 `;
 
 const NavigationButton = ({ scrollDirection, ...rest }) => (
-    <Wrapper
-        direction={scrollDirection}
-        {...rest}
-    >
+    <Wrapper direction={scrollDirection} {...rest}>
         <NavigateNext />
     </Wrapper>
 );

+ 136 - 80
src/components/PhotoFrame.tsx

@@ -21,8 +21,12 @@ import { isInsideBox, isSameDay as isSameDayAnyYear } from 'utils/search';
 import { SetDialogMessage } from './MessageDialog';
 import { CustomError } from 'utils/common/errorUtil';
 import {
-    GAP_BTW_TILES, DATE_CONTAINER_HEIGHT, IMAGE_CONTAINER_MAX_HEIGHT,
-    IMAGE_CONTAINER_MAX_WIDTH, MIN_COLUMNS, SPACE_BTW_DATES,
+    GAP_BTW_TILES,
+    DATE_CONTAINER_HEIGHT,
+    IMAGE_CONTAINER_MAX_HEIGHT,
+    IMAGE_CONTAINER_MAX_WIDTH,
+    MIN_COLUMNS,
+    SPACE_BTW_DATES,
 } from 'types';
 
 const NO_OF_PAGES = 2;
@@ -68,21 +72,24 @@ const getTemplateColumns = (columns: number, groups?: number[]): string => {
         if (sum < columns) {
             groups[groups.length - 1] += columns - sum;
         }
-        return groups.map((x) => `repeat(${x}, 1fr)`).join(` ${SPACE_BTW_DATES}px `);
+        return groups
+            .map((x) => `repeat(${x}, 1fr)`)
+            .join(` ${SPACE_BTW_DATES}px `);
     } else {
         return `repeat(${columns}, 1fr)`;
     }
 };
 
-const ListContainer = styled.div<{ columns: number, groups?: number[] }>`
+const ListContainer = styled.div<{ columns: number; groups?: number[] }>`
     display: grid;
-    grid-template-columns: ${({ columns, groups }) => getTemplateColumns(columns, groups)};
+    grid-template-columns: ${({ columns, groups }) =>
+        getTemplateColumns(columns, groups)};
     grid-column-gap: ${GAP_BTW_TILES}px;
     padding: 0 24px;
     width: 100%;
     color: #fff;
 
-    @media(max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
+    @media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
         padding: 0 4px;
     }
 `;
@@ -139,7 +146,7 @@ interface Props {
     search: Search;
     setSearchStats: setSearchStats;
     deleted?: number[];
-    setDialogMessage: SetDialogMessage
+    setDialogMessage: SetDialogMessage;
 }
 
 const PhotoFrame = ({
@@ -303,14 +310,13 @@ const PhotoFrame = ({
                         video.preload = 'metadata';
                         video.src = url;
                         video.currentTime = 3;
-                        const t = setTimeout(
-                            () => {
-                                reject(
-                                    Error(`${CustomError.VIDEO_PLAYBACK_FAILED} err: wait time exceeded`),
-                                );
-                            },
-                            WAIT_FOR_VIDEO_PLAYBACK,
-                        );
+                        const t = setTimeout(() => {
+                            reject(
+                                Error(
+                                    `${CustomError.VIDEO_PLAYBACK_FAILED} err: wait time exceeded`
+                                )
+                            );
+                        }, WAIT_FOR_VIDEO_PLAYBACK);
                     });
                     item.html = `
                         <video width="320" height="240" controls>
@@ -332,7 +338,8 @@ const PhotoFrame = ({
                     };
                     setDialogMessage({
                         title: constants.VIDEO_PLAYBACK_FAILED,
-                        content: constants.VIDEO_PLAYBACK_FAILED_DOWNLOAD_INSTEAD,
+                        content:
+                            constants.VIDEO_PLAYBACK_FAILED_DOWNLOAD_INSTEAD,
                         staticBackdrop: true,
                         proceed: {
                             text: constants.DOWNLOAD,
@@ -373,7 +380,7 @@ const PhotoFrame = ({
             if (
                 search.date &&
                 !isSameDayAnyYear(search.date)(
-                    new Date(item.metadata.creationTime / 1000),
+                    new Date(item.metadata.creationTime / 1000)
                 )
             ) {
                 return false;
@@ -397,11 +404,10 @@ const PhotoFrame = ({
             return false;
         });
 
-    const isSameDay = (first, second) => (
+    const isSameDay = (first, second) =>
         first.getFullYear() === second.getFullYear() &&
         first.getMonth() === second.getMonth() &&
-        first.getDate() === second.getDate()
-    );
+        first.getDate() === second.getDate();
 
     /**
      * Checks and merge multiple dates into a single row.
@@ -410,7 +416,10 @@ const PhotoFrame = ({
      * @param columns
      * @returns
      */
-    const mergeTimeStampList = (items: TimeStampListItem[], columns: number): TimeStampListItem[] => {
+    const mergeTimeStampList = (
+        items: TimeStampListItem[],
+        columns: number
+    ): TimeStampListItem[] => {
         const newList: TimeStampListItem[] = [];
         let index = 0;
         let newIndex = 0;
@@ -423,12 +432,18 @@ const PhotoFrame = ({
                 // we can add more items to the same list.
                 if (newList[newIndex]) {
                     // Check if items can be added to same list
-                    if (newList[newIndex + 1].items.length + items[index + 1].items.length <= columns) {
+                    if (
+                        newList[newIndex + 1].items.length +
+                            items[index + 1].items.length <=
+                        columns
+                    ) {
                         newList[newIndex].dates.push({
                             date: currItem.date,
                             span: items[index + 1].items.length,
                         });
-                        newList[newIndex + 1].items = newList[newIndex + 1].items.concat(items[index + 1].items);
+                        newList[newIndex + 1].items = newList[
+                            newIndex + 1
+                        ].items.concat(items[index + 1].items);
                         index += 2;
                     } else {
                         // Adding items would exceed the number of columns.
@@ -441,10 +456,12 @@ const PhotoFrame = ({
                     newList.push({
                         ...currItem,
                         date: null,
-                        dates: [{
-                            date: currItem.date,
-                            span: items[index + 1].items.length,
-                        }],
+                        dates: [
+                            {
+                                date: currItem.date,
+                                span: items[index + 1].items.length,
+                            },
+                        ],
                     });
                     newList.push(items[index + 1]);
                     index += 2;
@@ -474,7 +491,7 @@ const PhotoFrame = ({
         <>
             {!isFirstLoad && files.length === 0 && !searchMode ? (
                 <EmptyScreen>
-                    <img height={150} src='/images/gallery.png' />
+                    <img height={150} src="/images/gallery.png" />
                     <Button
                         variant="outline-success"
                         onClick={openFileUploader}
@@ -484,8 +501,7 @@ const PhotoFrame = ({
                             paddingRight: '32px',
                             paddingTop: '12px',
                             paddingBottom: '12px',
-                        }}
-                    >
+                        }}>
                         {constants.UPLOAD_FIRST_PHOTO}
                     </Button>
                 </EmptyScreen>
@@ -493,7 +509,9 @@ const PhotoFrame = ({
                 <Container>
                     <AutoSizer>
                         {({ height, width }) => {
-                            let columns = Math.floor(width / IMAGE_CONTAINER_MAX_WIDTH);
+                            let columns = Math.floor(
+                                width / IMAGE_CONTAINER_MAX_WIDTH
+                            );
                             let listItemHeight = IMAGE_CONTAINER_MAX_HEIGHT;
                             let skipMerge = false;
                             if (columns < MIN_COLUMNS) {
@@ -506,29 +524,38 @@ const PhotoFrame = ({
                             let listItemIndex = 0;
                             let currentDate = -1;
                             filteredData.forEach((item, index) => {
-                                if (!isSameDay(new Date(item.metadata.creationTime / 1000), new Date(currentDate))) {
-                                    currentDate = item.metadata.creationTime / 1000;
-                                    const dateTimeFormat = new Intl.DateTimeFormat('en-IN', {
-                                        weekday: 'short',
-                                        year: 'numeric',
-                                        month: 'short',
-                                        day: 'numeric',
-                                    });
+                                if (
+                                    !isSameDay(
+                                        new Date(
+                                            item.metadata.creationTime / 1000
+                                        ),
+                                        new Date(currentDate)
+                                    )
+                                ) {
+                                    currentDate =
+                                        item.metadata.creationTime / 1000;
+                                    const dateTimeFormat =
+                                        new Intl.DateTimeFormat('en-IN', {
+                                            weekday: 'short',
+                                            year: 'numeric',
+                                            month: 'short',
+                                            day: 'numeric',
+                                        });
                                     timeStampList.push({
                                         itemType: ITEM_TYPE.TIME,
                                         date: isSameDay(
                                             new Date(currentDate),
-                                            new Date(),
+                                            new Date()
                                         )
                                             ? 'Today'
                                             : isSameDay(
-                                                new Date(currentDate),
-                                                new Date(Date.now() - A_DAY),
-                                            )
-                                                ? 'Yesterday'
-                                                : dateTimeFormat.format(
-                                                    currentDate,
-                                                ),
+                                                  new Date(currentDate),
+                                                  new Date(Date.now() - A_DAY)
+                                              )
+                                            ? 'Yesterday'
+                                            : dateTimeFormat.format(
+                                                  currentDate
+                                              ),
                                         id: currentDate.toString(),
                                     });
                                     timeStampList.push({
@@ -553,7 +580,10 @@ const PhotoFrame = ({
                             });
 
                             if (!skipMerge) {
-                                timeStampList = mergeTimeStampList(timeStampList, columns);
+                                timeStampList = mergeTimeStampList(
+                                    timeStampList,
+                                    columns
+                                );
                             }
 
                             const getItemSize = (index) => {
@@ -567,68 +597,89 @@ const PhotoFrame = ({
                                 }
                             };
 
-                            const photoFrameHeight=(()=>{
-                                let sum=0;
-                                for (let i=0; i<timeStampList.length; i++) {
-                                    sum+=getItemSize(i);
+                            const photoFrameHeight = (() => {
+                                let sum = 0;
+                                for (let i = 0; i < timeStampList.length; i++) {
+                                    sum += getItemSize(i);
                                 }
                                 return sum;
                             })();
-                            files.length < 30 && !searchMode &&
+                            files.length < 30 &&
+                                !searchMode &&
                                 timeStampList.push({
                                     itemType: ITEM_TYPE.BANNER,
                                     banner: (
                                         <BannerContainer span={columns}>
-                                            <p>{constants.INSTALL_MOBILE_APP()}</p>
+                                            <p>
+                                                {constants.INSTALL_MOBILE_APP()}
+                                            </p>
                                         </BannerContainer>
                                     ),
                                     id: 'install-banner',
-                                    height: Math.max(48, height-photoFrameHeight),
+                                    height: Math.max(
+                                        48,
+                                        height - photoFrameHeight
+                                    ),
                                 });
                             const extraRowsToRender = Math.ceil(
-                                (NO_OF_PAGES * height) / IMAGE_CONTAINER_MAX_HEIGHT,
+                                (NO_OF_PAGES * height) /
+                                    IMAGE_CONTAINER_MAX_HEIGHT
                             );
 
                             const generateKey = (index) => {
                                 switch (timeStampList[index].itemType) {
                                     case ITEM_TYPE.TILE:
-                                        return `${timeStampList[index].items[0].id}-${timeStampList[index].items.slice(-1)[0].id}`;
+                                        return `${
+                                            timeStampList[index].items[0].id
+                                        }-${
+                                            timeStampList[index].items.slice(
+                                                -1
+                                            )[0].id
+                                        }`;
                                     default:
                                         return `${timeStampList[index].id}-${index}`;
                                 }
                             };
 
-
-                            const renderListItem = (listItem: TimeStampListItem) => {
+                            const renderListItem = (
+                                listItem: TimeStampListItem
+                            ) => {
                                 switch (listItem.itemType) {
                                     case ITEM_TYPE.TIME:
-                                        return listItem.dates
-                                            ? listItem.dates.map((item) => (
+                                        return listItem.dates ? (
+                                            listItem.dates.map((item) => (
                                                 <>
-                                                    <DateContainer key={item.date} span={item.span}>
+                                                    <DateContainer
+                                                        key={item.date}
+                                                        span={item.span}>
                                                         {item.date}
                                                     </DateContainer>
                                                     <div />
                                                 </>
                                             ))
-                                            : (
-                                                <DateContainer span={columns}>
-                                                    {listItem.date}
-                                                </DateContainer>
-                                            );
+                                        ) : (
+                                            <DateContainer span={columns}>
+                                                {listItem.date}
+                                            </DateContainer>
+                                        );
                                     case ITEM_TYPE.BANNER:
                                         return listItem.banner;
-                                    default:
-                                    {
-                                        const ret = (listItem.items.map(
-                                            (item, idx) => getThumbnail(
-                                                filteredData,
-                                                listItem.itemStartIndex + idx,
-                                            ),
-                                        ));
+                                    default: {
+                                        const ret = listItem.items.map(
+                                            (item, idx) =>
+                                                getThumbnail(
+                                                    filteredData,
+                                                    listItem.itemStartIndex +
+                                                        idx
+                                                )
+                                        );
                                         if (listItem.groups) {
                                             let sum = 0;
-                                            for (let i = 0; i < listItem.groups.length - 1; i++) {
+                                            for (
+                                                let i = 0;
+                                                i < listItem.groups.length - 1;
+                                                i++
+                                            ) {
                                                 sum = sum + listItem.groups[i];
                                                 ret.splice(sum, 0, <div />);
                                                 sum += 1;
@@ -648,12 +699,17 @@ const PhotoFrame = ({
                                     width={width}
                                     itemCount={timeStampList.length}
                                     itemKey={generateKey}
-                                    overscanCount={extraRowsToRender}
-                                >
+                                    overscanCount={extraRowsToRender}>
                                     {({ index, style }) => (
                                         <ListItem style={style}>
-                                            <ListContainer columns={columns} groups={timeStampList[index].groups}>
-                                                {renderListItem(timeStampList[index])}
+                                            <ListContainer
+                                                columns={columns}
+                                                groups={
+                                                    timeStampList[index].groups
+                                                }>
+                                                {renderListItem(
+                                                    timeStampList[index]
+                                                )}
                                             </ListContainer>
                                         </ListItem>
                                     )}

+ 9 - 11
src/components/PhotoSwipe/PhotoSwipe.tsx

@@ -78,18 +78,18 @@ function ExifData(props: { exif: any }) {
                 exif?.ImageHeight &&
                 renderInfoItem(
                     constants.IMAGE_SIZE,
-                    `${exif.ImageWidth} x ${exif.ImageHeight}`,
+                    `${exif.ImageWidth} x ${exif.ImageHeight}`
                 )}
             {exif?.Flash && renderInfoItem(constants.FLASH, exif.Flash)}
             {exif?.FocalLength &&
                 renderInfoItem(
                     constants.FOCAL_LENGTH,
-                    exif.FocalLength.toString(),
+                    exif.FocalLength.toString()
                 )}
             {exif?.ApertureValue &&
                 renderInfoItem(
                     constants.APERTURE,
-                    exif.ApertureValue.toString(),
+                    exif.ApertureValue.toString()
                 )}
             {exif?.ISOSpeedRatings &&
                 renderInfoItem(constants.ISO, exif.ISOSpeedRatings.toString())}
@@ -184,7 +184,7 @@ function PhotoSwipe(props: Iprops) {
             pswpElement.current,
             PhotoswipeUIDefault,
             items,
-            options,
+            options
         );
         events.forEach((event) => {
             const callback = props[event];
@@ -265,7 +265,7 @@ function PhotoSwipe(props: Iprops) {
         setExif(null);
         setTimeout(() => {
             const img = document.querySelector(
-                '.pswp__img:not(.pswp__img--placeholder)',
+                '.pswp__img:not(.pswp__img--placeholder)'
             );
             if (img) {
                 // @ts-expect-error
@@ -404,21 +404,19 @@ function PhotoSwipe(props: Iprops) {
                         </div>
                         {renderInfoItem(
                             constants.FILE_ID,
-                            items[photoSwipe?.getCurrentIndex()]?.id,
+                            items[photoSwipe?.getCurrentIndex()]?.id
                         )}
                         {metadata?.title &&
                             renderInfoItem(constants.FILE_NAME, metadata.title)}
                         {metadata?.creationTime &&
                             renderInfoItem(
                                 constants.CREATION_TIME,
-                                formatDateTime(metadata.creationTime / 1000),
+                                formatDateTime(metadata.creationTime / 1000)
                             )}
                         {metadata?.modificationTime &&
                             renderInfoItem(
                                 constants.UPDATED_ON,
-                                formatDateTime(
-                                    metadata.modificationTime / 1000,
-                                ),
+                                formatDateTime(metadata.modificationTime / 1000)
                             )}
                         {metadata?.longitude &&
                             metadata?.longitude &&
@@ -429,7 +427,7 @@ function PhotoSwipe(props: Iprops) {
                                     target="_blank"
                                     rel="noopener noreferrer">
                                     {constants.SHOW_MAP}
-                                </a>,
+                                </a>
                             )}
                         {exif && (
                             <>

+ 6 - 9
src/components/RecoveryKeyModal.tsx

@@ -12,11 +12,11 @@ export const CodeBlock = styled.div<{ height: number }>`
     justify-content: center;
     background: #1a1919;
     height: ${(props) => props.height}px;
-    padding-left:30px;
-    padding-right:20px;
+    padding-left: 30px;
+    padding-right: 20px;
     color: white;
     margin: 20px 0;
-    width:100%;
+    width: 100%;
 `;
 
 export const FreeFlowText = styled.div`
@@ -71,20 +71,17 @@ function RecoveryKeyModal({ somethingWentWrong, ...props }: Props) {
                     disabled: !recoveryKey,
                     variant: 'success',
                 },
-            }}
-        >
+            }}>
             <p>{constants.RECOVERY_KEY_DESCRIPTION}</p>
             <CodeBlock height={150}>
                 {recoveryKey ? (
-                    <FreeFlowText>
-                        {recoveryKey}
-                    </FreeFlowText>
+                    <FreeFlowText>{recoveryKey}</FreeFlowText>
                 ) : (
                     <EnteSpinner />
                 )}
             </CodeBlock>
             <p>{constants.KEY_NOT_STORED_DISCLAIMER}</p>
-        </MessageDialog >
+        </MessageDialog>
     );
 }
 export default RecoveryKeyModal;

+ 23 - 23
src/components/SearchBar.tsx

@@ -97,7 +97,9 @@ export default function SearchBar(props: Props) {
     }, [props.isOpen]);
 
     useEffect(() => {
-        window.addEventListener('resize', () => setWindowWidth(window.innerWidth));
+        window.addEventListener('resize', () =>
+            setWindowWidth(window.innerWidth)
+        );
     });
     // = =========================
     // Functionality
@@ -119,18 +121,19 @@ export default function SearchBar(props: Props) {
                 type: SuggestionType.DATE,
                 value: searchedDate,
                 label: getFormattedDate(searchedDate),
-            })),
+            }))
         );
 
         const searchResults = await searchLocation(searchPhrase);
         option.push(
             ...searchResults.map(
-                (searchResult) => ({
-                    type: SuggestionType.LOCATION,
-                    value: searchResult.bbox,
-                    label: searchResult.place,
-                } as Suggestion),
-            ),
+                (searchResult) =>
+                    ({
+                        type: SuggestionType.LOCATION,
+                        value: searchResult.bbox,
+                        label: searchResult.place,
+                    } as Suggestion)
+            )
         );
         return option;
     };
@@ -174,7 +177,8 @@ export default function SearchBar(props: Props) {
     // UI
     // = =========================
 
-    const getIconByType = (type: SuggestionType) => (type === SuggestionType.DATE ? <DateIcon /> : <LocationIcon />);
+    const getIconByType = (type: SuggestionType) =>
+        type === SuggestionType.DATE ? <DateIcon /> : <LocationIcon />;
 
     const LabelWithIcon = (props: { type: SuggestionType; label: string }) => (
         <div style={{ display: 'flex', alignItems: 'center' }}>
@@ -198,8 +202,7 @@ export default function SearchBar(props: Props) {
                 style={{
                     paddingLeft: '10px',
                     paddingBottom: '4px',
-                }}
-            >
+                }}>
                 {props.getValue().length === 0 || props.menuIsOpen ? (
                     <SearchIcon />
                 ) : props.getValue()[0].type === SuggestionType.DATE ? (
@@ -215,13 +218,13 @@ export default function SearchBar(props: Props) {
     const customStyles = {
         control: (style, { isFocused }) => ({
             ...style,
-            'backgroundColor': '#282828',
-            'color': '#d1d1d1',
-            'borderColor': isFocused ? '#2dc262' : '#444',
-            'boxShadow': 'none',
+            backgroundColor: '#282828',
+            color: '#d1d1d1',
+            borderColor: isFocused ? '#2dc262' : '#444',
+            boxShadow: 'none',
             ':hover': {
-                'borderColor': '#2dc262',
-                'cursor': 'text',
+                borderColor: '#2dc262',
+                cursor: 'text',
                 '&>.icon': { color: '#2dc262' },
             },
         }),
@@ -276,8 +279,7 @@ export default function SearchBar(props: Props) {
                         style={{
                             flex: 1,
                             margin: '10px',
-                        }}
-                    >
+                        }}>
                         <AsyncSelect
                             components={{
                                 Option: OptionWithIcon,
@@ -297,8 +299,7 @@ export default function SearchBar(props: Props) {
                         {props.isOpen && (
                             <div
                                 style={{ cursor: 'pointer' }}
-                                onClick={resetSearch}
-                            >
+                                onClick={resetSearch}>
                                 <CrossIcon />
                             </div>
                         )}
@@ -307,8 +308,7 @@ export default function SearchBar(props: Props) {
             ) : (
                 <SearchButton
                     isDisabled={props.isFirstFetch}
-                    onClick={() => !props.isFirstFetch && props.setOpen(true)}
-                >
+                    onClick={() => !props.isFirstFetch && props.setOpen(true)}>
                     <SearchIcon />
                 </SearchButton>
             )}

+ 8 - 11
src/components/SetPasswordForm.tsx

@@ -21,7 +21,7 @@ function SetPasswordForm(props: Props) {
     const [loading, setLoading] = useState(false);
     const onSubmit = async (
         values: formValues,
-        { setFieldError }: FormikHelpers<formValues>,
+        { setFieldError }: FormikHelpers<formValues>
     ) => {
         setLoading(true);
         try {
@@ -34,7 +34,7 @@ function SetPasswordForm(props: Props) {
         } catch (e) {
             setFieldError(
                 'passphrase',
-                `${constants.UNKNOWN_ERROR} ${e.message}`,
+                `${constants.UNKNOWN_ERROR} ${e.message}`
             );
         } finally {
             setLoading(false);
@@ -46,8 +46,7 @@ function SetPasswordForm(props: Props) {
                 <Card.Body>
                     <div
                         className="text-center"
-                        style={{ marginBottom: '40px' }}
-                    >
+                        style={{ marginBottom: '40px' }}>
                         <p>{constants.ENTER_ENC_PASSPHRASE}</p>
                         {constants.PASSPHRASE_DISCLAIMER()}
                     </div>
@@ -55,14 +54,13 @@ function SetPasswordForm(props: Props) {
                         initialValues={{ passphrase: '', confirm: '' }}
                         validationSchema={Yup.object().shape({
                             passphrase: Yup.string().required(
-                                constants.REQUIRED,
+                                constants.REQUIRED
                             ),
                             confirm: Yup.string().required(constants.REQUIRED),
                         })}
                         validateOnChange={false}
                         validateOnBlur={false}
-                        onSubmit={onSubmit}
-                    >
+                        onSubmit={onSubmit}>
                         {({
                             values,
                             touched,
@@ -79,7 +77,7 @@ function SetPasswordForm(props: Props) {
                                         onChange={handleChange('passphrase')}
                                         isInvalid={Boolean(
                                             touched.passphrase &&
-                                                errors.passphrase,
+                                                errors.passphrase
                                         )}
                                         autoFocus
                                         disabled={loading}
@@ -97,7 +95,7 @@ function SetPasswordForm(props: Props) {
                                         value={values.confirm}
                                         onChange={handleChange('confirm')}
                                         isInvalid={Boolean(
-                                            touched.confirm && errors.confirm,
+                                            touched.confirm && errors.confirm
                                         )}
                                         disabled={loading}
                                     />
@@ -115,8 +113,7 @@ function SetPasswordForm(props: Props) {
                     {props.back && (
                         <div
                             className="text-center"
-                            style={{ marginTop: '20px' }}
-                        >
+                            style={{ marginTop: '20px' }}>
                             <Button variant="link" onClick={props.back}>
                                 {constants.GO_BACK}
                             </Button>

+ 67 - 50
src/components/Sidebar.tsx

@@ -36,7 +36,7 @@ import { Subscription } from 'services/billingService';
 interface Props {
     collections: Collection[];
     setDialogMessage: SetDialogMessage;
-    setLoading: SetLoading,
+    setLoading: SetLoading;
     showPlanSelectorModal: () => void;
 }
 export default function Sidebar(props: Props) {
@@ -56,18 +56,23 @@ export default function Sidebar(props: Props) {
             if (!isOpen) {
                 return;
             }
-            const userDetails=await getUserDetails();
+            const userDetails = await getUserDetails();
             setUser({ ...user, email: userDetails.email });
             SetUsage(convertToHumanReadable(userDetails.usage));
             setSubscription(userDetails.subscription);
-            setData(LS_KEYS.USER, { ...getData(LS_KEYS.USER), email: userDetails.email });
+            setData(LS_KEYS.USER, {
+                ...getData(LS_KEYS.USER),
+                email: userDetails.email,
+            });
             setData(LS_KEYS.SUBSCRIPTION, userDetails.subscription);
         };
         main();
     }, [isOpen]);
 
     function openFeedbackURL() {
-        const feedbackURL: string = `${getEndpoint()}/users/feedback?token=${encodeURIComponent(getToken())}`;
+        const feedbackURL: string = `${getEndpoint()}/users/feedback?token=${encodeURIComponent(
+            getToken()
+        )}`;
         const win = window.open(feedbackURL, '_blank');
         win.focus();
     }
@@ -108,9 +113,13 @@ export default function Sidebar(props: Props) {
         <Menu
             isOpen={isOpen}
             onStateChange={(state) => setIsOpen(state.isOpen)}
-            itemListElement="div"
-        >
-            <div style={{ display: 'flex', outline: 'none', textAlign: 'center' }}>
+            itemListElement="div">
+            <div
+                style={{
+                    display: 'flex',
+                    outline: 'none',
+                    textAlign: 'center',
+                }}>
                 <LogoImage
                     style={{ height: '24px', padding: '3px' }}
                     alt="logo"
@@ -122,11 +131,16 @@ export default function Sidebar(props: Props) {
                     outline: 'none',
                     color: 'rgb(45, 194, 98)',
                     fontSize: '16px',
-                }}
-            >
+                }}>
                 {user?.email}
             </div>
-            <div style={{ flex: 1, overflow: 'auto', outline: 'none', paddingTop: '0' }}>
+            <div
+                style={{
+                    flex: 1,
+                    overflow: 'auto',
+                    outline: 'none',
+                    paddingTop: '0',
+                }}>
                 <div style={{ outline: 'none' }}>
                     <div style={{ display: 'flex' }}>
                         <h5 style={{ margin: '4px 0 12px 2px' }}>
@@ -137,15 +151,15 @@ export default function Sidebar(props: Props) {
                         {isSubscriptionActive(subscription) ? (
                             isOnFreePlan(subscription) ? (
                                 constants.FREE_SUBSCRIPTION_INFO(
-                                    subscription?.expiryTime,
+                                    subscription?.expiryTime
                                 )
                             ) : isSubscriptionCancelled(subscription) ? (
                                 constants.RENEWAL_CANCELLED_SUBSCRIPTION_INFO(
-                                    subscription?.expiryTime,
+                                    subscription?.expiryTime
                                 )
                             ) : (
                                 constants.RENEWAL_ACTIVE_SUBSCRIPTION_INFO(
-                                    subscription?.expiryTime,
+                                    subscription?.expiryTime
                                 )
                             )
                         ) : (
@@ -155,8 +169,7 @@ export default function Sidebar(props: Props) {
                             variant="outline-success"
                             block
                             size="sm"
-                            onClick={onManageClick}
-                        >
+                            onClick={onManageClick}>
                             {isSubscribed(subscription)
                                 ? constants.MANAGE
                                 : constants.SUBSCRIBE}
@@ -172,7 +185,7 @@ export default function Sidebar(props: Props) {
                         {usage ? (
                             constants.USAGE_INFO(
                                 usage,
-                                Number(convertBytesToGBs(subscription?.storage)),
+                                Number(convertBytesToGBs(subscription?.storage))
                             )
                         ) : (
                             <div style={{ textAlign: 'center' }}>
@@ -197,29 +210,28 @@ export default function Sidebar(props: Props) {
                 />
                 <LinkButton
                     style={{ marginTop: '30px' }}
-                    onClick={openFeedbackURL}
-                >
+                    onClick={openFeedbackURL}>
                     {constants.REQUEST_FEATURE}
                 </LinkButton>
                 <LinkButton
                     style={{ marginTop: '30px' }}
-                    onClick={openSupportMail}
-                >
+                    onClick={openSupportMail}>
                     {constants.SUPPORT}
                 </LinkButton>
                 <>
                     <RecoveryKeyModal
                         show={recoverModalView}
                         onHide={() => setRecoveryModalView(false)}
-                        somethingWentWrong={() => props.setDialogMessage({
-                            title: constants.RECOVER_KEY_GENERATION_FAILED,
-                            close: { variant: 'danger' },
-                        })}
+                        somethingWentWrong={() =>
+                            props.setDialogMessage({
+                                title: constants.RECOVER_KEY_GENERATION_FAILED,
+                                close: { variant: 'danger' },
+                            })
+                        }
                     />
                     <LinkButton
                         style={{ marginTop: '30px' }}
-                        onClick={() => setRecoveryModalView(true)}
-                    >
+                        onClick={() => setRecoveryModalView(true)}>
                         {constants.DOWNLOAD_RECOVERY_KEY}
                     </LinkButton>
                 </>
@@ -233,8 +245,7 @@ export default function Sidebar(props: Props) {
                     />
                     <LinkButton
                         style={{ marginTop: '30px' }}
-                        onClick={() => setTwoFactorModalView(true)}
-                    >
+                        onClick={() => setTwoFactorModalView(true)}>
                         {constants.TWO_FACTOR}
                     </LinkButton>
                 </>
@@ -243,8 +254,7 @@ export default function Sidebar(props: Props) {
                     onClick={() => {
                         setData(LS_KEYS.SHOW_BACK_BUTTON, { value: true });
                         router.push('change-password');
-                    }}
-                >
+                    }}>
                     {constants.CHANGE_PASSWORD}
                 </LinkButton>
                 <LinkButton
@@ -252,18 +262,24 @@ export default function Sidebar(props: Props) {
                     onClick={() => {
                         setData(LS_KEYS.SHOW_BACK_BUTTON, { value: true });
                         router.push('change-email');
-                    }}
-                >
+                    }}>
                     {constants.UPDATE_EMAIL}
                 </LinkButton>
                 <>
-                    <ExportModal show={exportModalView} onHide={() => setExportModalView(false)} usage={usage} />
-                    <LinkButton style={{ marginTop: '30px' }} onClick={exportFiles}>
+                    <ExportModal
+                        show={exportModalView}
+                        onHide={() => setExportModalView(false)}
+                        usage={usage}
+                    />
+                    <LinkButton
+                        style={{ marginTop: '30px' }}
+                        onClick={exportFiles}>
                         <div style={{ display: 'flex' }}>
-                            {constants.EXPORT}<div style={{ width: '20px' }} />
-                            {exportService.isExportInProgress() &&
+                            {constants.EXPORT}
+                            <div style={{ width: '20px' }} />
+                            {exportService.isExportInProgress() && (
                                 <InProgressIcon />
-                            }
+                            )}
                         </div>
                     </LinkButton>
                 </>
@@ -278,18 +294,19 @@ export default function Sidebar(props: Props) {
                 <LinkButton
                     variant="danger"
                     style={{ marginTop: '30px' }}
-                    onClick={() => props.setDialogMessage({
-                        title: `${constants.CONFIRM} ${constants.LOGOUT}`,
-                        content: constants.LOGOUT_MESSAGE,
-                        staticBackdrop: true,
-                        proceed: {
-                            text: constants.LOGOUT,
-                            action: logoutUser,
-                            variant: 'danger',
-                        },
-                        close: { text: constants.CANCEL },
-                    })}
-                >
+                    onClick={() =>
+                        props.setDialogMessage({
+                            title: `${constants.CONFIRM} ${constants.LOGOUT}`,
+                            content: constants.LOGOUT_MESSAGE,
+                            staticBackdrop: true,
+                            proceed: {
+                                text: constants.LOGOUT,
+                                action: logoutUser,
+                                variant: 'danger',
+                            },
+                            close: { text: constants.CANCEL },
+                        })
+                    }>
                     {constants.LOGOUT}
                 </LinkButton>
                 <div
@@ -299,6 +316,6 @@ export default function Sidebar(props: Props) {
                     }}
                 />
             </div>
-        </Menu >
+        </Menu>
     );
 }

+ 109 - 111
src/components/SignUp.tsx

@@ -36,7 +36,7 @@ export default function SignUp(props: SignUpProps) {
 
     const registerUser = async (
         { email, passphrase, confirm }: FormValues,
-        { setFieldError }: FormikHelpers<FormValues>,
+        { setFieldError }: FormikHelpers<FormValues>
     ) => {
         setLoading(true);
         try {
@@ -47,12 +47,13 @@ export default function SignUp(props: SignUpProps) {
         }
         try {
             if (passphrase === confirm) {
-                const { keyAttributes, masterKey } = await generateKeyAttributes(passphrase);
+                const { keyAttributes, masterKey } =
+                    await generateKeyAttributes(passphrase);
                 setData(LS_KEYS.ORIGINAL_KEY_ATTRIBUTES, keyAttributes);
                 await generateAndSaveIntermediateKeyAttributes(
                     passphrase,
                     keyAttributes,
-                    masterKey,
+                    masterKey
                 );
 
                 await setSessionKeys(masterKey);
@@ -68,113 +69,110 @@ export default function SignUp(props: SignUpProps) {
         setLoading(false);
     };
 
-    return (<>
-        <Card.Title style={{ marginBottom: '32px' }}>
-            <LogoImg src="/icon.svg" />
-            {constants.SIGN_UP}
-        </Card.Title>
-        <Formik<FormValues>
-            initialValues={{
-                email: '',
-                passphrase: '',
-                confirm: '',
-            }}
-            validationSchema={Yup.object().shape({
-                email: Yup.string()
-                    .email(constants.EMAIL_ERROR)
-                    .required(constants.REQUIRED),
-                passphrase: Yup.string().required(
-                    constants.REQUIRED,
-                ),
-                confirm: Yup.string().required(constants.REQUIRED),
-            })}
-            validateOnChange={false}
-            validateOnBlur={false}
-            onSubmit={registerUser}
-        >
-            {({
-                values,
-                errors,
-                touched,
-                handleChange,
-                handleSubmit,
-            }): JSX.Element => (
-                <Form noValidate onSubmit={handleSubmit}>
-                    <Form.Group controlId="registrationForm.email">
-                        <Form.Control
-                            type="email"
-                            placeholder={constants.ENTER_EMAIL}
-                            value={values.email}
-                            onChange={handleChange('email')}
-                            isInvalid={Boolean(
-                                touched.email && errors.email,
-                            )}
-                            autoFocus
-                            disabled={loading}
+    return (
+        <>
+            <Card.Title style={{ marginBottom: '32px' }}>
+                <LogoImg src="/icon.svg" />
+                {constants.SIGN_UP}
+            </Card.Title>
+            <Formik<FormValues>
+                initialValues={{
+                    email: '',
+                    passphrase: '',
+                    confirm: '',
+                }}
+                validationSchema={Yup.object().shape({
+                    email: Yup.string()
+                        .email(constants.EMAIL_ERROR)
+                        .required(constants.REQUIRED),
+                    passphrase: Yup.string().required(constants.REQUIRED),
+                    confirm: Yup.string().required(constants.REQUIRED),
+                })}
+                validateOnChange={false}
+                validateOnBlur={false}
+                onSubmit={registerUser}>
+                {({
+                    values,
+                    errors,
+                    touched,
+                    handleChange,
+                    handleSubmit,
+                }): JSX.Element => (
+                    <Form noValidate onSubmit={handleSubmit}>
+                        <Form.Group controlId="registrationForm.email">
+                            <Form.Control
+                                type="email"
+                                placeholder={constants.ENTER_EMAIL}
+                                value={values.email}
+                                onChange={handleChange('email')}
+                                isInvalid={Boolean(
+                                    touched.email && errors.email
+                                )}
+                                autoFocus
+                                disabled={loading}
+                            />
+                            <FormControl.Feedback type="invalid">
+                                {errors.email}
+                            </FormControl.Feedback>
+                        </Form.Group>
+                        <Form.Group>
+                            <Form.Control
+                                type="password"
+                                placeholder={constants.PASSPHRASE_HINT}
+                                value={values.passphrase}
+                                onChange={handleChange('passphrase')}
+                                isInvalid={Boolean(
+                                    touched.passphrase && errors.passphrase
+                                )}
+                                disabled={loading}
+                            />
+                            <Form.Control.Feedback type="invalid">
+                                {errors.passphrase}
+                            </Form.Control.Feedback>
+                        </Form.Group>
+                        <Form.Group>
+                            <Form.Control
+                                type="password"
+                                placeholder={constants.RE_ENTER_PASSPHRASE}
+                                value={values.confirm}
+                                onChange={handleChange('confirm')}
+                                isInvalid={Boolean(
+                                    touched.confirm && errors.confirm
+                                )}
+                                disabled={loading}
+                            />
+                            <Form.Control.Feedback type="invalid">
+                                {errors.confirm}
+                            </Form.Control.Feedback>
+                        </Form.Group>
+                        <Form.Group
+                            style={{
+                                marginBottom: '0',
+                                textAlign: 'left',
+                            }}
+                            controlId="formBasicCheckbox-1">
+                            <Form.Check
+                                checked={acceptTerms}
+                                onChange={(e) =>
+                                    setAcceptTerms(e.target.checked)
+                                }
+                                type="checkbox"
+                                label={constants.TERMS_AND_CONDITIONS()}
+                            />
+                        </Form.Group>
+                        <br />
+                        <SubmitButton
+                            buttonText={constants.SUBMIT}
+                            loading={loading}
+                            disabled={!acceptTerms}
                         />
-                        <FormControl.Feedback type="invalid">
-                            {errors.email}
-                        </FormControl.Feedback>
-                    </Form.Group>
-                    <Form.Group>
-                        <Form.Control
-                            type="password"
-                            placeholder={constants.PASSPHRASE_HINT}
-                            value={values.passphrase}
-                            onChange={handleChange('passphrase')}
-                            isInvalid={Boolean(
-                                touched.passphrase &&
-                                    errors.passphrase,
-                            )}
-                            disabled={loading}
-                        />
-                        <Form.Control.Feedback type="invalid">
-                            {errors.passphrase}
-                        </Form.Control.Feedback>
-                    </Form.Group>
-                    <Form.Group>
-                        <Form.Control
-                            type="password"
-                            placeholder={
-                                constants.RE_ENTER_PASSPHRASE
-                            }
-                            value={values.confirm}
-                            onChange={handleChange('confirm')}
-                            isInvalid={Boolean(
-                                touched.confirm && errors.confirm,
-                            )}
-                            disabled={loading}
-                        />
-                        <Form.Control.Feedback type="invalid">
-                            {errors.confirm}
-                        </Form.Control.Feedback>
-                    </Form.Group>
-                    <Form.Group
-                        style={{
-                            marginBottom: '0',
-                            textAlign: 'left',
-                        }}
-                        controlId="formBasicCheckbox-1"
-                    >
-                        <Form.Check
-                            checked={acceptTerms}
-                            onChange={(e) => setAcceptTerms(e.target.checked)}
-                            type="checkbox"
-                            label={constants.TERMS_AND_CONDITIONS()}
-                        />
-                    </Form.Group>
-                    <br />
-                    <SubmitButton
-                        buttonText={constants.SUBMIT}
-                        loading={loading}
-                        disabled={!acceptTerms}
-                    />
-                    <br />
-                    <Button block variant="link" onClick={props.login}>
-                        {constants.ACCOUNT_EXISTS}
-                    </Button>
-                </Form>
-            )}
-        </Formik>
-    </>);
+                        <br />
+                        <Button block variant="link" onClick={props.login}>
+                            {constants.ACCOUNT_EXISTS}
+                        </Button>
+                    </Form>
+                )}
+            </Formik>
+        </>
+    );
 }

+ 16 - 12
src/components/SingleInputForm.tsx

@@ -40,7 +40,7 @@ export default function SingleInputForm(props: Props) {
 
     const submitForm = async (
         values: formValues,
-        { setFieldError }: FormikHelpers<formValues>,
+        { setFieldError }: FormikHelpers<formValues>
     ) => {
         SetLoading(true);
         await props.callback(values.passphrase, setFieldError);
@@ -54,11 +54,8 @@ export default function SingleInputForm(props: Props) {
                 passphrase: Yup.string().required(constants.REQUIRED),
             })}
             validateOnChange={false}
-            validateOnBlur={false}
-        >
-            {({
-                values, touched, errors, handleChange, handleSubmit,
-            }) => (
+            validateOnBlur={false}>
+            {({ values, touched, errors, handleChange, handleSubmit }) => (
                 <Form noValidate onSubmit={handleSubmit}>
                     <Form.Group>
                         <Group>
@@ -68,17 +65,24 @@ export default function SingleInputForm(props: Props) {
                                 value={values.passphrase}
                                 onChange={handleChange('passphrase')}
                                 isInvalid={Boolean(
-                                    touched.passphrase && errors.passphrase,
+                                    touched.passphrase && errors.passphrase
                                 )}
                                 disabled={loading}
                                 autoFocus
                             />
-                            {
-                                props.fieldType === 'password' &&
-                                <Button type='button' onClick={() => setShowPassword(!showPassword)}>
-                                    {showPassword ? <VisibilityOff /> : <Visibility />}
+                            {props.fieldType === 'password' && (
+                                <Button
+                                    type="button"
+                                    onClick={() =>
+                                        setShowPassword(!showPassword)
+                                    }>
+                                    {showPassword ? (
+                                        <VisibilityOff />
+                                    ) : (
+                                        <Visibility />
+                                    )}
                                 </Button>
-                            }
+                            )}
                             <Form.Control.Feedback type="invalid">
                                 {errors.passphrase}
                             </Form.Control.Feedback>

+ 8 - 6
src/components/SubmitButton.tsx

@@ -7,22 +7,24 @@ interface Props {
     inline?: any;
     disabled?: boolean;
 }
-const SubmitButton = ({
-    loading, buttonText, inline, disabled,
-}: Props) => (
+const SubmitButton = ({ loading, buttonText, inline, disabled }: Props) => (
     <Button
         className="submitButton"
         variant="outline-success"
         type="submit"
         block={!inline}
         disabled={loading || disabled}
-        style={{ padding: '6px 1em' }}
-    >
+        style={{ padding: '6px 1em' }}>
         {loading ? (
             <Spinner
                 as="span"
                 animation="border"
-                style={{ width: '22px', height: '22px', borderWidth: '0.20em', color: '#2dc262' }}
+                style={{
+                    width: '22px',
+                    height: '22px',
+                    borderWidth: '0.20em',
+                    color: '#2dc262',
+                }}
             />
         ) : (
             buttonText

+ 72 - 37
src/components/TwoFactorModal.tsx

@@ -13,7 +13,7 @@ interface Props {
     show: boolean;
     onHide: () => void;
     setDialogMessage: SetDialogMessage;
-    setLoading: SetLoading
+    setLoading: SetLoading;
     closeSidebar: () => void;
 }
 
@@ -26,12 +26,16 @@ function TwoFactorModal(props: Props) {
         if (!props.show) {
             return;
         }
-        const isTwoFactorEnabled = getData(LS_KEYS.USER).isTwoFactorEnabled ?? false;
+        const isTwoFactorEnabled =
+            getData(LS_KEYS.USER).isTwoFactorEnabled ?? false;
         setTwoFactorStatus(isTwoFactorEnabled);
         const main = async () => {
             const isTwoFactorEnabled = await getTwoFactorStatus();
             setTwoFactorStatus(isTwoFactorEnabled);
-            setData(LS_KEYS.USER, { ...getData(LS_KEYS.USER), isTwoFactorEnabled: false });
+            setData(LS_KEYS.USER, {
+                ...getData(LS_KEYS.USER),
+                isTwoFactorEnabled: false,
+            });
         };
         main();
     }, [props.show]);
@@ -51,12 +55,21 @@ function TwoFactorModal(props: Props) {
     const twoFactorDisable = async () => {
         try {
             await disableTwoFactor();
-            setData(LS_KEYS.USER, { ...getData(LS_KEYS.USER), isTwoFactorEnabled: false });
+            setData(LS_KEYS.USER, {
+                ...getData(LS_KEYS.USER),
+                isTwoFactorEnabled: false,
+            });
             props.onHide();
             props.closeSidebar();
-            appContext.setDisappearingFlashMessage({ message: constants.TWO_FACTOR_DISABLE_SUCCESS, severity: 'info' });
+            appContext.setDisappearingFlashMessage({
+                message: constants.TWO_FACTOR_DISABLE_SUCCESS,
+                severity: 'info',
+            });
         } catch (e) {
-            appContext.setDisappearingFlashMessage({ message: constants.TWO_FACTOR_DISABLE_FAILED, severity: 'danger' });
+            appContext.setDisappearingFlashMessage({
+                message: constants.TWO_FACTOR_DISABLE_FAILED,
+                severity: 'danger',
+            });
         }
     };
     const warnTwoFactorReconfigure = async () => {
@@ -82,38 +95,60 @@ function TwoFactorModal(props: Props) {
             attributes={{
                 title: constants.TWO_FACTOR_AUTHENTICATION,
                 staticBackdrop: true,
-            }}
-
-        >
-            <div {...(!isTwoFactorEnabled ? { style: { padding: '10px 10px 30px 10px' } } : { style: { padding: '10px' } })}>
-                {
-                    isTwoFactorEnabled
-                        ? <>
-                            <Row>
-                                <Label>{constants.UPDATE_TWO_FACTOR_HINT}</Label>
-                                <Value>
-                                    <Button variant={'outline-success'} onClick={warnTwoFactorReconfigure}>{constants.RECONFIGURE}</Button>
-                                </Value>
-                            </Row>
-                            <Row>
-                                <Label>{constants.DISABLE_TWO_FACTOR_HINT} </Label>
-                                <Value>
-                                    <Button variant={'outline-danger'} onClick={warnTwoFactorDisable}>{constants.DISABLE}</Button>
-                                </Value>
-                            </Row>
-
-                        </> : (
-                            <DeadCenter>
-                                <svg xmlns="http://www.w3.org/2000/svg" height="36px" viewBox="0 0 24 24" width="36px" fill="#000000"><g fill="none"><path d="M0 0h24v24H0V0z" /><path d="M0 0h24v24H0V0z" opacity=".87" /></g><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM9 6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9V6zm9 14H6V10h12v10zm-6-3c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" /></svg>
-                                <p />
-                                <p>{constants.TWO_FACTOR_INFO}</p>
-                                <div style={{ height: '10px' }} />
-                                <Button variant="outline-success" onClick={() => router.push('/two-factor/setup')}>{constants.ENABLE_TWO_FACTOR}</Button>
-                            </DeadCenter>
-                        )
-                }
+            }}>
+            <div
+                {...(!isTwoFactorEnabled
+                    ? { style: { padding: '10px 10px 30px 10px' } }
+                    : { style: { padding: '10px' } })}>
+                {isTwoFactorEnabled ? (
+                    <>
+                        <Row>
+                            <Label>{constants.UPDATE_TWO_FACTOR_HINT}</Label>
+                            <Value>
+                                <Button
+                                    variant={'outline-success'}
+                                    onClick={warnTwoFactorReconfigure}>
+                                    {constants.RECONFIGURE}
+                                </Button>
+                            </Value>
+                        </Row>
+                        <Row>
+                            <Label>{constants.DISABLE_TWO_FACTOR_HINT} </Label>
+                            <Value>
+                                <Button
+                                    variant={'outline-danger'}
+                                    onClick={warnTwoFactorDisable}>
+                                    {constants.DISABLE}
+                                </Button>
+                            </Value>
+                        </Row>
+                    </>
+                ) : (
+                    <DeadCenter>
+                        <svg
+                            xmlns="http://www.w3.org/2000/svg"
+                            height="36px"
+                            viewBox="0 0 24 24"
+                            width="36px"
+                            fill="#000000">
+                            <g fill="none">
+                                <path d="M0 0h24v24H0V0z" />
+                                <path d="M0 0h24v24H0V0z" opacity=".87" />
+                            </g>
+                            <path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM9 6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9V6zm9 14H6V10h12v10zm-6-3c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" />
+                        </svg>
+                        <p />
+                        <p>{constants.TWO_FACTOR_INFO}</p>
+                        <div style={{ height: '10px' }} />
+                        <Button
+                            variant="outline-success"
+                            onClick={() => router.push('/two-factor/setup')}>
+                            {constants.ENABLE_TWO_FACTOR}
+                        </Button>
+                    </DeadCenter>
+                )}
             </div>
-        </MessageDialog >
+        </MessageDialog>
     );
 }
 export default TwoFactorModal;

+ 34 - 17
src/components/VerifyTwoFactor.tsx

@@ -11,8 +11,8 @@ interface formValues {
     otp: string;
 }
 interface Props {
-    onSubmit: any
-    back: any
+    onSubmit: any;
+    back: any;
     buttonText: string;
 }
 
@@ -21,7 +21,7 @@ export default function VerifyTwoFactor(props: Props) {
     const otpInputRef = useRef(null);
     const submitForm = async (
         { otp }: formValues,
-        { setFieldError, resetForm }: FormikHelpers<formValues>,
+        { setFieldError, resetForm }: FormikHelpers<formValues>
     ) => {
         try {
             setWaiting(true);
@@ -36,7 +36,11 @@ export default function VerifyTwoFactor(props: Props) {
         setWaiting(false);
     };
 
-    const onChange = (otp: string, callback: Function, triggerSubmit: Function) => {
+    const onChange = (
+        otp: string,
+        callback: Function,
+        triggerSubmit: Function
+    ) => {
         callback(otp);
         if (otp.length === 6) {
             triggerSubmit(otp);
@@ -44,13 +48,14 @@ export default function VerifyTwoFactor(props: Props) {
     };
     return (
         <>
-            <p style={{ marginBottom: '30px' }}>enter the 6-digit code from your authenticator app.</p>
+            <p style={{ marginBottom: '30px' }}>
+                enter the 6-digit code from your authenticator app.
+            </p>
             <Formik<formValues>
                 initialValues={{ otp: '' }}
                 validateOnChange={false}
                 validateOnBlur={false}
-                onSubmit={submitForm}
-            >
+                onSubmit={submitForm}>
                 {({
                     values,
                     errors,
@@ -58,8 +63,13 @@ export default function VerifyTwoFactor(props: Props) {
                     handleSubmit,
                     submitForm,
                 }) => (
-                    <Form noValidate onSubmit={handleSubmit} style={{ width: '100%' }}>
-                        <Form.Group style={{ marginBottom: '32px' }} controlId="formBasicEmail">
+                    <Form
+                        noValidate
+                        onSubmit={handleSubmit}
+                        style={{ width: '100%' }}>
+                        <Form.Group
+                            style={{ marginBottom: '32px' }}
+                            controlId="formBasicEmail">
                             <DeadCenter>
                                 <OtpInput
                                     placeholder="123456"
@@ -67,16 +77,27 @@ export default function VerifyTwoFactor(props: Props) {
                                     shouldAutoFocus
                                     value={values.otp}
                                     onChange={(otp) => {
-                                        onChange(otp, handleChange('otp'), submitForm);
+                                        onChange(
+                                            otp,
+                                            handleChange('otp'),
+                                            submitForm
+                                        );
                                     }}
                                     numInputs={6}
                                     separator={'-'}
                                     isInputNum
                                     className={'otp-input'}
                                 />
-                                {errors.otp &&
-                                    <div style={{ display: 'block', marginTop: '16px' }} className="invalid-feedback">{constants.INCORRECT_CODE}</div>
-                                }
+                                {errors.otp && (
+                                    <div
+                                        style={{
+                                            display: 'block',
+                                            marginTop: '16px',
+                                        }}
+                                        className="invalid-feedback">
+                                        {constants.INCORRECT_CODE}
+                                    </div>
+                                )}
                             </DeadCenter>
                         </Form.Group>
                         <SubmitButton
@@ -87,10 +108,6 @@ export default function VerifyTwoFactor(props: Props) {
                     </Form>
                 )}
             </Formik>
-
-
         </>
     );
 }
-
-

+ 9 - 3
src/components/icons/AddIcon.tsx

@@ -7,9 +7,15 @@ export default function AddIcon(props) {
             height={props.height}
             viewBox={props.viewBox}
             width={props.width}
-            fill='currentColor'
-        >
-            <g><rect fill="none" height="24" width="24"/></g><g><g><path d="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6V13z"/></g></g>
+            fill="currentColor">
+            <g>
+                <rect fill="none" height="24" width="24" />
+            </g>
+            <g>
+                <g>
+                    <path d="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6V13z" />
+                </g>
+            </g>
         </svg>
     );
 }

+ 1 - 2
src/components/icons/ArrowEast.tsx

@@ -7,8 +7,7 @@ export default function ArrowEast(props) {
             height={props.height}
             viewBox={props.viewBox}
             width={props.width}
-            {...props}
-        >
+            {...props}>
             <rect fill="none" height="24" width="24" />
             <path d="M15,5l-1.41,1.41L18.17,11H2V13h16.17l-4.59,4.59L15,19l7-7L15,5z" />
         </svg>

+ 1 - 2
src/components/icons/CloudUpload.tsx

@@ -7,8 +7,7 @@ export default function CloudUpload(props) {
             height={props.height}
             viewBox={props.viewBox}
             width={props.width}
-            fill="currentColor"
-        >
+            fill="currentColor">
             <path d="M0 0h24v24H0V0z" fill="none" />
             <path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3zM8 13h2.55v3h2.9v-3H16l-4-4z" />
         </svg>

+ 1 - 2
src/components/icons/CrossIcon.tsx

@@ -6,8 +6,7 @@ export default function DateIcon(props) {
             xmlns="http://www.w3.org/2000/svg"
             height={props.height}
             viewBox={props.viewBox}
-            width={props.width}
-        >
+            width={props.width}>
             <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
         </svg>
     );

+ 1 - 2
src/components/icons/DateIcon.tsx

@@ -6,8 +6,7 @@ export default function DateIcon(props) {
             xmlns="http://www.w3.org/2000/svg"
             height={props.height}
             viewBox={props.viewBox}
-            width={props.width}
-        >
+            width={props.width}>
             <path d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V10h14v10zm-4.5-7a2.5 2.5 0 0 0 0 5 2.5 2.5 0 0 0 0-5z" />
         </svg>
     );

+ 1 - 2
src/components/icons/DeleteIcon.tsx

@@ -7,8 +7,7 @@ export default function DeleteIcon(props) {
             height={props.height}
             viewBox={props.viewBox}
             width={props.width}
-            fill='currentColor'
-        >
+            fill="currentColor">
             <path d="M0 0h24v24H0z" fill="none" />
             <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" />
         </svg>

+ 3 - 6
src/components/icons/ExpandLess.tsx

@@ -1,20 +1,17 @@
-
-
 import React from 'react';
 export default function ExpandLess(props) {
     return (
-        <div >
+        <div>
             <svg
                 xmlns="http://www.w3.org/2000/svg"
                 height={props.height}
                 viewBox={props.viewBox}
                 width={props.width}
                 fill="#000000">
-                <path d="M0 0h24v24H0V0z" fill="none"/>
-                <path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14l-6-6z"/>
+                <path d="M0 0h24v24H0V0z" fill="none" />
+                <path d="M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14l-6-6z" />
             </svg>
         </div>
-
     );
 }
 

+ 3 - 6
src/components/icons/ExpandMore.tsx

@@ -1,20 +1,17 @@
-
-
 import React from 'react';
 export default function ExpandMore(props) {
     return (
-        <div >
+        <div>
             <svg
                 xmlns="http://www.w3.org/2000/svg"
                 height={props.height}
                 viewBox={props.viewBox}
                 width={props.width}
                 fill="#000000">
-                <path d="M24 24H0V0h24v24z" fill="none" opacity=".87"/>
-                <path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41z"/>
+                <path d="M24 24H0V0h24v24z" fill="none" opacity=".87" />
+                <path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41z" />
             </svg>
         </div>
-
     );
 }
 

+ 1 - 4
src/components/icons/FolderIcon.tsx

@@ -1,9 +1,7 @@
-
-
 import React from 'react';
 export default function FolderIcon(props) {
     return (
-        <div >
+        <div>
             <svg
                 xmlns="http://www.w3.org/2000/svg"
                 height={props.height}
@@ -14,7 +12,6 @@ export default function FolderIcon(props) {
                 <path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z" />
             </svg>
         </div>
-
     );
 }
 

+ 7 - 7
src/components/icons/InProgressIcon.tsx

@@ -4,16 +4,16 @@ import styled from 'styled-components';
 const Rotate = styled.div<{ disabled }>`
     width: 24px;
     height: 27px;
-    ${(props) => !props.disabled && '-webkit-animation: rotation 1s infinite linear'};
-    cursor:${(props) => props.disabled && 'pointer'};
+    ${(props) =>
+        !props.disabled && '-webkit-animation: rotation 1s infinite linear'};
+    cursor: ${(props) => props.disabled && 'pointer'};
     transition-duration: 0.8s;
     transition-property: transform;
     &:hover {
-        color:#fff;
+        color: #fff;
         transform: rotate(90deg);
         -webkit-transform: rotate(90deg);
     }
-
 `;
 export default function InProgressIcon(props) {
     return (
@@ -24,9 +24,10 @@ export default function InProgressIcon(props) {
                 viewBox={props.viewBox}
                 width={props.width}
                 fill="#000000">
-                <path d="M.01 0h24v24h-24V0z" fill="none" /><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z" />
+                <path d="M.01 0h24v24h-24V0z" fill="none" />
+                <path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8zm0 14c-3.31 0-6-2.69-6-6 0-1.01.25-1.97.7-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4-4-4-4v3z" />
             </svg>
-        </ Rotate>
+        </Rotate>
     );
 }
 InProgressIcon.defaultProps = {
@@ -35,4 +36,3 @@ InProgressIcon.defaultProps = {
     width: 24,
     viewBox: '0 0 24 24',
 };
-

+ 1 - 2
src/components/icons/LocationIcon.tsx

@@ -6,8 +6,7 @@ export default function LocationIcon(props) {
             xmlns="http://www.w3.org/2000/svg"
             height={props.height}
             viewBox={props.viewBox}
-            width={props.width}
-        >
+            width={props.width}>
             <path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zM7 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 2.88-2.88 7.19-5 9.88C9.92 16.21 7 11.85 7 9z" />
             <circle cx="12" cy="9" r="2.5" />
         </svg>

+ 1 - 2
src/components/icons/NavigateNext.tsx

@@ -8,8 +8,7 @@ export default function NavigateNext(props) {
             viewBox="0 0 24 24"
             width="24px"
             fill="currentColor"
-            {...props}
-        >
+            {...props}>
             <path d="M0 0h24v24H0z" fill="none" />
             <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
         </svg>

+ 1 - 2
src/components/icons/PlayCircleOutline.tsx

@@ -6,8 +6,7 @@ export default function PlayCircleOutline(props) {
             xmlns="http://www.w3.org/2000/svg"
             height={props.height}
             viewBox={props.viewBox}
-            width={props.width}
-        >
+            width={props.width}>
             <path d="M0 0h24v24H0z" fill="none" />
             <path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
         </svg>

+ 1 - 2
src/components/icons/SadFace.tsx

@@ -6,8 +6,7 @@ export default function SadFace(props) {
             xmlns="http://www.w3.org/2000/svg"
             height={props.height}
             viewBox={props.viewBox}
-            width={props.width}
-        >
+            width={props.width}>
             <path d="M0 0h24v24H0V0z" fill="none" />
             <circle cx="15.5" cy="9.5" r="1.5" />
             <circle cx="8.5" cy="9.5" r="1.5" />

+ 1 - 2
src/components/icons/SearchIcon.tsx

@@ -6,8 +6,7 @@ export default function SearchIcon(props) {
             xmlns="http://www.w3.org/2000/svg"
             height={props.height}
             viewBox={props.viewBox}
-            width={props.width}
-        >
+            width={props.width}>
             <path d="M20.49 19l-5.73-5.73C15.53 12.2 16 10.91 16 9.5A6.5 6.5 0 1 0 9.5 16c1.41 0 2.7-.47 3.77-1.24L19 20.49 20.49 19zM5 9.5C5 7.01 7.01 5 9.5 5S14 7.01 14 9.5 11.99 14 9.5 14 5 11.99 5 9.5z" />
         </svg>
     );

+ 3 - 3
src/components/icons/Visibility.tsx

@@ -1,17 +1,17 @@
 import React from 'react';
 export default function Visibility(props) {
     return (
-        <div >
+        <div>
             <svg
                 xmlns="http://www.w3.org/2000/svg"
                 height={props.height}
                 viewBox={props.viewBox}
                 width={props.width}
                 fill="#000000">
-                <path d="M0 0h24v24H0z" fill="none"/><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
+                <path d="M0 0h24v24H0z" fill="none" />
+                <path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
             </svg>
         </div>
-
     );
 }
 

+ 6 - 3
src/components/icons/VisibilityOff.tsx

@@ -1,17 +1,20 @@
 import React from 'react';
 export default function VisibilityOff(props) {
     return (
-        <div >
+        <div>
             <svg
                 xmlns="http://www.w3.org/2000/svg"
                 height={props.height}
                 viewBox={props.viewBox}
                 width={props.width}
                 fill="#000000">
-                <path d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" fill="none"/><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/>
+                <path
+                    d="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z"
+                    fill="none"
+                />
+                <path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z" />
             </svg>
         </div>
-
     );
 }
 

+ 5 - 3
src/components/icons/power_settings.tsx

@@ -6,10 +6,12 @@ export default function PowerSettings(props) {
             xmlns="http://www.w3.org/2000/svg"
             height={props.height}
             viewBox={props.viewBox}
-            width={props.width}
-        >
+            width={props.width}>
             <path fill="none" d="M0 0h24v24H0z" />
-            <path fill="#ff6666" d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z" />
+            <path
+                fill="#ff6666"
+                d="M13 3h-2v10h2V3zm4.83 2.17l-1.42 1.42C17.99 7.86 19 9.81 19 12c0 3.87-3.13 7-7 7s-7-3.13-7-7c0-2.19 1.01-4.14 2.58-5.42L6.17 5.17C4.23 6.82 3 9.26 3 12c0 4.97 4.03 9 9 9s9-4.03 9-9c0-2.74-1.23-5.18-3.17-6.83z"
+            />
         </svg>
     );
 }

+ 10 - 10
src/components/pages/gallery/AlertBanner.tsx

@@ -2,17 +2,18 @@ import React from 'react';
 import Alert from 'react-bootstrap/Alert';
 import { getVariantColor } from './LinkButton';
 
-interface Props{
-    bannerMessage?:any
-    variant?:string
-    children?:any
+interface Props {
+    bannerMessage?: any;
+    variant?: string;
+    children?: any;
 }
-export default function AlertBanner(props:Props) {
+export default function AlertBanner(props: Props) {
     return (
         <Alert
-            variant={props.variant??'danger'}
+            variant={props.variant ?? 'danger'}
             style={{
-                display: props.bannerMessage || props.children ? 'block' : 'none',
+                display:
+                    props.bannerMessage || props.children ? 'block' : 'none',
                 textAlign: 'center',
 
                 border: 'none',
@@ -23,9 +24,8 @@ export default function AlertBanner(props:Props) {
                 padding: 0,
                 margin: '0 25px',
                 marginBottom: '10px',
-            }}
-        >
-            {props.bannerMessage?props.bannerMessage:props.children}
+            }}>
+            {props.bannerMessage ? props.bannerMessage : props.children}
         </Alert>
     );
 }

+ 5 - 10
src/components/pages/gallery/ChoiceModal.tsx

@@ -19,15 +19,13 @@ function ChoiceModal({
         <MessageDialog
             size="lg"
             {...props}
-            attributes={{ title: constants.MULTI_FOLDER_UPLOAD }}
-        >
+            attributes={{ title: constants.MULTI_FOLDER_UPLOAD }}>
             <p>{constants.UPLOAD_STRATEGY_CHOICE}</p>
             <div
                 style={{
                     display: 'flex',
                     flexWrap: 'wrap',
-                }}
-            >
+                }}>
                 <Button
                     variant="outline-success"
                     onClick={() => {
@@ -38,8 +36,7 @@ function ChoiceModal({
                         padding: '12px 24px',
                         flex: 2,
                         whiteSpace: 'nowrap',
-                    }}
-                >
+                    }}>
                     {constants.UPLOAD_STRATEGY_SINGLE_COLLECTION}
                 </Button>
                 <div
@@ -48,8 +45,7 @@ function ChoiceModal({
                         textAlign: 'center',
                         minWidth: '100px',
                         margin: '2% auto',
-                    }}
-                >
+                    }}>
                     <strong>{constants.OR}</strong>
                 </div>
                 <Button
@@ -62,8 +58,7 @@ function ChoiceModal({
                         padding: '12px 24px',
                         flex: 2,
                         whiteSpace: 'nowrap',
-                    }}
-                >
+                    }}>
                     {constants.UPLOAD_STRATEGY_COLLECTION_PER_FOLDER}
                 </Button>
             </div>

+ 5 - 10
src/components/pages/gallery/CollectionNamer.tsx

@@ -51,8 +51,7 @@ export default function CollectionNamer({ attributes, ...props }: Props) {
             size="sm"
             attributes={{
                 title: attributes?.title,
-            }}
-        >
+            }}>
             <Formik<formValues>
                 initialValues={{ albumName: attributes.autoFilledName }}
                 validationSchema={Yup.object().shape({
@@ -60,11 +59,8 @@ export default function CollectionNamer({ attributes, ...props }: Props) {
                 })}
                 validateOnChange={false}
                 validateOnBlur={false}
-                onSubmit={onSubmit}
-            >
-                {({
-                    values, touched, errors, handleChange, handleSubmit,
-                }) => (
+                onSubmit={onSubmit}>
+                {({ values, touched, errors, handleChange, handleSubmit }) => (
                     <Form noValidate onSubmit={handleSubmit}>
                         <Form.Group>
                             <Form.Control
@@ -73,7 +69,7 @@ export default function CollectionNamer({ attributes, ...props }: Props) {
                                 value={values.albumName}
                                 onChange={handleChange('albumName')}
                                 isInvalid={Boolean(
-                                    touched.albumName && errors.albumName,
+                                    touched.albumName && errors.albumName
                                 )}
                                 placeholder={constants.ENTER_ALBUM_NAME}
                                 ref={collectionNameInputRef}
@@ -82,8 +78,7 @@ export default function CollectionNamer({ attributes, ...props }: Props) {
 
                             <Form.Control.Feedback
                                 type="invalid"
-                                className="text-center"
-                            >
+                                className="text-center">
                                 {errors.albumName}
                             </Form.Control.Feedback>
                         </Form.Group>

+ 8 - 11
src/components/pages/gallery/CollectionOptions.tsx

@@ -24,7 +24,7 @@ interface Props {
 const CollectionOptions = (props: Props) => {
     const collectionRename = async (
         selectedCollection: Collection,
-        newName: string,
+        newName: string
     ) => {
         if (selectedCollection.name !== newName) {
             await renameCollection(selectedCollection, newName);
@@ -37,16 +37,16 @@ const CollectionOptions = (props: Props) => {
             buttonText: constants.RENAME,
             autoFilledName: getSelectedCollection(
                 props.selectedCollectionID,
-                props.collections,
+                props.collections
             )?.name,
             callback: (newName) => {
                 props.startLoadingBar();
                 collectionRename(
                     getSelectedCollection(
                         props.selectedCollectionID,
-                        props.collections,
+                        props.collections
                     ),
-                    newName,
+                    newName
                 );
             },
         });
@@ -64,7 +64,7 @@ const CollectionOptions = (props: Props) => {
                         props.selectedCollectionID,
                         props.syncWithRemote,
                         props.redirectToAll,
-                        props.setDialogMessage,
+                        props.setDialogMessage
                     );
                 },
                 variant: 'danger',
@@ -78,8 +78,7 @@ const CollectionOptions = (props: Props) => {
     const MenuLink = (props) => (
         <LinkButton
             style={{ fontSize: '14px', fontWeight: 700, padding: '8px 1em' }}
-            {...props}
-        >
+            {...props}>
             {props.children}
         </LinkButton>
     );
@@ -89,8 +88,7 @@ const CollectionOptions = (props: Props) => {
             style={{
                 background: '#282828',
                 padding: 0,
-            }}
-        >
+            }}>
             {props.children}
         </ListGroup.Item>
     );
@@ -111,8 +109,7 @@ const CollectionOptions = (props: Props) => {
                     <MenuItem>
                         <MenuLink
                             variant="danger"
-                            onClick={confirmDeleteCollection}
-                        >
+                            onClick={confirmDeleteCollection}>
                             {constants.DELETE}
                         </MenuLink>
                     </MenuItem>

+ 1 - 1
src/components/pages/gallery/CollectionSelector.tsx

@@ -73,7 +73,7 @@ function CollectionSelector({
                     </Card.Text>
                 </CollectionCard>
             </CollectionIcon>
-        ),
+        )
     );
 
     return (

+ 45 - 31
src/components/pages/gallery/Collections.tsx

@@ -3,9 +3,7 @@ import { SetDialogMessage } from 'components/MessageDialog';
 import NavigationButton, {
     SCROLL_DIRECTION,
 } from 'components/NavigationButton';
-import React, {
-    useEffect, useRef, useState,
-} from 'react';
+import React, { useEffect, useRef, useState } from 'react';
 import { OverlayTrigger, Tooltip } from 'react-bootstrap';
 import { Collection, CollectionType } from 'services/collectionService';
 import { User } from 'services/userService';
@@ -26,7 +24,7 @@ interface CollectionProps {
     setCollectionNamerAttributes: SetCollectionNamerAttributes;
     startLoadingBar: () => void;
     searchMode: boolean;
-    collectionFilesCount: Map<number, number>
+    collectionFilesCount: Map<number, number>;
 }
 
 const Container = styled.div`
@@ -38,7 +36,7 @@ const Container = styled.div`
     position: relative;
     padding: 0 24px;
 
-    @media(max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
+    @media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
         padding: 0 4px;
     }
 `;
@@ -58,7 +56,8 @@ const Chip = styled.button<{ active: boolean }>`
     padding-left: 24px;
     margin: 3px;
     border: none;
-    background-color: ${(props) => (props.active ? '#fff' : 'rgba(255, 255, 255, 0.3)')};
+    background-color: ${(props) =>
+        props.active ? '#fff' : 'rgba(255, 255, 255, 0.3)'};
     outline: none !important;
     &:hover {
         background-color: ${(props) => !props.active && '#bbbbbb'};
@@ -71,9 +70,11 @@ const Chip = styled.button<{ active: boolean }>`
 
 export default function Collections(props: CollectionProps) {
     const { selected, collections, selectCollection } = props;
-    const [selectedCollectionID, setSelectedCollectionID] = useState<number>(null);
+    const [selectedCollectionID, setSelectedCollectionID] =
+        useState<number>(null);
     const collectionRef = useRef<HTMLDivElement>(null);
-    const [collectionShareModalView, setCollectionShareModalView] = useState(false);
+    const [collectionShareModalView, setCollectionShareModalView] =
+        useState(false);
     const [scrollObj, setScrollObj] = useState<{
         scrollLeft?: number;
         scrollWidth?: number;
@@ -82,7 +83,8 @@ export default function Collections(props: CollectionProps) {
 
     const updateScrollObj = () => {
         if (collectionRef.current) {
-            const { scrollLeft, scrollWidth, clientWidth } = collectionRef.current;
+            const { scrollLeft, scrollWidth, clientWidth } =
+                collectionRef.current;
             setScrollObj({ scrollLeft, scrollWidth, clientWidth });
         }
     };
@@ -126,10 +128,13 @@ export default function Collections(props: CollectionProps) {
     const renderTooltip = (collectionID) => {
         const fileCount = props.collectionFilesCount?.get(collectionID);
         return (
-            <Tooltip style={{
-                padding: '0',
-                paddingBottom: '5px',
-            }} id="button-tooltip" {...props}>
+            <Tooltip
+                style={{
+                    padding: '0',
+                    paddingBottom: '5px',
+                }}
+                id="button-tooltip"
+                {...props}>
                 <div
                     {...props}
                     style={{
@@ -139,8 +144,7 @@ export default function Collections(props: CollectionProps) {
                         color: '#ddd',
                         borderRadius: 3,
                         fontSize: '12px',
-                    }}
-                >
+                    }}>
                     {fileCount} {fileCount > 1 ? 'items' : 'item'}
                 </div>
             </Tooltip>
@@ -155,7 +159,7 @@ export default function Collections(props: CollectionProps) {
                     onHide={() => setCollectionShareModalView(false)}
                     collection={getSelectedCollection(
                         selectedCollectionID,
-                        props.collections,
+                        props.collections
                     )}
                     syncWithRemote={props.syncWithRemote}
                 />
@@ -181,35 +185,45 @@ export default function Collections(props: CollectionProps) {
                                 key={item.id}
                                 placement="top"
                                 delay={{ show: 250, hide: 400 }}
-                                overlay={renderTooltip(item.id)}
-                            >
+                                overlay={renderTooltip(item.id)}>
                                 <Chip
-
                                     active={selected === item.id}
-                                    onClick={clickHandler(item)}
-                                >
+                                    onClick={clickHandler(item)}>
                                     {item.name}
                                     {item.type !== CollectionType.favorites &&
-                                        item.owner.id === user?.id ? (<OverlayTrigger
+                                    item.owner.id === user?.id ? (
+                                        <OverlayTrigger
                                             rootClose
                                             trigger="click"
                                             placement="bottom"
-                                            overlay={collectionOptions}
-                                        >
+                                            overlay={collectionOptions}>
                                             <OptionIcon
-                                                onClick={() => setSelectedCollectionID(item.id)}
+                                                onClick={() =>
+                                                    setSelectedCollectionID(
+                                                        item.id
+                                                    )
+                                                }
                                             />
-                                        </OverlayTrigger>) : (<div style={{
-                                            display: 'inline-block',
-                                            width: '24px',
-                                        }}
-                                        />)}
+                                        </OverlayTrigger>
+                                    ) : (
+                                        <div
+                                            style={{
+                                                display: 'inline-block',
+                                                width: '24px',
+                                            }}
+                                        />
+                                    )}
                                 </Chip>
                             </OverlayTrigger>
                         ))}
                     </Wrapper>
                     {scrollObj.scrollLeft <
-                        scrollObj.scrollWidth - scrollObj.clientWidth && (<NavigationButton scrollDirection={SCROLL_DIRECTION.RIGHT} onClick={scrollCollection(SCROLL_DIRECTION.RIGHT)} />)}
+                        scrollObj.scrollWidth - scrollObj.clientWidth && (
+                        <NavigationButton
+                            scrollDirection={SCROLL_DIRECTION.RIGHT}
+                            onClick={scrollCollection(SCROLL_DIRECTION.RIGHT)}
+                        />
+                    )}
                 </Container>
             </>
         )

+ 2 - 3
src/components/pages/gallery/LinkButton.tsx

@@ -4,7 +4,7 @@ enum ButtonVariant {
     success = 'success',
     danger = 'danger',
     secondary = 'secondary',
-    warning='warning'
+    warning = 'warning',
 }
 type Props = React.PropsWithChildren<{
     onClick: any;
@@ -35,8 +35,7 @@ export default function LinkButton(props: Props) {
                 marginBottom: 0,
                 ...props.style,
             }}
-            onClick={props?.onClick ?? (() => null)}
-        >
+            onClick={props?.onClick ?? (() => null)}>
             {props.children}
         </h5>
     );

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

@@ -16,17 +16,18 @@ const OptionIcon = ({ onClick }: Props) => (
             onClick();
             e.stopPropagation();
         }}
-        style={{ marginBottom: '2px' }}
-    >
+        style={{ marginBottom: '2px' }}>
         <svg
             xmlns="http://www.w3.org/2000/svg"
             height="20px"
             width="24px"
             viewBox="0 0 24 24"
-            fill="#000000"
-        >
+            fill="#000000">
             <path d="M0 0h24v24H0V0z" fill="none" />
-            <path fill="#666" d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />
+            <path
+                fill="#666"
+                d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"
+            />
         </svg>
     </OptionIconWrapper>
 );

+ 89 - 81
src/components/pages/gallery/PlanSelector.tsx

@@ -79,19 +79,24 @@ function PlanSelector(props: Props) {
     const [plans, setPlans] = useState(null);
     const [planPeriod, setPlanPeriod] = useState<PLAN_PERIOD>(PLAN_PERIOD.YEAR);
     const togglePeriod = () => {
-        setPlanPeriod((prevPeriod) => (prevPeriod === PLAN_PERIOD.MONTH
-            ? PLAN_PERIOD.YEAR
-            : PLAN_PERIOD.MONTH));
+        setPlanPeriod((prevPeriod) =>
+            prevPeriod === PLAN_PERIOD.MONTH
+                ? PLAN_PERIOD.YEAR
+                : PLAN_PERIOD.MONTH
+        );
     };
     useEffect(() => {
-        if ( props.modalView) {
+        if (props.modalView) {
             const main = async () => {
                 props.setLoading(true);
-                let plans=await billingService.getPlans();
+                let plans = await billingService.getPlans();
 
-                const planNotListed= plans.filter((plan)=>isUserSubscribedPlan(plan, subscription)).length===0;
+                const planNotListed =
+                    plans.filter((plan) =>
+                        isUserSubscribedPlan(plan, subscription)
+                    ).length === 0;
                 if (!isOnFreePlan(subscription) && planNotListed) {
-                    plans=[planForSubscription(subscription), ...plans];
+                    plans = [planForSubscription(subscription), ...plans];
                 }
                 setPlans(plans);
                 props.setLoading(false);
@@ -114,7 +119,7 @@ function PlanSelector(props: Props) {
         } else if (hasStripeSubscription(subscription)) {
             props.setDialogMessage({
                 title: `${constants.CONFIRM} ${reverseString(
-                    constants.UPDATE_SUBSCRIPTION,
+                    constants.UPDATE_SUBSCRIPTION
                 )}`,
                 content: constants.UPDATE_SUBSCRIPTION_MESSAGE,
                 staticBackdrop: true,
@@ -125,7 +130,7 @@ function PlanSelector(props: Props) {
                         plan,
                         props.setDialogMessage,
                         props.setLoading,
-                        props.closeModal,
+                        props.closeModal
                     ),
                     variant: 'success',
                 },
@@ -154,8 +159,7 @@ function PlanSelector(props: Props) {
                 key={plan.stripeID}
                 className="subscription-plan-selector"
                 selected={isUserSubscribedPlan(plan, subscription)}
-                onClick={async () => (await onPlanSelect(plan))}
-            >
+                onClick={async () => await onPlanSelect(plan)}>
                 <div>
                     <span
                         style={{
@@ -163,8 +167,7 @@ function PlanSelector(props: Props) {
                             fontWeight: 900,
                             fontSize: '40px',
                             lineHeight: '40px',
-                        }}
-                    >
+                        }}>
                         {convertBytesToGBs(plan.storage, 0)}
                     </span>
                     <span
@@ -172,24 +175,30 @@ function PlanSelector(props: Props) {
                             color: '#858585',
                             fontSize: '24px',
                             fontWeight: 900,
-                        }}
-                    >
+                        }}>
                         {' '}
-                    GB
+                        GB
                     </span>
                 </div>
                 <div
                     className="bold-text"
-                    style={{ color: '#aaa', lineHeight: '36px', fontSize: '20px' }}
-                >
+                    style={{
+                        color: '#aaa',
+                        lineHeight: '36px',
+                        fontSize: '20px',
+                    }}>
                     {`${plan.price} / ${plan.period}`}
                 </div>
                 <Button
                     variant="outline-success"
                     block
-                    style={{ marginTop: '20px', fontSize: '14px', display: 'flex', justifyContent: 'center' }}
-                    disabled={isUserSubscribedPlan(plan, subscription)}
-                >
+                    style={{
+                        marginTop: '20px',
+                        fontSize: '14px',
+                        display: 'flex',
+                        justifyContent: 'center',
+                    }}
+                    disabled={isUserSubscribedPlan(plan, subscription)}>
                     {constants.CHOOSE_PLAN_BTN}
                     <ArrowEast style={{ marginLeft: '5px' }} />
                 </Button>
@@ -202,16 +211,14 @@ function PlanSelector(props: Props) {
             size="xl"
             centered
             backdrop={hasPaidSubscription(subscription) ? 'true' : 'static'}
-            contentClassName="plan-selector-modal-content"
-        >
+            contentClassName="plan-selector-modal-content">
             <Modal.Header closeButton>
                 <Modal.Title
                     style={{
                         marginLeft: '12px',
                         width: '100%',
                         textAlign: 'center',
-                    }}
-                >
+                    }}>
                     <span>
                         {hasPaidSubscription(subscription)
                             ? constants.MANAGE_PLAN
@@ -224,22 +231,23 @@ function PlanSelector(props: Props) {
                     <div style={{ display: 'flex' }}>
                         <span
                             className="bold-text"
-                            style={{ fontSize: '16px' }}
-                        >
+                            style={{ fontSize: '16px' }}>
                             {constants.MONTHLY}
                         </span>
 
                         <Form.Switch
                             checked={planPeriod === PLAN_PERIOD.YEAR}
                             id="plan-period-toggler"
-                            style={{ margin: '-4px 0 20px 15px', fontSize: '10px' }}
+                            style={{
+                                margin: '-4px 0 20px 15px',
+                                fontSize: '10px',
+                            }}
                             className="custom-switch-md"
                             onChange={togglePeriod}
                         />
                         <span
                             className="bold-text"
-                            style={{ fontSize: '16px' }}
-                        >
+                            style={{ fontSize: '16px' }}>
                             {constants.YEARLY}
                         </span>
                     </div>
@@ -251,8 +259,7 @@ function PlanSelector(props: Props) {
                         flexWrap: 'wrap',
                         minHeight: '212px',
                         margin: '5px 0',
-                    }}
-                >
+                    }}>
                     {plans && PlanIcons}
                 </div>
                 <DeadCenter style={{ marginBottom: '30px' }}>
@@ -261,55 +268,55 @@ function PlanSelector(props: Props) {
                             {isSubscriptionCancelled(subscription) ? (
                                 <LinkButton
                                     variant="success"
-                                    onClick={() => props.setDialogMessage({
-                                        title:
-                                                constants.CONFIRM_ACTIVATE_SUBSCRIPTION,
-                                        content: constants.ACTIVATE_SUBSCRIPTION_MESSAGE(
-                                            subscription.expiryTime,
-                                        ),
-                                        staticBackdrop: true,
-                                        proceed: {
-                                            text:
-                                                    constants.ACTIVATE_SUBSCRIPTION,
-                                            action: activateSubscription.bind(
-                                                null,
-                                                props.setDialogMessage,
-                                                props.closeModal,
-                                                props.setLoading,
-                                            ),
-                                            variant: 'success',
-                                        },
-                                        close: {
-                                            text: constants.CANCEL,
-                                        },
-                                    })}
-                                >
+                                    onClick={() =>
+                                        props.setDialogMessage({
+                                            title: constants.CONFIRM_ACTIVATE_SUBSCRIPTION,
+                                            content:
+                                                constants.ACTIVATE_SUBSCRIPTION_MESSAGE(
+                                                    subscription.expiryTime
+                                                ),
+                                            staticBackdrop: true,
+                                            proceed: {
+                                                text: constants.ACTIVATE_SUBSCRIPTION,
+                                                action: activateSubscription.bind(
+                                                    null,
+                                                    props.setDialogMessage,
+                                                    props.closeModal,
+                                                    props.setLoading
+                                                ),
+                                                variant: 'success',
+                                            },
+                                            close: {
+                                                text: constants.CANCEL,
+                                            },
+                                        })
+                                    }>
                                     {constants.ACTIVATE_SUBSCRIPTION}
                                 </LinkButton>
                             ) : (
                                 <LinkButton
                                     variant="danger"
-                                    onClick={() => props.setDialogMessage({
-                                        title:
-                                                constants.CONFIRM_CANCEL_SUBSCRIPTION,
-                                        content: constants.CANCEL_SUBSCRIPTION_MESSAGE(),
-                                        staticBackdrop: true,
-                                        proceed: {
-                                            text:
-                                                    constants.CANCEL_SUBSCRIPTION,
-                                            action: cancelSubscription.bind(
-                                                null,
-                                                props.setDialogMessage,
-                                                props.closeModal,
-                                                props.setLoading,
-                                            ),
-                                            variant: 'danger',
-                                        },
-                                        close: {
-                                            text: constants.CANCEL,
-                                        },
-                                    })}
-                                >
+                                    onClick={() =>
+                                        props.setDialogMessage({
+                                            title: constants.CONFIRM_CANCEL_SUBSCRIPTION,
+                                            content:
+                                                constants.CANCEL_SUBSCRIPTION_MESSAGE(),
+                                            staticBackdrop: true,
+                                            proceed: {
+                                                text: constants.CANCEL_SUBSCRIPTION,
+                                                action: cancelSubscription.bind(
+                                                    null,
+                                                    props.setDialogMessage,
+                                                    props.closeModal,
+                                                    props.setLoading
+                                                ),
+                                                variant: 'danger',
+                                            },
+                                            close: {
+                                                text: constants.CANCEL,
+                                            },
+                                        })
+                                    }>
                                     {constants.CANCEL_SUBSCRIPTION}
                                 </LinkButton>
                             )}
@@ -318,10 +325,9 @@ function PlanSelector(props: Props) {
                                 onClick={updatePaymentMethod.bind(
                                     null,
                                     props.setDialogMessage,
-                                    props.setLoading,
+                                    props.setLoading
                                 )}
-                                style={{ marginTop: '20px' }}
-                            >
+                                style={{ marginTop: '20px' }}>
                                 {constants.MANAGEMENT_PORTAL}
                             </LinkButton>
                         </>
@@ -329,8 +335,10 @@ function PlanSelector(props: Props) {
                         <LinkButton
                             variant="primary"
                             onClick={props.closeModal}
-                            style={{ color: 'rgb(121, 121, 121)', marginTop: '20px' }}
-                        >
+                            style={{
+                                color: 'rgb(121, 121, 121)',
+                                marginTop: '20px',
+                            }}>
                             {isOnFreePlan(subscription)
                                 ? constants.SKIP
                                 : constants.CLOSE}

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

@@ -25,7 +25,7 @@ const Check = styled.input`
     opacity: 0;
     outline: none;
     cursor: pointer;
-    @media(pointer: coarse) {
+    @media (pointer: coarse) {
         pointer-events: none;
     }
 
@@ -183,8 +183,7 @@ export default function PreviewCard(props: IProps) {
             onClick={handleClick}
             disabled={!forcedEnable && !file?.msrc && !imgSrc}
             selected={selected}
-            {...(selectable ? useLongPress(longPressCallback, 500) : {})}
-        >
+            {...(selectable ? useLongPress(longPressCallback, 500) : {})}>
             {selectable && (
                 <Check
                     type="checkbox"

+ 30 - 20
src/components/pages/gallery/SelectedFileOptions.tsx

@@ -42,32 +42,42 @@ const SelectedFileOptions = ({
     count,
     clearSelection,
 }: Props) => {
-    const addToCollection = () => setCollectionSelectorAttributes({
-        callback: (collection) => addToCollectionHelper(null, collection),
-        showNextModal: showCreateCollectionModal,
-        title: constants.ADD_TO_COLLECTION,
-    });
+    const addToCollection = () =>
+        setCollectionSelectorAttributes({
+            callback: (collection) => addToCollectionHelper(null, collection),
+            showNextModal: showCreateCollectionModal,
+            title: constants.ADD_TO_COLLECTION,
+        });
 
-    const deleteHandler = () => setDialogMessage({
-        title: constants.CONFIRM_DELETE_FILE,
-        content: constants.DELETE_FILE_MESSAGE,
-        staticBackdrop: true,
-        proceed: {
-            action: deleteFileHelper,
-            text: constants.DELETE,
-            variant: 'danger',
-        },
-        close: { text: constants.CANCEL },
-    });
+    const deleteHandler = () =>
+        setDialogMessage({
+            title: constants.CONFIRM_DELETE_FILE,
+            content: constants.DELETE_FILE_MESSAGE,
+            staticBackdrop: true,
+            proceed: {
+                action: deleteFileHelper,
+                text: constants.DELETE,
+                variant: 'danger',
+            },
+            close: { text: constants.CANCEL },
+        });
 
     return (
         <SelectionBar>
             <SelectionContainer>
-                <IconButton onClick={clearSelection}><CrossIcon /></IconButton>
-                <div>{count} {constants.SELECTED}</div>
+                <IconButton onClick={clearSelection}>
+                    <CrossIcon />
+                </IconButton>
+                <div>
+                    {count} {constants.SELECTED}
+                </div>
             </SelectionContainer>
-            <IconButton onClick={addToCollection}><AddIcon /></IconButton>
-            <IconButton onClick={deleteHandler}><DeleteIcon /></IconButton>
+            <IconButton onClick={addToCollection}>
+                <AddIcon />
+            </IconButton>
+            <IconButton onClick={deleteHandler}>
+                <DeleteIcon />
+            </IconButton>
         </SelectionBar>
     );
 };

+ 31 - 23
src/components/pages/gallery/Upload.tsx

@@ -1,5 +1,8 @@
 import React, { useContext, useEffect, useState } from 'react';
-import UploadService, { FileWithCollection, UPLOAD_STAGES } from 'services/uploadService';
+import UploadService, {
+    FileWithCollection,
+    UPLOAD_STAGES,
+} from 'services/uploadService';
 import { createAlbum } from 'services/collectionService';
 import { getLocalFiles } from 'services/fileService';
 import constants from 'utils/strings/constants';
@@ -25,8 +28,8 @@ interface Props {
     setDialogMessage: SetDialogMessage;
     setUploadInProgress: any;
     showCollectionSelector: () => void;
-    fileRejections:FileRejection[];
-    setFiles:SetFiles;
+    fileRejections: FileRejection[];
+    setFiles: SetFiles;
 }
 
 export enum UPLOAD_STRATEGY {
@@ -42,18 +45,22 @@ interface AnalysisResult {
 export default function Upload(props: Props) {
     const [progressView, setProgressView] = useState(false);
     const [uploadStage, setUploadStage] = useState<UPLOAD_STAGES>(
-        UPLOAD_STAGES.START,
+        UPLOAD_STAGES.START
     );
     const [fileCounter, setFileCounter] = useState({ current: 0, total: 0 });
     const [fileProgress, setFileProgress] = useState(new Map<string, number>());
-    const [uploadResult, setUploadResult]=useState(new Map<string, number>());
+    const [uploadResult, setUploadResult] = useState(new Map<string, number>());
     const [percentComplete, setPercentComplete] = useState(0);
     const [choiceModalView, setChoiceModalView] = useState(false);
-    const [fileAnalysisResult, setFileAnalysisResult] = useState<AnalysisResult>(null);
+    const [fileAnalysisResult, setFileAnalysisResult] =
+        useState<AnalysisResult>(null);
     const appContext = useContext(AppContext);
 
     useEffect(() => {
-        if (props.acceptedFiles?.length > 0 || appContext.sharedFiles?.length > 0) {
+        if (
+            props.acceptedFiles?.length > 0 ||
+            appContext.sharedFiles?.length > 0
+        ) {
             props.setLoading(true);
 
             let fileAnalysisResult;
@@ -92,7 +99,7 @@ export default function Upload(props: Props) {
                 props.closeCollectionSelector();
                 await uploadFilesToNewCollections(
                     UPLOAD_STRATEGY.SINGLE_COLLECTION,
-                    collectionName,
+                    collectionName
                 );
             },
         });
@@ -121,7 +128,7 @@ export default function Upload(props: Props) {
         if (commonPathPrefix) {
             commonPathPrefix = commonPathPrefix.substr(
                 1,
-                commonPathPrefix.lastIndexOf('/') - 1,
+                commonPathPrefix.lastIndexOf('/') - 1
             );
         }
         return {
@@ -135,7 +142,7 @@ export default function Upload(props: Props) {
             const filePath = file['path'];
             const folderPath = filePath.substr(0, filePath.lastIndexOf('/'));
             const folderName = folderPath.substr(
-                folderPath.lastIndexOf('/') + 1,
+                folderPath.lastIndexOf('/') + 1
             );
             if (!collectionWiseFiles.has(folderName)) {
                 collectionWiseFiles.set(folderName, []);
@@ -148,10 +155,11 @@ export default function Upload(props: Props) {
     const uploadFilesToExistingCollection = async (collection) => {
         try {
             uploadInit();
-            const filesWithCollectionToUpload: FileWithCollection[] = props.acceptedFiles.map((file) => ({
-                file,
-                collection,
-            }));
+            const filesWithCollectionToUpload: FileWithCollection[] =
+                props.acceptedFiles.map((file) => ({
+                    file,
+                    collection,
+                }));
             await uploadFiles(filesWithCollectionToUpload);
         } catch (e) {
             logError(e, 'Failed to upload files to existing collections');
@@ -160,7 +168,7 @@ export default function Upload(props: Props) {
 
     const uploadFilesToNewCollections = async (
         strategy: UPLOAD_STRATEGY,
-        collectionName,
+        collectionName
     ) => {
         try {
             uploadInit();
@@ -197,13 +205,13 @@ export default function Upload(props: Props) {
     };
 
     const uploadFiles = async (
-        filesWithCollectionToUpload: FileWithCollection[],
+        filesWithCollectionToUpload: FileWithCollection[]
     ) => {
         try {
             props.setUploadInProgress(true);
             props.closeCollectionSelector();
             await props.syncWithRemote(true, true);
-            const localFiles= await getLocalFiles();
+            const localFiles = await getLocalFiles();
             await UploadService.uploadFiles(
                 filesWithCollectionToUpload,
                 localFiles,
@@ -214,7 +222,7 @@ export default function Upload(props: Props) {
                     setFileProgress,
                     setUploadResult,
                 },
-                props.setFiles,
+                props.setFiles
             );
         } catch (err) {
             props.setBannerMessage(err.message);
@@ -226,13 +234,12 @@ export default function Upload(props: Props) {
             props.syncWithRemote();
         }
     };
-    const retryFailed = async (
-    ) => {
+    const retryFailed = async () => {
         try {
             props.setUploadInProgress(true);
             uploadInit();
             await props.syncWithRemote(true, true);
-            const localFiles= await getLocalFiles();
+            const localFiles = await getLocalFiles();
             await UploadService.retryFailedFiles(localFiles);
         } catch (err) {
             props.setBannerMessage(err.message);
@@ -244,14 +251,15 @@ export default function Upload(props: Props) {
         }
     };
 
-
     return (
         <>
             <ChoiceModal
                 show={choiceModalView}
                 onHide={() => setChoiceModalView(false)}
                 uploadFiles={uploadFilesToNewCollections}
-                showCollectionCreateModal={() => showCreateCollectionModal(fileAnalysisResult)}
+                showCollectionCreateModal={() =>
+                    showCreateCollectionModal(fileAnalysisResult)
+                }
             />
             <UploadProgress
                 now={percentComplete}

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

@@ -21,8 +21,7 @@ function UploadButton({ openFileUploader, isFirstFetch }) {
                 viewBox="0 0 24 24"
                 fill="green"
                 width="32px"
-                height="32px"
-            >
+                height="32px">
                 <path fill="none" d="M0 0h24v24H0z" />
                 <path
                     fill="#2dc262"

+ 150 - 114
src/components/pages/gallery/UploadProgress.tsx

@@ -1,9 +1,7 @@
 import ExpandLess from 'components/icons/ExpandLess';
 import ExpandMore from 'components/icons/ExpandMore';
 import React, { useState } from 'react';
-import {
-    Button, Modal, ProgressBar,
-} from 'react-bootstrap';
+import { Button, Modal, ProgressBar } from 'react-bootstrap';
 import { FileRejection } from 'react-dropzone';
 import { FileUploadResults, UPLOAD_STAGES } from 'services/uploadService';
 import styled from 'styled-components';
@@ -19,77 +17,85 @@ interface Props {
     retryFailed;
     fileProgress: Map<string, number>;
     show;
-    fileRejections:FileRejection[]
-    uploadResult:Map<string, number>;
+    fileRejections: FileRejection[];
+    uploadResult: Map<string, number>;
 }
-interface FileProgresses{
-    fileName:string;
-    progress:number;
+interface FileProgresses {
+    fileName: string;
+    progress: number;
 }
 
-const Content =styled.div<{collapsed:boolean, sm?:boolean, height?:number}>`
+const Content = styled.div<{
+    collapsed: boolean;
+    sm?: boolean;
+    height?: number;
+}>`
     overflow: hidden;
-    height:${(props)=> props.collapsed?'0px':props.height+'px'};
-    transition:${(props)=> 'height '+0.001*props.height+'s ease-out'};
+    height: ${(props) => (props.collapsed ? '0px' : props.height + 'px')};
+    transition: ${(props) => 'height ' + 0.001 * props.height + 's ease-out'};
     margin-bottom: 20px;
-    & >p {
-        padding-left:35px;
-        margin:0;
+    & > p {
+        padding-left: 35px;
+        margin: 0;
     }
 `;
-const FileList =styled.ul`
-    padding-left:50px;
-    margin-top:5px;
+const FileList = styled.ul`
+    padding-left: 50px;
+    margin-top: 5px;
     & > li {
-        padding-left:10px;
-        margin-bottom:10px;
-        color:#ccc;
+        padding-left: 10px;
+        margin-bottom: 10px;
+        color: #ccc;
     }
 `;
 
-const SectionTitle =styled.div`
-    display:flex;
-    justify-content:space-between;
-    padding:0 20px;
-    color:#eee;
-    font-size:20px;
-    cursor:pointer;
+const SectionTitle = styled.div`
+    display: flex;
+    justify-content: space-between;
+    padding: 0 20px;
+    color: #eee;
+    font-size: 20px;
+    cursor: pointer;
 `;
 
-
-interface ResultSectionProps{
+interface ResultSectionProps {
     fileUploadResultMap: Map<FileUploadResults, string[]>;
-    fileUploadResult:FileUploadResults;
+    fileUploadResult: FileUploadResults;
     sectionTitle;
     sectionInfo;
-    infoHeight:number;
+    infoHeight: number;
 }
-const ResultSection =(props:ResultSectionProps)=>{
-    const [listView, setListView]=useState(false);
-    const fileList=props.fileUploadResultMap?.get(props.fileUploadResult);
+const ResultSection = (props: ResultSectionProps) => {
+    const [listView, setListView] = useState(false);
+    const fileList = props.fileUploadResultMap?.get(props.fileUploadResult);
     if (!fileList?.length) {
         return <></>;
     }
-    return (<>
-        <SectionTitle onClick={()=>setListView(!listView)} > {props.sectionTitle} {listView?<ExpandLess/>:<ExpandMore/>}</SectionTitle>
-        <Content collapsed={!listView} height={fileList.length *33 + props.infoHeight}>
-            <p>{props.sectionInfo}</p>
-            <FileList>
-                {fileList.map((fileName) => (
-
-                    <li key={fileName}>
-                        {fileName}
-                    </li>
-                ))}
-            </FileList>
-        </Content>
-    </>);
+    return (
+        <>
+            <SectionTitle onClick={() => setListView(!listView)}>
+                {' '}
+                {props.sectionTitle}{' '}
+                {listView ? <ExpandLess /> : <ExpandMore />}
+            </SectionTitle>
+            <Content
+                collapsed={!listView}
+                height={fileList.length * 33 + props.infoHeight}>
+                <p>{props.sectionInfo}</p>
+                <FileList>
+                    {fileList.map((fileName) => (
+                        <li key={fileName}>{fileName}</li>
+                    ))}
+                </FileList>
+            </Content>
+        </>
+    );
 };
 
 export default function UploadProgress(props: Props) {
     const fileProgressStatuses = [] as FileProgresses[];
     const fileUploadResultMap = new Map<FileUploadResults, string[]>();
-    let filesNotUploaded=false;
+    let filesNotUploaded = false;
 
     if (props.fileProgress) {
         for (const [fileName, progress] of props.fileProgress) {
@@ -101,10 +107,10 @@ export default function UploadProgress(props: Props) {
             if (!fileUploadResultMap.has(progress)) {
                 fileUploadResultMap.set(progress, []);
             }
-            if (progress<0) {
-                filesNotUploaded=true;
+            if (progress < 0) {
+                filesNotUploaded = true;
             }
-            const fileList= fileUploadResultMap.get(progress);
+            const fileList = fileUploadResultMap.get(progress);
             fileUploadResultMap.set(progress, [...fileList, fileName]);
         }
     }
@@ -119,84 +125,114 @@ export default function UploadProgress(props: Props) {
             }
             aria-labelledby="contained-modal-title-vcenter"
             centered
-            backdrop={
-                fileProgressStatuses?.length !== 0 ? 'static' : 'true'
-            }
-        >
+            backdrop={fileProgressStatuses?.length !== 0 ? 'static' : 'true'}>
             <Modal.Header
-                style={{ display: 'flex',
+                style={{
+                    display: 'flex',
                     justifyContent: 'center',
                     textAlign: 'center',
                     borderBottom: 'none',
                     paddingTop: '30px',
-                    paddingBottom: '0px' }}
-                closeButton={props.uploadStage === UPLOAD_STAGES.FINISH}
-            >
-
+                    paddingBottom: '0px',
+                }}
+                closeButton={props.uploadStage === UPLOAD_STAGES.FINISH}>
                 <h4 style={{ width: '100%' }}>
                     {props.uploadStage === UPLOAD_STAGES.UPLOADING
-                        ? constants.UPLOAD[props.uploadStage](
-                            props.fileCounter,
-                        )
+                        ? constants.UPLOAD[props.uploadStage](props.fileCounter)
                         : constants.UPLOAD[props.uploadStage]}
                 </h4>
             </Modal.Header>
             <Modal.Body>
-                {(props.uploadStage === UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ||
-                        props.uploadStage === UPLOAD_STAGES.UPLOADING) &&
-                    (
-                        < ProgressBar
-                            now={props.now}
-                            animated
-                            variant="upload-progress-bar"
-                        />
-                    )
-                }
-                {fileProgressStatuses.length>0 &&
-                <FileList>
-                    {fileProgressStatuses.map(({ fileName, progress }) => (
-                        <li key={fileName} style={{ marginTop: '12px' }}>
-                            {props.uploadStage===UPLOAD_STAGES.FINISH
-                                ? fileName
-                                : constants.FILE_UPLOAD_PROGRESS(
-                                    fileName,
-                                    progress,
-                                )
-                            }
-                        </li>
-                    ))}
-                </FileList>}
+                {(props.uploadStage ===
+                    UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ||
+                    props.uploadStage === UPLOAD_STAGES.UPLOADING) && (
+                    <ProgressBar
+                        now={props.now}
+                        animated
+                        variant="upload-progress-bar"
+                    />
+                )}
+                {fileProgressStatuses.length > 0 && (
+                    <FileList>
+                        {fileProgressStatuses.map(({ fileName, progress }) => (
+                            <li key={fileName} style={{ marginTop: '12px' }}>
+                                {props.uploadStage === UPLOAD_STAGES.FINISH
+                                    ? fileName
+                                    : constants.FILE_UPLOAD_PROGRESS(
+                                          fileName,
+                                          progress
+                                      )}
+                            </li>
+                        ))}
+                    </FileList>
+                )}
 
-                <ResultSection fileUploadResultMap={fileUploadResultMap} fileUploadResult={FileUploadResults.UPLOADED} sectionTitle={constants.SUCCESSFUL_UPLOADS} sectionInfo={constants.SUCCESS_INFO} infoHeight={32}/>
+                <ResultSection
+                    fileUploadResultMap={fileUploadResultMap}
+                    fileUploadResult={FileUploadResults.UPLOADED}
+                    sectionTitle={constants.SUCCESSFUL_UPLOADS}
+                    sectionInfo={constants.SUCCESS_INFO}
+                    infoHeight={32}
+                />
 
-                {props.uploadStage === UPLOAD_STAGES.FINISH && filesNotUploaded && (
-                    <AlertBanner variant="warning">
-                        {constants.FILE_NOT_UPLOADED_LIST}
-                    </AlertBanner>
-                )}
-                <ResultSection fileUploadResultMap={fileUploadResultMap} fileUploadResult={FileUploadResults.BLOCKED} sectionTitle={constants.BLOCKED_UPLOADS} sectionInfo={constants.ETAGS_BLOCKED(DESKTOP_APP_DOWNLOAD_URL)} infoHeight={140}/>
-                <ResultSection fileUploadResultMap={fileUploadResultMap} fileUploadResult={FileUploadResults.FAILED} sectionTitle={constants.FAILED_UPLOADS} sectionInfo={constants.FAILED_INFO} infoHeight={48}/>
-                <ResultSection fileUploadResultMap={fileUploadResultMap} fileUploadResult={FileUploadResults.SKIPPED} sectionTitle={constants.SKIPPED_FILES} sectionInfo={constants.SKIPPED_INFO} infoHeight={32}/>
-                <ResultSection fileUploadResultMap={fileUploadResultMap} fileUploadResult={FileUploadResults.UNSUPPORTED} sectionTitle={constants.UNSUPPORTED_FILES} sectionInfo={constants.UNSUPPORTED_INFO} infoHeight={32}/>
+                {props.uploadStage === UPLOAD_STAGES.FINISH &&
+                    filesNotUploaded && (
+                        <AlertBanner variant="warning">
+                            {constants.FILE_NOT_UPLOADED_LIST}
+                        </AlertBanner>
+                    )}
+                <ResultSection
+                    fileUploadResultMap={fileUploadResultMap}
+                    fileUploadResult={FileUploadResults.BLOCKED}
+                    sectionTitle={constants.BLOCKED_UPLOADS}
+                    sectionInfo={constants.ETAGS_BLOCKED(
+                        DESKTOP_APP_DOWNLOAD_URL
+                    )}
+                    infoHeight={140}
+                />
+                <ResultSection
+                    fileUploadResultMap={fileUploadResultMap}
+                    fileUploadResult={FileUploadResults.FAILED}
+                    sectionTitle={constants.FAILED_UPLOADS}
+                    sectionInfo={constants.FAILED_INFO}
+                    infoHeight={48}
+                />
+                <ResultSection
+                    fileUploadResultMap={fileUploadResultMap}
+                    fileUploadResult={FileUploadResults.SKIPPED}
+                    sectionTitle={constants.SKIPPED_FILES}
+                    sectionInfo={constants.SKIPPED_INFO}
+                    infoHeight={32}
+                />
+                <ResultSection
+                    fileUploadResultMap={fileUploadResultMap}
+                    fileUploadResult={FileUploadResults.UNSUPPORTED}
+                    sectionTitle={constants.UNSUPPORTED_FILES}
+                    sectionInfo={constants.UNSUPPORTED_INFO}
+                    infoHeight={32}
+                />
 
                 {props.uploadStage === UPLOAD_STAGES.FINISH && (
                     <Modal.Footer style={{ border: 'none' }}>
-                        {props.uploadStage===UPLOAD_STAGES.FINISH && ((fileUploadResultMap?.get(FileUploadResults.FAILED)?.length>0 || fileUploadResultMap?.get(FileUploadResults.BLOCKED)?.length>0)? (
-                            <Button
-                                variant="outline-success"
-                                style={{ width: '100%' }}
-                                onClick={props.retryFailed}
-                            >
-                                {constants.RETRY_FAILED}
-                            </Button>
-                        ) : ( <Button
-                            variant="outline-secondary"
-                            style={{ width: '100%' }}
-                            onClick={props.closeModal}
-                        >
-                            {constants.CLOSE}
-                        </Button>
-                        ))}
+                        {props.uploadStage === UPLOAD_STAGES.FINISH &&
+                            (fileUploadResultMap?.get(FileUploadResults.FAILED)
+                                ?.length > 0 ||
+                            fileUploadResultMap?.get(FileUploadResults.BLOCKED)
+                                ?.length > 0 ? (
+                                <Button
+                                    variant="outline-success"
+                                    style={{ width: '100%' }}
+                                    onClick={props.retryFailed}>
+                                    {constants.RETRY_FAILED}
+                                </Button>
+                            ) : (
+                                <Button
+                                    variant="outline-secondary"
+                                    style={{ width: '100%' }}
+                                    onClick={props.closeModal}>
+                                    {constants.CLOSE}
+                                </Button>
+                            ))}
                     </Modal.Footer>
                 )}
             </Modal.Body>

+ 54 - 37
src/pages/_app.tsx

@@ -397,29 +397,29 @@ export interface BannerMessage {
     variant: string;
 }
 
-
 type AppContextType = {
     showNavBar: (show: boolean) => void;
     sharedFiles: File[];
     resetSharedFiles: () => void;
     setDisappearingFlashMessage: (message: FlashMessage) => void;
-}
+};
 
 export interface FlashMessage {
     message: string;
-    severity: string
+    severity: string;
 }
 export const AppContext = createContext<AppContextType>(null);
 
 const redirectMap = {
-    roadmap: (token: string) => `${getEndpoint()}/users/roadmap?token=${encodeURIComponent(token)}`,
+    roadmap: (token: string) =>
+        `${getEndpoint()}/users/roadmap?token=${encodeURIComponent(token)}`,
 };
 
 export default function App({ Component, err }) {
     const router = useRouter();
     const [loading, setLoading] = useState(false);
     const [offline, setOffline] = useState(
-        typeof window !== 'undefined' && !window.navigator.onLine,
+        typeof window !== 'undefined' && !window.navigator.onLine
     );
     const [showNavbar, setShowNavBar] = useState(false);
     const [sharedFiles, setSharedFiles] = useState<File[]>(null);
@@ -451,7 +451,7 @@ export default function App({ Component, err }) {
             (error) => {
                 logError(error);
                 return Promise.reject(error);
-            },
+            }
         );
     }, []);
 
@@ -462,7 +462,7 @@ export default function App({ Component, err }) {
     useEffect(() => {
         console.log(
             `%c${constants.CONSOLE_WARNING_STOP}`,
-            'color: red; font-size: 52px;',
+            'color: red; font-size: 52px;'
         );
         console.log(`%c${constants.CONSOLE_WARNING_DESC}`, 'font-size: 20px;');
 
@@ -486,7 +486,9 @@ export default function App({ Component, err }) {
             if (redirectName) {
                 const user = getData(LS_KEYS.USER);
                 if (user?.token) {
-                    window.location.href = redirectMap[redirectName](user.token);
+                    window.location.href = redirectMap[redirectName](
+                        user.token
+                    );
                 }
             }
         });
@@ -513,24 +515,27 @@ export default function App({ Component, err }) {
             <Head>
                 <title>{constants.TITLE}</title>
                 {/* Cloudflare Web Analytics */}
-                {pageRootURL?.hostname && (pageRootURL.hostname === 'photos.ente.io'
-                    ? <script
-                        defer
-                        src="https://static.cloudflareinsights.com/beacon.min.js"
-                        data-cf-beacon='{"token": "6a388287b59c439cb2070f78cc89dde1"}'
-                    /> : pageRootURL.hostname === 'web.ente.io'
-                        ? < script
+                {pageRootURL?.hostname &&
+                    (pageRootURL.hostname === 'photos.ente.io' ? (
+                        <script
                             defer
-                            src='https://static.cloudflareinsights.com/beacon.min.js'
-                            data-cf-beacon='{"token": "dfde128b7bb34a618ad34a08f1ba7609"}' />
-                        : console.warn('Web analytics is disabled')
-                )
-                }
+                            src="https://static.cloudflareinsights.com/beacon.min.js"
+                            data-cf-beacon='{"token": "6a388287b59c439cb2070f78cc89dde1"}'
+                        />
+                    ) : pageRootURL.hostname === 'web.ente.io' ? (
+                        <script
+                            defer
+                            src="https://static.cloudflareinsights.com/beacon.min.js"
+                            data-cf-beacon='{"token": "dfde128b7bb34a618ad34a08f1ba7609"}'
+                        />
+                    ) : (
+                        console.warn('Web analytics is disabled')
+                    ))}
                 {/* End Cloudflare Web Analytics  */}
             </Head>
             <GlobalStyles />
-            {
-                showNavbar && <Navbar>
+            {showNavbar && (
+                <Navbar>
                     <FlexContainer>
                         <LogoImage
                             style={{ height: '24px', padding: '3px' }}
@@ -539,21 +544,33 @@ export default function App({ Component, err }) {
                         />
                     </FlexContainer>
                 </Navbar>
-            }
-            <MessageContainer>{offline && constants.OFFLINE_MSG}</MessageContainer>
-            {
-                sharedFiles &&
-                (router.pathname === '/gallery'
-                    ? <MessageContainer>{constants.FILES_TO_BE_UPLOADED(sharedFiles.length)}</MessageContainer>
-                    : <MessageContainer>{constants.LOGIN_TO_UPLOAD_FILES(sharedFiles.length)}</MessageContainer>)
-            }
-            {flashMessage && <FlashMessageBar flashMessage={flashMessage} onClose={() => setFlashMessage(null)} />}
-            <AppContext.Provider value={{
-                showNavBar,
-                sharedFiles,
-                resetSharedFiles,
-                setDisappearingFlashMessage,
-            }}>
+            )}
+            <MessageContainer>
+                {offline && constants.OFFLINE_MSG}
+            </MessageContainer>
+            {sharedFiles &&
+                (router.pathname === '/gallery' ? (
+                    <MessageContainer>
+                        {constants.FILES_TO_BE_UPLOADED(sharedFiles.length)}
+                    </MessageContainer>
+                ) : (
+                    <MessageContainer>
+                        {constants.LOGIN_TO_UPLOAD_FILES(sharedFiles.length)}
+                    </MessageContainer>
+                ))}
+            {flashMessage && (
+                <FlashMessageBar
+                    flashMessage={flashMessage}
+                    onClose={() => setFlashMessage(null)}
+                />
+            )}
+            <AppContext.Provider
+                value={{
+                    showNavBar,
+                    sharedFiles,
+                    resetSharedFiles,
+                    setDisappearingFlashMessage,
+                }}>
                 {loading ? (
                     <Container>
                         <EnteSpinner>

+ 20 - 9
src/pages/_document.tsx

@@ -1,7 +1,5 @@
 import React from 'react';
-import Document, {
-    Html, Head, Main, NextScript,
-} from 'next/document';
+import Document, { Html, Head, Main, NextScript } from 'next/document';
 import { ServerStyleSheet } from 'styled-components';
 
 export default class MyDocument extends Document {
@@ -10,9 +8,11 @@ export default class MyDocument extends Document {
         const originalRenderPage = ctx.renderPage;
 
         try {
-            ctx.renderPage = () => originalRenderPage({
-                enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
-            });
+            ctx.renderPage = () =>
+                originalRenderPage({
+                    enhanceApp: (App) => (props) =>
+                        sheet.collectStyles(<App {...props} />),
+                });
 
             const initialProps = await Document.getInitialProps(ctx);
             return {
@@ -37,13 +37,24 @@ export default class MyDocument extends Document {
                         name="description"
                         content="ente is a privacy focussed photo storage service that offers end-to-end encryption."
                     />
-                    <link rel="icon" href="/images/favicon.png" type="image/png" />
+                    <link
+                        rel="icon"
+                        href="/images/favicon.png"
+                        type="image/png"
+                    />
                     <link rel="manifest" href="manifest.json" />
                     <link rel="apple-touch-icon" href="/images/ente-512.png" />
                     <meta name="theme-color" content="#111" />
-                    <link rel="icon" type="image/png" href="/images/favicon.png" />
+                    <link
+                        rel="icon"
+                        type="image/png"
+                        href="/images/favicon.png"
+                    />
                     <meta name="apple-mobile-web-app-capable" content="yes" />
-                    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
+                    <meta
+                        name="apple-mobile-web-app-status-bar-style"
+                        content="black"
+                    />
                 </Head>
                 <body>
                     <Main />

+ 2 - 1
src/pages/api/[...all].ts

@@ -6,7 +6,8 @@ export const config = {
     },
 };
 
-const API_ENDPOINT = process.env.NEXT_PUBLIC_ENTE_ENDPOINT || 'https://api.staging.ente.io';
+const API_ENDPOINT =
+    process.env.NEXT_PUBLIC_ENTE_ENDPOINT || 'https://api.staging.ente.io';
 
 export default createProxyMiddleware({
     target: API_ENDPOINT,

+ 38 - 38
src/pages/change-email/index.tsx

@@ -9,15 +9,14 @@ import EnteSpinner from 'components/EnteSpinner';
 import ChangeEmailForm from 'components/ChangeEmail';
 import EnteCard from 'components/EnteCard';
 
-
 function ChangeEmailPage() {
-    const [email, setEmail]=useState('');
-    const [waiting, setWaiting]=useState(true);
-    const [showMessage, setShowMessage]=useState(false);
-    const [showBigDialog, setShowBigDialog]=useState(false);
+    const [email, setEmail] = useState('');
+    const [waiting, setWaiting] = useState(true);
+    const [showMessage, setShowMessage] = useState(false);
+    const [showBigDialog, setShowBigDialog] = useState(false);
 
     useEffect(() => {
-        const token=getToken();
+        const token = getToken();
         if (!token) {
             router.push('/');
             return;
@@ -25,39 +24,40 @@ function ChangeEmailPage() {
         setWaiting(false);
     }, []);
 
-
     return (
-        <Container>{waiting
-            ? <EnteSpinner>
-                <span className="sr-only">Loading...</span>
-            </EnteSpinner>
-            :<EnteCard size={showBigDialog?'md':'sm'}>
-                <Card.Body style={{ padding: '40px 30px' }}>
-                    <Card.Title style={{ marginBottom: '32px' }}>
-                        <LogoImg src="/icon.svg" />
-                        {constants.UPDATE_EMAIL}
-                    </Card.Title>
-                    <Alert
-                        variant="success"
-                        show={showMessage}
-                        style={{ paddingBottom: 0 }}
-                        transition
-                        dismissible
-                        onClose={()=>setShowMessage(false)}
-                    >
-                        {constants.EMAIL_SENT({ email })}
-                    </Alert>
-                    <ChangeEmailForm
-                        showMessage={(value)=>{
-                            setShowMessage(value);
-                            setShowBigDialog(value);
-                        }}
-                        setEmail={setEmail}
-                    />
-                </Card.Body>
-            </EnteCard>
-        }
-        </Container>);
+        <Container>
+            {waiting ? (
+                <EnteSpinner>
+                    <span className="sr-only">Loading...</span>
+                </EnteSpinner>
+            ) : (
+                <EnteCard size={showBigDialog ? 'md' : 'sm'}>
+                    <Card.Body style={{ padding: '40px 30px' }}>
+                        <Card.Title style={{ marginBottom: '32px' }}>
+                            <LogoImg src="/icon.svg" />
+                            {constants.UPDATE_EMAIL}
+                        </Card.Title>
+                        <Alert
+                            variant="success"
+                            show={showMessage}
+                            style={{ paddingBottom: 0 }}
+                            transition
+                            dismissible
+                            onClose={() => setShowMessage(false)}>
+                            {constants.EMAIL_SENT({ email })}
+                        </Alert>
+                        <ChangeEmailForm
+                            showMessage={(value) => {
+                                setShowMessage(value);
+                                setShowBigDialog(value);
+                            }}
+                            setEmail={setEmail}
+                        />
+                    </Card.Body>
+                </EnteCard>
+            )}
+        </Container>
+    );
 }
 
 export default ChangeEmailPage;

+ 3 - 2
src/pages/change-password/index.tsx

@@ -45,7 +45,8 @@ export default function Generate() {
             setFieldError('confirm', constants.PASSWORD_GENERATION_FAILED);
             return;
         }
-        const encryptedKeyAttributes: B64EncryptionResult = await cryptoWorker.encryptToB64(key, kek.key);
+        const encryptedKeyAttributes: B64EncryptionResult =
+            await cryptoWorker.encryptToB64(key, kek.key);
         const updatedKey: UpdatedKey = {
             kekSalt,
             encryptedKey: encryptedKeyAttributes.encryptedData,
@@ -60,7 +61,7 @@ export default function Generate() {
         await generateAndSaveIntermediateKeyAttributes(
             passphrase,
             updatedKeyAttributes,
-            key,
+            key
         );
 
         setSessionKeys(key);

+ 12 - 14
src/pages/credentials/index.tsx

@@ -29,7 +29,10 @@ export default function Credentials() {
         const user = getData(LS_KEYS.USER);
         const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
         const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
-        if ((!user?.token && !user?.encryptedToken) || !keyAttributes?.memLimit) {
+        if (
+            (!user?.token && !user?.encryptedToken) ||
+            !keyAttributes?.memLimit
+        ) {
             clearData();
             router.push('/');
         } else if (!keyAttributes) {
@@ -51,7 +54,7 @@ export default function Credentials() {
                     passphrase,
                     keyAttributes.kekSalt,
                     keyAttributes.opsLimit,
-                    keyAttributes.memLimit,
+                    keyAttributes.memLimit
                 );
             } catch (e) {
                 console.error('failed to deriveKey ', e.message);
@@ -61,13 +64,13 @@ export default function Credentials() {
                 const key: string = await cryptoWorker.decryptB64(
                     keyAttributes.encryptedKey,
                     keyAttributes.keyDecryptionNonce,
-                    kek,
+                    kek
                 );
                 if (isFirstLogin()) {
                     await generateAndSaveIntermediateKeyAttributes(
                         passphrase,
                         keyAttributes,
-                        key,
+                        key
                     );
                 }
                 await setSessionKeys(key);
@@ -81,7 +84,7 @@ export default function Credentials() {
         } catch (e) {
             setFieldError(
                 'passphrase',
-                `${constants.UNKNOWN_ERROR} ${e.message}`,
+                `${constants.UNKNOWN_ERROR} ${e.message}`
             );
             console.error('failed to verifyPassphrase ', e.message);
         }
@@ -90,13 +93,10 @@ export default function Credentials() {
     return (
         <>
             <Container>
-                <Card
-                    style={{ minWidth: '320px' }}
-                    className="text-center"
-                >
+                <Card style={{ minWidth: '320px' }} className="text-center">
                     <Card.Body style={{ padding: '40px 30px' }}>
                         <Card.Title style={{ marginBottom: '32px' }}>
-                            <LogoImg src='/icon.svg' />
+                            <LogoImg src="/icon.svg" />
                             {constants.PASSWORD}
                         </Card.Title>
                         <SingleInputForm
@@ -110,12 +110,10 @@ export default function Credentials() {
                                 display: 'flex',
                                 flexDirection: 'column',
                                 marginTop: '12px',
-                            }}
-                        >
+                            }}>
                             <Button
                                 variant="link"
-                                onClick={() => router.push('/recover')}
-                            >
+                                onClick={() => router.push('/recover')}>
                                 {constants.FORGOT_PASSWORD}
                             </Button>
                             <Button variant="link" onClick={logoutUser}>

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

@@ -113,7 +113,7 @@ const defaultGalleryContext: GalleryContextType = {
 };
 
 export const GalleryContext = createContext<GalleryContextType>(
-    defaultGalleryContext,
+    defaultGalleryContext
 );
 
 export default function Gallery() {
@@ -277,7 +277,7 @@ export default function Gallery() {
     }
     const addToCollectionHelper = (
         collectionName: string,
-        collection: Collection,
+        collection: Collection
     ) => {
         loadingBar.current?.continuousStart();
         addFilesToCollection(
@@ -288,7 +288,7 @@ export default function Gallery() {
             syncWithRemote,
             selectCollection,
             collectionName,
-            collection,
+            collection
         );
     };
 
@@ -348,7 +348,7 @@ export default function Gallery() {
                 getInputProps={getInputProps}
                 showCollectionSelector={setCollectionSelectorView.bind(
                     null,
-                    true,
+                    true
                 )}>
                 {loading && (
                     <LoadingOverlay>
@@ -420,14 +420,14 @@ export default function Gallery() {
                     acceptedFiles={acceptedFiles}
                     showCollectionSelector={setCollectionSelectorView.bind(
                         null,
-                        true,
+                        true
                     )}
                     setCollectionSelectorAttributes={
                         setCollectionSelectorAttributes
                     }
                     closeCollectionSelector={setCollectionSelectorView.bind(
                         null,
-                        false,
+                        false
                     )}
                     setLoading={setLoading}
                     setCollectionNamerAttributes={setCollectionNamerAttributes}

+ 3 - 3
src/pages/generate/index.tsx

@@ -35,7 +35,7 @@ export default function Generate() {
             setLoading(true);
             const key = getKey(SESSION_KEYS.ENCRYPTION_KEY);
             const keyAttributes: KeyAttributes = getData(
-                LS_KEYS.ORIGINAL_KEY_ATTRIBUTES,
+                LS_KEYS.ORIGINAL_KEY_ATTRIBUTES
             );
             router.prefetch('/gallery');
             const user = getData(LS_KEYS.USER);
@@ -64,14 +64,14 @@ export default function Generate() {
     const onSubmit = async (passphrase, setFieldError) => {
         try {
             const { keyAttributes, masterKey } = await generateKeyAttributes(
-                passphrase,
+                passphrase
             );
 
             await putAttributes(token, keyAttributes);
             await generateAndSaveIntermediateKeyAttributes(
                 passphrase,
                 keyAttributes,
-                masterKey,
+                masterKey
             );
             await setSessionKeys(masterKey);
             setJustSignedUp(true);

+ 74 - 57
src/pages/index.tsx

@@ -20,7 +20,7 @@ const Container = styled.div`
     justify-content: center;
     background-color: #000;
 
-    @media(max-width: 1024px) {
+    @media (max-width: 1024px) {
         flex-direction: column;
     }
 `;
@@ -33,7 +33,7 @@ const SlideContainer = styled.div`
     justify-content: center;
     text-align: center;
 
-    @media(max-width: 1024px) {
+    @media (max-width: 1024px) {
         flex-grow: 0;
     }
 `;
@@ -47,7 +47,7 @@ const DesktopBox = styled.div`
     justify-content: center;
     background-color: #242424;
 
-    @media(max-width: 1024px) {
+    @media (max-width: 1024px) {
         display: none;
     }
 `;
@@ -55,7 +55,7 @@ const DesktopBox = styled.div`
 const MobileBox = styled.div`
     display: none;
 
-    @media(max-width: 1024px) {
+    @media (max-width: 1024px) {
         display: flex;
         flex-direction: column;
         padding: 40px 10px;
@@ -91,7 +91,7 @@ const Img = styled.img`
     height: 250px;
     object-fit: contain;
 
-    @media(max-width: 400px) {
+    @media (max-width: 400px) {
         height: 180px;
     }
 `;
@@ -123,56 +123,73 @@ export default function LandingPage() {
     const signUp = () => setShowLogin(false);
     const login = () => setShowLogin(true);
 
-    return <Container>
-        {loading ? <EnteSpinner />
-            : (<>
-                <SlideContainer>
-                    <UpperText>
-                        {constants.HERO_HEADER()}
-                    </UpperText>
-                    <Carousel controls={false}>
-                        <Carousel.Item>
-                            <Img src="/images/slide-1.png" />
-                            <FeatureText>{constants.HERO_SLIDE_1_TITLE}</FeatureText>
-                            <TextContainer>{constants.HERO_SLIDE_1}</TextContainer>
-                        </Carousel.Item>
-                        <Carousel.Item>
-                            <Img src="/images/slide-2.png" />
-                            <FeatureText>{constants.HERO_SLIDE_2_TITLE}</FeatureText>
-                            <TextContainer>{constants.HERO_SLIDE_2}</TextContainer>
-                        </Carousel.Item>
-                        <Carousel.Item>
-                            <Img src="/images/slide-3.png" />
-                            <FeatureText>{constants.HERO_SLIDE_3_TITLE}</FeatureText>
-                            <TextContainer>{constants.HERO_SLIDE_3}</TextContainer>
-                        </Carousel.Item>
-                    </Carousel>
-                </SlideContainer>
-                <MobileBox>
-                    <Button
-                        variant="outline-success"
-                        size="lg"
-                        style={{ color: '#fff', padding: '10px 50px' }}
-                        onClick={() => router.push('signup')}
-                    >
-                        {constants.SIGN_UP}
-                    </Button>
-                    <br />
-                    <Button
-                        variant="link"
-                        size="lg"
-                        style={{ color: '#fff', padding: '10px 50px' }}
-                        onClick={() => router.push('login')}
-                    >
-                        {constants.SIGN_IN}
-                    </Button>
-                </MobileBox>
-                <DesktopBox>
-                    <SideBox>
-                        {showLogin ? <Login signUp={signUp} /> : <SignUp login={login} />}
-                    </SideBox>
-                </DesktopBox>
-                {blockUsage && <IncognitoWarning />}
-            </>)}
-    </Container>;
+    return (
+        <Container>
+            {loading ? (
+                <EnteSpinner />
+            ) : (
+                <>
+                    <SlideContainer>
+                        <UpperText>{constants.HERO_HEADER()}</UpperText>
+                        <Carousel controls={false}>
+                            <Carousel.Item>
+                                <Img src="/images/slide-1.png" />
+                                <FeatureText>
+                                    {constants.HERO_SLIDE_1_TITLE}
+                                </FeatureText>
+                                <TextContainer>
+                                    {constants.HERO_SLIDE_1}
+                                </TextContainer>
+                            </Carousel.Item>
+                            <Carousel.Item>
+                                <Img src="/images/slide-2.png" />
+                                <FeatureText>
+                                    {constants.HERO_SLIDE_2_TITLE}
+                                </FeatureText>
+                                <TextContainer>
+                                    {constants.HERO_SLIDE_2}
+                                </TextContainer>
+                            </Carousel.Item>
+                            <Carousel.Item>
+                                <Img src="/images/slide-3.png" />
+                                <FeatureText>
+                                    {constants.HERO_SLIDE_3_TITLE}
+                                </FeatureText>
+                                <TextContainer>
+                                    {constants.HERO_SLIDE_3}
+                                </TextContainer>
+                            </Carousel.Item>
+                        </Carousel>
+                    </SlideContainer>
+                    <MobileBox>
+                        <Button
+                            variant="outline-success"
+                            size="lg"
+                            style={{ color: '#fff', padding: '10px 50px' }}
+                            onClick={() => router.push('signup')}>
+                            {constants.SIGN_UP}
+                        </Button>
+                        <br />
+                        <Button
+                            variant="link"
+                            size="lg"
+                            style={{ color: '#fff', padding: '10px 50px' }}
+                            onClick={() => router.push('login')}>
+                            {constants.SIGN_IN}
+                        </Button>
+                    </MobileBox>
+                    <DesktopBox>
+                        <SideBox>
+                            {showLogin ? (
+                                <Login signUp={signUp} />
+                            ) : (
+                                <SignUp login={login} />
+                            )}
+                        </SideBox>
+                    </DesktopBox>
+                    {blockUsage && <IncognitoWarning />}
+                </>
+            )}
+        </Container>
+    );
 }

+ 15 - 10
src/pages/login/index.tsx

@@ -27,14 +27,19 @@ export default function Home() {
         router.push('/signup');
     };
 
-    return <Container>{loading
-        ? <EnteSpinner>
-            <span className="sr-only">Loading...</span>
-        </EnteSpinner>
-        :<Card style={{ minWidth: '320px' }} className="text-center">
-            <Card.Body style={{ padding: '40px 30px' }}>
-                <Login signUp={register}/>
-            </Card.Body>
-        </Card>}
-    </Container>;
+    return (
+        <Container>
+            {loading ? (
+                <EnteSpinner>
+                    <span className="sr-only">Loading...</span>
+                </EnteSpinner>
+            ) : (
+                <Card style={{ minWidth: '320px' }} className="text-center">
+                    <Card.Body style={{ padding: '40px 30px' }}>
+                        <Login signUp={register} />
+                    </Card.Body>
+                </Card>
+            )}
+        </Container>
+    );
 }

+ 5 - 10
src/pages/recover/index.tsx

@@ -38,7 +38,7 @@ export default function Recover() {
             const masterKey: string = await cryptoWorker.decryptB64(
                 keyAttributes.masterKeyEncryptedWithRecoveryKey,
                 keyAttributes.masterKeyDecryptionNonce,
-                await cryptoWorker.fromHex(recoveryKey),
+                await cryptoWorker.fromHex(recoveryKey)
             );
             setSessionKeys(masterKey);
             router.push('/changePassword');
@@ -51,13 +51,10 @@ export default function Recover() {
     return (
         <>
             <Container>
-                <Card
-                    style={{ minWidth: '320px' }}
-                    className="text-center"
-                >
+                <Card style={{ minWidth: '320px' }} className="text-center">
                     <Card.Body style={{ padding: '40px 30px' }}>
                         <Card.Title style={{ marginBottom: '32px' }}>
-                            <LogoImg src='/icon.svg' />
+                            <LogoImg src="/icon.svg" />
                             {constants.RECOVER_ACCOUNT}
                         </Card.Title>
                         <SingleInputForm
@@ -71,12 +68,10 @@ export default function Recover() {
                                 display: 'flex',
                                 flexDirection: 'column',
                                 marginTop: '12px',
-                            }}
-                        >
+                            }}>
                             <Button
                                 variant="link"
-                                onClick={() => SetMessageDialogView(true)}
-                            >
+                                onClick={() => SetMessageDialogView(true)}>
                                 {constants.NO_RECOVERY_KEY}
                             </Button>
                             <Button variant="link" onClick={router.back}>

+ 6 - 5
src/pages/signup/index.tsx

@@ -7,7 +7,6 @@ import EnteSpinner from 'components/EnteSpinner';
 import { getData, LS_KEYS } from 'utils/storage/localStorage';
 import SignUp from 'components/SignUp';
 
-
 export default function SignUpPage() {
     const router = useRouter();
     const appContext = useContext(AppContext);
@@ -29,14 +28,16 @@ export default function SignUpPage() {
     };
 
     return (
-        <Container>{
-            loading ? <EnteSpinner />
-                : <Card style={{ minWidth: '320px' }} className="text-center">
+        <Container>
+            {loading ? (
+                <EnteSpinner />
+            ) : (
+                <Card style={{ minWidth: '320px' }} className="text-center">
                     <Card.Body style={{ padding: '40px 30px' }}>
                         <SignUp login={login} />
                     </Card.Body>
                 </Card>
-        }
+            )}
         </Container>
     );
 }

+ 7 - 11
src/pages/two-factor/recover/index.tsx

@@ -15,7 +15,8 @@ import { recoverTwoFactor, removeTwoFactor } from 'services/userService';
 export default function Recover() {
     const router = useRouter();
     const [messageDialogView, SetMessageDialogView] = useState(false);
-    const [encryptedTwoFactorSecret, setEncryptedTwoFactorSecret] = useState<B64EncryptionResult>(null);
+    const [encryptedTwoFactorSecret, setEncryptedTwoFactorSecret] =
+        useState<B64EncryptionResult>(null);
     const [sessionID, setSessionID] = useState(null);
     useEffect(() => {
         router.prefetch('/gallery');
@@ -41,7 +42,7 @@ export default function Recover() {
             const twoFactorSecret: string = await cryptoWorker.decryptB64(
                 encryptedTwoFactorSecret.encryptedData,
                 encryptedTwoFactorSecret.nonce,
-                await cryptoWorker.fromHex(recoveryKey),
+                await cryptoWorker.fromHex(recoveryKey)
             );
             const resp = await removeTwoFactor(sessionID, twoFactorSecret);
             const { keyAttributes, encryptedToken, token, id } = resp;
@@ -63,13 +64,10 @@ export default function Recover() {
     return (
         <>
             <Container>
-                <Card
-                    style={{ minWidth: '320px' }}
-                    className="text-center"
-                >
+                <Card style={{ minWidth: '320px' }} className="text-center">
                     <Card.Body style={{ padding: '40px 30px' }}>
                         <Card.Title style={{ marginBottom: '32px' }}>
-                            <LogoImg src='/icon.svg' />
+                            <LogoImg src="/icon.svg" />
                             {constants.RECOVER_TWO_FACTOR}
                         </Card.Title>
                         <SingleInputForm
@@ -83,12 +81,10 @@ export default function Recover() {
                                 display: 'flex',
                                 flexDirection: 'column',
                                 marginTop: '12px',
-                            }}
-                        >
+                            }}>
                             <Button
                                 variant="link"
-                                onClick={() => SetMessageDialogView(true)}
-                            >
+                                onClick={() => SetMessageDialogView(true)}>
                                 {constants.NO_RECOVERY_KEY}
                             </Button>
                             <Button variant="link" onClick={router.back}>

+ 92 - 34
src/pages/two-factor/setup/index.tsx

@@ -4,7 +4,11 @@ import { CodeBlock, FreeFlowText } from 'components/RecoveryKeyModal';
 import { DeadCenter } from 'pages/gallery';
 import React, { useContext, useEffect, useState } from 'react';
 import { Button, Card } from 'react-bootstrap';
-import { enableTwoFactor, setupTwoFactor, TwoFactorSecret } from 'services/userService';
+import {
+    enableTwoFactor,
+    setupTwoFactor,
+    TwoFactorSecret,
+} from 'services/userService';
 import styled from 'styled-components';
 import constants from 'utils/strings/constants';
 import Container from 'components/Container';
@@ -15,22 +19,25 @@ import { encryptWithRecoveryKey } from 'utils/crypto';
 import { setData, LS_KEYS, getData } from 'utils/storage/localStorage';
 import { AppContext } from 'pages/_app';
 
-
 enum SetupMode {
     QR_CODE,
     MANUAL_CODE,
 }
 
 const QRCode = styled.img`
-    height:200px;
-    width:200px;
-    margin:1rem;
+    height: 200px;
+    width: 200px;
+    margin: 1rem;
 `;
 
 export default function SetupTwoFactor() {
     const [setupMode, setSetupMode] = useState<SetupMode>(SetupMode.QR_CODE);
-    const [twoFactorSecret, setTwoFactorSecret] = useState<TwoFactorSecret>(null);
-    const [recoveryEncryptedTwoFactorSecret, setRecoveryEncryptedTwoFactorSecret] = useState<B64EncryptionResult>(null);
+    const [twoFactorSecret, setTwoFactorSecret] =
+        useState<TwoFactorSecret>(null);
+    const [
+        recoveryEncryptedTwoFactorSecret,
+        setRecoveryEncryptedTwoFactorSecret,
+    ] = useState<B64EncryptionResult>(null);
     const router = useRouter();
     const appContext = useContext(AppContext);
     useEffect(() => {
@@ -40,11 +47,17 @@ export default function SetupTwoFactor() {
         const main = async () => {
             try {
                 const twoFactorSecret = await setupTwoFactor();
-                const recoveryEncryptedTwoFactorSecret = await encryptWithRecoveryKey(twoFactorSecret.secretCode);
+                const recoveryEncryptedTwoFactorSecret =
+                    await encryptWithRecoveryKey(twoFactorSecret.secretCode);
                 setTwoFactorSecret(twoFactorSecret);
-                setRecoveryEncryptedTwoFactorSecret(recoveryEncryptedTwoFactorSecret);
+                setRecoveryEncryptedTwoFactorSecret(
+                    recoveryEncryptedTwoFactorSecret
+                );
             } catch (e) {
-                appContext.setDisappearingFlashMessage({ message: constants.TWO_FACTOR_SETUP_FAILED, severity: 'danger' });
+                appContext.setDisappearingFlashMessage({
+                    message: constants.TWO_FACTOR_SETUP_FAILED,
+                    severity: 'danger',
+                });
                 router.push('/gallery');
             }
         };
@@ -52,8 +65,14 @@ export default function SetupTwoFactor() {
     }, []);
     const onSubmit = async (otp: string) => {
         await enableTwoFactor(otp, recoveryEncryptedTwoFactorSecret);
-        setData(LS_KEYS.USER, { ...getData(LS_KEYS.USER), isTwoFactorEnabled: true });
-        appContext.setDisappearingFlashMessage({ message: constants.TWO_FACTOR_SETUP_SUCCESS, severity: 'info' });
+        setData(LS_KEYS.USER, {
+            ...getData(LS_KEYS.USER),
+            isTwoFactorEnabled: true,
+        });
+        appContext.setDisappearingFlashMessage({
+            message: constants.TWO_FACTOR_SETUP_SUCCESS,
+            severity: 'info',
+        });
         router.push('/gallery');
     };
     return (
@@ -62,35 +81,67 @@ export default function SetupTwoFactor() {
                 <Card.Body style={{ padding: '40px 30px', minHeight: '400px' }}>
                     <DeadCenter>
                         <Card.Title style={{ marginBottom: '32px' }}>
-                            <LogoImg src='/icon.svg' />
+                            <LogoImg src="/icon.svg" />
                             {constants.TWO_FACTOR}
                         </Card.Title>
                         {setupMode === SetupMode.QR_CODE ? (
                             <>
                                 <p>{constants.TWO_FACTOR_QR_INSTRUCTION}</p>
                                 <DeadCenter>
-                                    {!twoFactorSecret ? <div style={{ height: '200px', width: '200px', margin: '1rem', display: 'flex', justifyContent: 'center', alignItems: 'center', border: '1px solid #aaa' }}><EnteSpinner /></div>
-                                        : <QRCode src={`data:image/png;base64,${twoFactorSecret.qrCode}`} />
-                                    }
-                                    <Button block variant="link" onClick={() => setSetupMode(SetupMode.MANUAL_CODE)}>
+                                    {!twoFactorSecret ? (
+                                        <div
+                                            style={{
+                                                height: '200px',
+                                                width: '200px',
+                                                margin: '1rem',
+                                                display: 'flex',
+                                                justifyContent: 'center',
+                                                alignItems: 'center',
+                                                border: '1px solid #aaa',
+                                            }}>
+                                            <EnteSpinner />
+                                        </div>
+                                    ) : (
+                                        <QRCode
+                                            src={`data:image/png;base64,${twoFactorSecret.qrCode}`}
+                                        />
+                                    )}
+                                    <Button
+                                        block
+                                        variant="link"
+                                        onClick={() =>
+                                            setSetupMode(SetupMode.MANUAL_CODE)
+                                        }>
                                         {constants.ENTER_CODE_MANUALLY}
                                     </Button>
                                 </DeadCenter>
                             </>
-                        ) : (<>
-                            <p>{constants.TWO_FACTOR_MANUAL_CODE_INSTRUCTION}</p>
-                            <CodeBlock height={100}>
-                                {!twoFactorSecret ? <EnteSpinner /> : (
-                                    <FreeFlowText>
-                                        {twoFactorSecret.secretCode}
-                                    </FreeFlowText>
-
-                                )}
-                            </CodeBlock>
-                            <Button block variant="link" style={{ marginBottom: '1rem' }} onClick={() => setSetupMode(SetupMode.QR_CODE)}>
-                                {constants.SCAN_QR_CODE}
-                            </Button>
-                        </>
+                        ) : (
+                            <>
+                                <p>
+                                    {
+                                        constants.TWO_FACTOR_MANUAL_CODE_INSTRUCTION
+                                    }
+                                </p>
+                                <CodeBlock height={100}>
+                                    {!twoFactorSecret ? (
+                                        <EnteSpinner />
+                                    ) : (
+                                        <FreeFlowText>
+                                            {twoFactorSecret.secretCode}
+                                        </FreeFlowText>
+                                    )}
+                                </CodeBlock>
+                                <Button
+                                    block
+                                    variant="link"
+                                    style={{ marginBottom: '1rem' }}
+                                    onClick={() =>
+                                        setSetupMode(SetupMode.QR_CODE)
+                                    }>
+                                    {constants.SCAN_QR_CODE}
+                                </Button>
+                            </>
                         )}
                         <div
                             style={{
@@ -99,13 +150,20 @@ export default function SetupTwoFactor() {
                                 width: '100%',
                             }}
                         />
-                        <VerifyTwoFactor onSubmit={onSubmit} back={router.back} buttonText={constants.ENABLE} />
-                        <Button style={{ marginTop: '16px' }} variant="link-danger" onClick={router.back}>
+                        <VerifyTwoFactor
+                            onSubmit={onSubmit}
+                            back={router.back}
+                            buttonText={constants.ENABLE}
+                        />
+                        <Button
+                            style={{ marginTop: '16px' }}
+                            variant="link-danger"
+                            onClick={router.back}>
                             {constants.GO_BACK}
                         </Button>
                     </DeadCenter>
                 </Card.Body>
             </Card>
-        </Container >
+        </Container>
     );
 }

+ 8 - 6
src/pages/two-factor/verify/index.tsx

@@ -52,21 +52,23 @@ export default function Home() {
             <Card style={{ minWidth: '300px' }} className="text-center">
                 <Card.Body style={{ padding: '40px 30px', minHeight: '400px' }}>
                     <Card.Title style={{ marginBottom: '32px' }}>
-                        <LogoImg src='/icon.svg' />
+                        <LogoImg src="/icon.svg" />
                         {constants.TWO_FACTOR}
                     </Card.Title>
-                    <VerifyTwoFactor onSubmit={onSubmit} back={router.back} buttonText={constants.VERIFY} />
+                    <VerifyTwoFactor
+                        onSubmit={onSubmit}
+                        back={router.back}
+                        buttonText={constants.VERIFY}
+                    />
                     <div
                         style={{
                             display: 'flex',
                             flexDirection: 'column',
                             marginTop: '12px',
-                        }}
-                    >
+                        }}>
                         <Button
                             variant="link"
-                            onClick={() => router.push('/two-factor/recover')}
-                        >
+                            onClick={() => router.push('/two-factor/recover')}>
                             {constants.LOST_DEVICE}
                         </Button>
                         <Button variant="link" onClick={logoutUser}>

+ 16 - 7
src/pages/verify/index.tsx

@@ -56,14 +56,24 @@ export default function Verify() {
 
     const onSubmit = async (
         { ott }: formValues,
-        { setFieldError }: FormikHelpers<formValues>,
+        { setFieldError }: FormikHelpers<formValues>
     ) => {
         try {
             setLoading(true);
             const resp = await verifyOtt(email, ott);
-            const { keyAttributes, encryptedToken, token, id, twoFactorSessionID } = resp.data as EmailVerificationResponse;
+            const {
+                keyAttributes,
+                encryptedToken,
+                token,
+                id,
+                twoFactorSessionID,
+            } = resp.data as EmailVerificationResponse;
             if (twoFactorSessionID) {
-                setData(LS_KEYS.USER, { email, twoFactorSessionID, isTwoFactorEnabled: true });
+                setData(LS_KEYS.USER, {
+                    email,
+                    twoFactorSessionID,
+                    isTwoFactorEnabled: true,
+                });
                 router.push('/two-factor/verify');
                 return;
             }
@@ -109,7 +119,7 @@ export default function Verify() {
             <Card style={{ minWidth: '300px' }} className="text-center">
                 <Card.Body style={{ padding: '40px 30px' }}>
                     <Card.Title style={{ marginBottom: '32px' }}>
-                        <LogoImg src='/icon.svg' />
+                        <LogoImg src="/icon.svg" />
                         {constants.VERIFY_EMAIL}
                     </Card.Title>
                     {constants.EMAIL_SENT({ email })}
@@ -123,8 +133,7 @@ export default function Verify() {
                         })}
                         validateOnChange={false}
                         validateOnBlur={false}
-                        onSubmit={onSubmit}
-                    >
+                        onSubmit={onSubmit}>
                         {({
                             values,
                             touched,
@@ -140,7 +149,7 @@ export default function Verify() {
                                         value={values.ott}
                                         onChange={handleChange('ott')}
                                         isInvalid={Boolean(
-                                            touched.ott && errors.ott,
+                                            touched.ott && errors.ott
                                         )}
                                         placeholder={constants.ENTER_OTT}
                                         disabled={loading}

+ 19 - 11
src/serviceWorker.js

@@ -8,17 +8,25 @@ pageCache();
 precacheAndRoute(self.__WB_MANIFEST);
 cleanupOutdatedCaches();
 
-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');
+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());

+ 12 - 10
src/services/HTTPService.ts

@@ -21,7 +21,7 @@ class HTTPService {
                 }
                 const { response } = err;
                 return Promise.reject(response);
-            },
+            }
         );
     }
 
@@ -77,7 +77,9 @@ class HTTPService {
             ...config.headers,
         };
         if (customConfig?.cancel) {
-            config.cancelToken=new axios.CancelToken((c)=> (customConfig.cancel.exec=c));
+            config.cancelToken = new axios.CancelToken(
+                (c) => (customConfig.cancel.exec = c)
+            );
         }
         return await axios({ ...config, ...customConfig });
     }
@@ -89,7 +91,7 @@ class HTTPService {
         url: string,
         params?: IQueryPrams,
         headers?: IHTTPHeaders,
-        customConfig?: any,
+        customConfig?: any
     ) {
         return this.request(
             {
@@ -98,7 +100,7 @@ class HTTPService {
                 params,
                 url,
             },
-            customConfig,
+            customConfig
         );
     }
 
@@ -110,7 +112,7 @@ class HTTPService {
         data?: any,
         params?: IQueryPrams,
         headers?: IHTTPHeaders,
-        customConfig?: any,
+        customConfig?: any
     ) {
         return this.request(
             {
@@ -120,7 +122,7 @@ class HTTPService {
                 params,
                 url,
             },
-            customConfig,
+            customConfig
         );
     }
 
@@ -132,7 +134,7 @@ class HTTPService {
         data: any,
         params?: IQueryPrams,
         headers?: IHTTPHeaders,
-        customConfig?: any,
+        customConfig?: any
     ) {
         return this.request(
             {
@@ -142,7 +144,7 @@ class HTTPService {
                 params,
                 url,
             },
-            customConfig,
+            customConfig
         );
     }
 
@@ -154,7 +156,7 @@ class HTTPService {
         data: any,
         params?: IQueryPrams,
         headers?: IHTTPHeaders,
-        customConfig?: any,
+        customConfig?: any
     ) {
         return this.request(
             {
@@ -164,7 +166,7 @@ class HTTPService {
                 params,
                 url,
             },
-            customConfig,
+            customConfig
         );
     }
 }

+ 18 - 16
src/services/billingService.ts

@@ -26,8 +26,8 @@ export interface Subscription {
     attributes: {
         isCancelled: boolean;
     };
-    price:string;
-    period:string;
+    price: string;
+    period: string;
 }
 export interface Plan {
     id: string;
@@ -64,9 +64,11 @@ class billingService {
         }
     }
 
-    public async getPlans():Promise<Plan[]> {
+    public async getPlans(): Promise<Plan[]> {
         try {
-            const response = await HTTPService.get(`${ENDPOINT}/billing/plans/v2`);
+            const response = await HTTPService.get(
+                `${ENDPOINT}/billing/plans/v2`
+            );
             const { plans } = response.data;
             return plans;
         } catch (e) {
@@ -81,12 +83,12 @@ class billingService {
                 null,
                 {
                     'X-Auth-Token': getToken(),
-                },
+                }
             );
             const { subscription } = response.data;
             setData(LS_KEYS.SUBSCRIPTION, subscription);
         } catch (e) {
-            logError(e, 'failed to get user\'s subscription details');
+            logError(e, "failed to get user's subscription details");
         }
     }
 
@@ -112,7 +114,7 @@ class billingService {
                 null,
                 {
                     'X-Auth-Token': getToken(),
-                },
+                }
             );
             const { result } = response.data;
             switch (result.status) {
@@ -122,12 +124,12 @@ class billingService {
                     break;
                 case PAYMENT_INTENT_STATUS.REQUIRE_PAYMENT_METHOD:
                     throw new Error(
-                        PAYMENT_INTENT_STATUS.REQUIRE_PAYMENT_METHOD,
+                        PAYMENT_INTENT_STATUS.REQUIRE_PAYMENT_METHOD
                     );
                 case PAYMENT_INTENT_STATUS.REQUIRE_ACTION:
                     {
                         const { error } = await this.stripe.confirmCardPayment(
-                            result.clientSecret,
+                            result.clientSecret
                         );
                         if (error) {
                             throw error;
@@ -154,7 +156,7 @@ class billingService {
                 null,
                 {
                     'X-Auth-Token': getToken(),
-                },
+                }
             );
             const { subscription } = response.data;
             setData(LS_KEYS.SUBSCRIPTION, subscription);
@@ -172,7 +174,7 @@ class billingService {
                 null,
                 {
                     'X-Auth-Token': getToken(),
-                },
+                }
             );
             const { subscription } = response.data;
             setData(LS_KEYS.SUBSCRIPTION, subscription);
@@ -190,12 +192,12 @@ class billingService {
             },
             {
                 'X-Auth-Token': getToken(),
-            },
+            }
         );
     }
 
     public async verifySubscription(
-        sessionID: string = null,
+        sessionID: string = null
     ): Promise<Subscription> {
         try {
             const response = await HTTPService.post(
@@ -208,7 +210,7 @@ class billingService {
                 null,
                 {
                     'X-Auth-Token': getToken(),
-                },
+                }
             );
             const { subscription } = response.data;
             setData(LS_KEYS.SUBSCRIPTION, subscription);
@@ -226,7 +228,7 @@ class billingService {
                 null,
                 {
                     'X-Auth-Token': getToken(),
-                },
+                }
             );
             window.location.href = response.data.url;
         } catch (e) {
@@ -242,7 +244,7 @@ class billingService {
                 { startTime: 0, endTime: Date.now() * 1000 },
                 {
                     'X-Auth-Token': getToken(),
-                },
+                }
             );
             return convertToHumanReadable(response.data.usage);
         } catch (e) {

+ 53 - 45
src/services/collectionService.ts

@@ -52,7 +52,7 @@ export interface CollectionAndItsLatestFile {
 
 const getCollectionWithSecrets = async (
     collection: Collection,
-    masterKey: string,
+    masterKey: string
 ) => {
     const worker = await new CryptoWorker();
     const userID = getData(LS_KEYS.USER).id;
@@ -61,26 +61,27 @@ const getCollectionWithSecrets = async (
         decryptedKey = await worker.decryptB64(
             collection.encryptedKey,
             collection.keyDecryptionNonce,
-            masterKey,
+            masterKey
         );
     } else {
         const keyAttributes = getData(LS_KEYS.KEY_ATTRIBUTES);
         const secretKey = await worker.decryptB64(
             keyAttributes.encryptedSecretKey,
             keyAttributes.secretKeyDecryptionNonce,
-            masterKey,
+            masterKey
         );
         decryptedKey = await worker.boxSealOpen(
             collection.encryptedKey,
             keyAttributes.publicKey,
-            secretKey,
+            secretKey
         );
     }
-    collection.name = collection.name ||
+    collection.name =
+        collection.name ||
         (await worker.decryptToUTF8(
             collection.encryptedName,
             collection.nameDecryptionNonce,
-            decryptedKey,
+            decryptedKey
         ));
     return {
         ...collection,
@@ -91,7 +92,7 @@ const getCollectionWithSecrets = async (
 const getCollections = async (
     token: string,
     sinceTime: number,
-    key: string,
+    key: string
 ): Promise<Collection[]> => {
     try {
         const resp = await HTTPService.get(
@@ -99,7 +100,7 @@ const getCollections = async (
             {
                 sinceTime,
             },
-            { 'X-Auth-Token': token },
+            { 'X-Auth-Token': token }
         );
         const promises: Promise<Collection>[] = resp.data.collections.map(
             async (collection: Collection) => {
@@ -110,16 +111,16 @@ const getCollections = async (
                 try {
                     collectionWithSecrets = await getCollectionWithSecrets(
                         collection,
-                        key,
+                        key
                     );
                     return collectionWithSecrets;
                 } catch (e) {
                     logError(
                         e,
-                        `decryption failed for collection with id=${collection.id}`,
+                        `decryption failed for collection with id=${collection.id}`
                     );
                 }
-            },
+            }
         );
         return await Promise.all(promises);
     } catch (e) {
@@ -129,18 +130,21 @@ const getCollections = async (
 };
 
 export const getLocalCollections = async (): Promise<Collection[]> => {
-    const collections: Collection[] = (await localForage.getItem(COLLECTIONS)) ?? [];
+    const collections: Collection[] =
+        (await localForage.getItem(COLLECTIONS)) ?? [];
     return collections;
 };
 
-export const getCollectionUpdationTime = async (): Promise<number> => (await localForage.getItem<number>(COLLECTION_UPDATION_TIME)) ?? 0;
+export const getCollectionUpdationTime = async (): Promise<number> =>
+    (await localForage.getItem<number>(COLLECTION_UPDATION_TIME)) ?? 0;
 
 export const syncCollections = async () => {
     const localCollections = await getLocalCollections();
     const lastCollectionUpdationTime = await getCollectionUpdationTime();
     const token = getToken();
     const key = await getActualKey();
-    const updatedCollections = (await getCollections(token, lastCollectionUpdationTime, key)) ?? [];
+    const updatedCollections =
+        (await getCollections(token, lastCollectionUpdationTime, key)) ?? [];
     if (updatedCollections.length === 0) {
         return localCollections;
     }
@@ -153,7 +157,7 @@ export const syncCollections = async () => {
         if (
             !latestCollectionsInstances.has(collection.id) ||
             latestCollectionsInstances.get(collection.id).updationTime <
-            collection.updationTime
+                collection.updationTime
         ) {
             latestCollectionsInstances.set(collection.id, collection);
         }
@@ -161,7 +165,7 @@ export const syncCollections = async () => {
 
     const collections: Collection[] = [];
     let updationTime = await localForage.getItem<number>(
-        COLLECTION_UPDATION_TIME,
+        COLLECTION_UPDATION_TIME
     );
     // eslint-disable-next-line @typescript-eslint/no-unused-vars
     for (const [_, collection] of latestCollectionsInstances) {
@@ -179,7 +183,7 @@ export const syncCollections = async () => {
 
 export const getCollectionsAndTheirLatestFile = (
     collections: Collection[],
-    files: File[],
+    files: File[]
 ): CollectionAndItsLatestFile[] => {
     const latestFile = new Map<number, File>();
 
@@ -213,15 +217,16 @@ export const getFavItemIds = async (files: File[]): Promise<Set<number>> => {
     return new Set(
         files
             .filter((file) => file.collectionID === favCollection.id)
-            .map((file): number => file.id),
+            .map((file): number => file.id)
     );
 };
 
-export const createAlbum = async (albumName: string) => createCollection(albumName, CollectionType.album);
+export const createAlbum = async (albumName: string) =>
+    createCollection(albumName, CollectionType.album);
 
 export const createCollection = async (
     collectionName: string,
-    type: CollectionType,
+    type: CollectionType
 ): Promise<Collection> => {
     try {
         const existingCollections = await syncCollections();
@@ -239,14 +244,14 @@ export const createCollection = async (
             nonce: keyDecryptionNonce,
         }: B64EncryptionResult = await worker.encryptToB64(
             collectionKey,
-            encryptionKey,
+            encryptionKey
         );
         const {
             encryptedData: encryptedName,
             nonce: nameDecryptionNonce,
         }: B64EncryptionResult = await worker.encryptUTF8(
             collectionName,
-            collectionKey,
+            collectionKey
         );
         const newCollection: Collection = {
             id: null,
@@ -263,11 +268,11 @@ export const createCollection = async (
         };
         let createdCollection: Collection = await postCollection(
             newCollection,
-            token,
+            token
         );
         createdCollection = await getCollectionWithSecrets(
             createdCollection,
-            encryptionKey,
+            encryptionKey
         );
         return createdCollection;
     } catch (e) {
@@ -278,14 +283,14 @@ export const createCollection = async (
 
 const postCollection = async (
     collectionData: Collection,
-    token: string,
+    token: string
 ): Promise<Collection> => {
     try {
         const response = await HTTPService.post(
             `${ENDPOINT}/collections`,
             collectionData,
             null,
-            { 'X-Auth-Token': token },
+            { 'X-Auth-Token': token }
         );
         return response.data.collection;
     } catch (e) {
@@ -298,7 +303,7 @@ export const addToFavorites = async (file: File) => {
     if (!favCollection) {
         favCollection = await createCollection(
             'Favorites',
-            CollectionType.favorites,
+            CollectionType.favorites
         );
         await localForage.setItem(FAV_COLLECTION, favCollection);
     }
@@ -312,7 +317,7 @@ export const removeFromFavorites = async (file: File) => {
 
 export const addToCollection = async (
     collection: Collection,
-    files: File[],
+    files: File[]
 ) => {
     try {
         const params = {};
@@ -322,7 +327,8 @@ export const addToCollection = async (
         await Promise.all(
             files.map(async (file) => {
                 file.collectionID = collection.id;
-                const newEncryptedKey: B64EncryptionResult = await worker.encryptToB64(file.key, collection.key);
+                const newEncryptedKey: B64EncryptionResult =
+                    await worker.encryptToB64(file.key, collection.key);
                 file.encryptedKey = newEncryptedKey.encryptedData;
                 file.keyDecryptionNonce = newEncryptedKey.nonce;
                 if (params['files'] === undefined) {
@@ -334,13 +340,13 @@ export const addToCollection = async (
                     keyDecryptionNonce: file.keyDecryptionNonce,
                 });
                 return file;
-            }),
+            })
         );
         await HTTPService.post(
             `${ENDPOINT}/collections/add-files`,
             params,
             null,
-            { 'X-Auth-Token': token },
+            { 'X-Auth-Token': token }
         );
     } catch (e) {
         logError(e, 'Add to collection Failed ');
@@ -357,13 +363,13 @@ const removeFromCollection = async (collection: Collection, files: File[]) => {
                     params['fileIDs'] = [];
                 }
                 params['fileIDs'].push(file.id);
-            }),
+            })
         );
         await HTTPService.post(
             `${ENDPOINT}/collections/remove-files`,
             params,
             null,
-            { 'X-Auth-Token': token },
+            { 'X-Auth-Token': token }
         );
     } catch (e) {
         logError(e, 'remove from collection failed ');
@@ -374,7 +380,7 @@ export const deleteCollection = async (
     collectionID: number,
     syncWithRemote: () => Promise<void>,
     redirectToAll: () => void,
-    setDialogMessage: SetDialogMessage,
+    setDialogMessage: SetDialogMessage
 ) => {
     try {
         const token = getToken();
@@ -383,7 +389,7 @@ export const deleteCollection = async (
             `${ENDPOINT}/collections/${collectionID}`,
             null,
             null,
-            { 'X-Auth-Token': token },
+            { 'X-Auth-Token': token }
         );
         await syncWithRemote();
         redirectToAll();
@@ -399,7 +405,7 @@ export const deleteCollection = async (
 
 export const renameCollection = async (
     collection: Collection,
-    newCollectionName: string,
+    newCollectionName: string
 ) => {
     const token = getToken();
     const worker = await new CryptoWorker();
@@ -408,7 +414,7 @@ export const renameCollection = async (
         nonce: nameDecryptionNonce,
     }: B64EncryptionResult = await worker.encryptUTF8(
         newCollectionName,
-        collection.key,
+        collection.key
     );
     const collectionRenameRequest = {
         collectionID: collection.id,
@@ -421,12 +427,12 @@ export const renameCollection = async (
         null,
         {
             'X-Auth-Token': token,
-        },
+        }
     );
 };
 export const shareCollection = async (
     collection: Collection,
-    withUserEmail: string,
+    withUserEmail: string
 ) => {
     try {
         const worker = await new CryptoWorker();
@@ -435,7 +441,7 @@ export const shareCollection = async (
         const publicKey: string = await getPublicKey(withUserEmail);
         const encryptedKey: string = await worker.boxSeal(
             collection.key,
-            publicKey,
+            publicKey
         );
         const shareCollectionRequest = {
             collectionID: collection.id,
@@ -448,7 +454,7 @@ export const shareCollection = async (
             null,
             {
                 'X-Auth-Token': token,
-            },
+            }
         );
     } catch (e) {
         logError(e, 'share collection failed ');
@@ -458,7 +464,7 @@ export const shareCollection = async (
 
 export const unshareCollection = async (
     collection: Collection,
-    withUserEmail: string,
+    withUserEmail: string
 ) => {
     try {
         const token = getToken();
@@ -472,7 +478,7 @@ export const unshareCollection = async (
             null,
             {
                 'X-Auth-Token': token,
-            },
+            }
         );
     } catch (e) {
         logError(e, 'unshare collection failed ');
@@ -492,11 +498,13 @@ export const getFavCollection = async () => {
 
 export const getNonEmptyCollections = (
     collections: Collection[],
-    files: File[],
+    files: File[]
 ) => {
     const nonEmptyCollectionsIds = new Set<number>();
     for (const file of files) {
         nonEmptyCollectionsIds.add(file.collectionID);
     }
-    return collections.filter((collection) => nonEmptyCollectionsIds.has(collection.id));
+    return collections.filter((collection) =>
+        nonEmptyCollectionsIds.has(collection.id)
+    );
 };

+ 12 - 12
src/services/downloadManager.ts

@@ -43,18 +43,18 @@ class DownloadManager {
             getThumbnailUrl(file.id),
             null,
             { 'X-Auth-Token': token },
-            { responseType: 'arraybuffer' },
+            { responseType: 'arraybuffer' }
         );
         const worker = await new CryptoWorker();
         const decrypted: any = await worker.decryptThumbnail(
             new Uint8Array(resp.data),
             await worker.fromB64(file.thumbnail.decryptionHeader),
-            file.key,
+            file.key
         );
         try {
             await cache.put(
                 file.id.toString(),
-                new Response(new Blob([decrypted])),
+                new Response(new Blob([decrypted]))
             );
         } catch (e) {
             // TODO: handle storage full exception.
@@ -71,11 +71,11 @@ class DownloadManager {
                 if (forPreview) {
                     if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
                         const originalName = fileNameWithoutExtension(
-                            file.metadata.title,
+                            file.metadata.title
                         );
                         const motionPhoto = await decodeMotionPhoto(
                             fileBlob,
-                            originalName,
+                            originalName
                         );
                         fileBlob = new Blob([motionPhoto.image]);
                     }
@@ -85,7 +85,7 @@ class DownloadManager {
                 }
                 this.fileDownloads.set(
                     `${file.id}_${forPreview}`,
-                    URL.createObjectURL(fileBlob),
+                    URL.createObjectURL(fileBlob)
                 );
             }
             return this.fileDownloads.get(`${file.id}_${forPreview}`);
@@ -108,12 +108,12 @@ class DownloadManager {
                 getFileUrl(file.id),
                 null,
                 { 'X-Auth-Token': token },
-                { responseType: 'arraybuffer' },
+                { responseType: 'arraybuffer' }
             );
             const decrypted: any = await worker.decryptFile(
                 new Uint8Array(resp.data),
                 await worker.fromB64(file.file.decryptionHeader),
-                file.key,
+                file.key
             );
             return generateStreamFromArrayBuffer(decrypted);
         }
@@ -126,7 +126,7 @@ class DownloadManager {
         const stream = new ReadableStream({
             async start(controller) {
                 const decryptionHeader = await worker.fromB64(
-                    file.file.decryptionHeader,
+                    file.file.decryptionHeader
                 );
                 const fileKey = await worker.fromB64(file.key);
                 const { pullState, decryptionChunkSize } =
@@ -139,19 +139,19 @@ class DownloadManager {
                         // Is there more data to read?
                         if (!done) {
                             const buffer = new Uint8Array(
-                                data.byteLength + value.byteLength,
+                                data.byteLength + value.byteLength
                             );
                             buffer.set(new Uint8Array(data), 0);
                             buffer.set(new Uint8Array(value), data.byteLength);
                             if (buffer.length > decryptionChunkSize) {
                                 const fileData = buffer.slice(
                                     0,
-                                    decryptionChunkSize,
+                                    decryptionChunkSize
                                 );
                                 const { decryptedData } =
                                     await worker.decryptChunk(
                                         fileData,
-                                        pullState,
+                                        pullState
                                     );
                                 controller.enqueue(decryptedData);
                                 data = buffer.slice(decryptionChunkSize);

+ 103 - 58
src/services/exportService.ts

@@ -1,7 +1,17 @@
 import { FILE_TYPE } from 'pages/gallery';
 import { retryAsyncFunction, runningInBrowser } from 'utils/common';
-import { getExportPendingFiles, getExportFailedFiles, getFilesUploadedAfterLastExport, getExportRecordFileUID, dedupe, getGoogleLikeMetadataFile } from 'utils/export';
-import { fileNameWithoutExtension, generateStreamFromArrayBuffer } from 'utils/file';
+import {
+    getExportPendingFiles,
+    getExportFailedFiles,
+    getFilesUploadedAfterLastExport,
+    getExportRecordFileUID,
+    dedupe,
+    getGoogleLikeMetadataFile,
+} from 'utils/export';
+import {
+    fileNameWithoutExtension,
+    generateStreamFromArrayBuffer,
+} from 'utils/file';
 import { logError } from 'utils/sentry';
 import { getData, LS_KEYS } from 'utils/storage/localStorage';
 import { Collection, getLocalCollections } from './collectionService';
@@ -19,7 +29,7 @@ export interface ExportStats {
 }
 
 export interface ExportRecord {
-    stage: ExportStage
+    stage: ExportStage;
     lastAttemptTimestamp: number;
     progress: ExportProgress;
     queuedFiles: string[];
@@ -30,7 +40,7 @@ export enum ExportStage {
     INIT,
     INPROGRESS,
     PAUSED,
-    FINISHED
+    FINISHED,
 }
 
 enum ExportNotification {
@@ -40,26 +50,26 @@ enum ExportNotification {
     FAILED = 'export failed',
     ABORT = 'export aborted',
     PAUSE = 'export paused',
-    UP_TO_DATE = `no new files to export`
+    UP_TO_DATE = `no new files to export`,
 }
 
 enum RecordType {
     SUCCESS = 'success',
-    FAILED = 'failed'
+    FAILED = 'failed',
 }
 export enum ExportType {
     NEW,
     PENDING,
-    RETRY_FAILED
+    RETRY_FAILED,
 }
 
-const ExportRecordFileName='export_status.json';
-const MetadataFolderName='metadata';
+const ExportRecordFileName = 'export_status.json';
+const MetadataFolderName = 'metadata';
 
 class ExportService {
     ElectronAPIs: any;
 
-    private exportInProgress: Promise<{ paused: boolean; }> = null;
+    private exportInProgress: Promise<{ paused: boolean }> = null;
     private recordUpdateInProgress = Promise.resolve();
     private stopExport: boolean = false;
     private pauseExport: boolean = false;
@@ -76,7 +86,10 @@ class ExportService {
     pauseRunningExport() {
         this.pauseExport = true;
     }
-    async exportFiles(updateProgress: (progress: ExportProgress) => void, exportType: ExportType) {
+    async exportFiles(
+        updateProgress: (progress: ExportProgress) => void,
+        exportType: ExportType
+    ) {
         if (this.exportInProgress) {
             this.ElectronAPIs.sendNotification(ExportNotification.IN_PROGRESS);
             return this.exportInProgress;
@@ -93,22 +106,37 @@ class ExportService {
         const exportRecord = await this.getExportRecord(exportDir);
 
         if (exportType === ExportType.NEW) {
-            filesToExport = await getFilesUploadedAfterLastExport(allFiles, exportRecord);
+            filesToExport = await getFilesUploadedAfterLastExport(
+                allFiles,
+                exportRecord
+            );
         } else if (exportType === ExportType.RETRY_FAILED) {
             filesToExport = await getExportFailedFiles(allFiles, exportRecord);
         } else {
             filesToExport = await getExportPendingFiles(allFiles, exportRecord);
         }
-        this.exportInProgress = this.fileExporter(filesToExport, collections, updateProgress, exportDir);
+        this.exportInProgress = this.fileExporter(
+            filesToExport,
+            collections,
+            updateProgress,
+            exportDir
+        );
         const resp = await this.exportInProgress;
         this.exportInProgress = null;
         return resp;
     }
 
-    async fileExporter(files: File[], collections: Collection[], updateProgress: (progress: ExportProgress,) => void, dir: string): Promise<{ paused: boolean }> {
+    async fileExporter(
+        files: File[],
+        collections: Collection[],
+        updateProgress: (progress: ExportProgress) => void,
+        dir: string
+    ): Promise<{ paused: boolean }> {
         try {
             if (!files?.length) {
-                this.ElectronAPIs.sendNotification(ExportNotification.UP_TO_DATE);
+                this.ElectronAPIs.sendNotification(
+                    ExportNotification.UP_TO_DATE
+                );
                 return { paused: false };
             }
             this.stopExport = false;
@@ -117,22 +145,24 @@ class ExportService {
             const failedFileCount = 0;
 
             this.ElectronAPIs.showOnTray({
-                export_progress:
-                    `0 / ${files.length} files exported`,
+                export_progress: `0 / ${files.length} files exported`,
             });
             updateProgress({
-                current: 0, total: files.length,
+                current: 0,
+                total: files.length,
             });
             this.ElectronAPIs.sendNotification(ExportNotification.START);
 
             const collectionIDMap = new Map<number, string>();
             for (const collection of collections) {
-                const collectionFolderPath = `${dir}/${collection.id}_${this.sanitizeName(collection.name)}`;
+                const collectionFolderPath = `${dir}/${
+                    collection.id
+                }_${this.sanitizeName(collection.name)}`;
                 await this.ElectronAPIs.checkExistsAndCreateCollectionDir(
-                    collectionFolderPath,
+                    collectionFolderPath
                 );
                 await this.ElectronAPIs.checkExistsAndCreateCollectionDir(
-                    `${collectionFolderPath}/${MetadataFolderName}`,
+                    `${collectionFolderPath}/${MetadataFolderName}`
                 );
                 collectionIDMap.set(collection.id, collectionFolderPath);
             }
@@ -140,8 +170,7 @@ class ExportService {
                 if (this.stopExport || this.pauseExport) {
                     if (this.pauseExport) {
                         this.ElectronAPIs.showOnTray({
-                            export_progress:
-                                `${index} / ${files.length} files exported (paused)`,
+                            export_progress: `${index} / ${files.length} files exported (paused)`,
                             paused: true,
                         });
                     }
@@ -150,39 +179,42 @@ class ExportService {
                 const collectionPath = collectionIDMap.get(file.collectionID);
                 try {
                     await this.downloadAndSave(file, collectionPath);
-                    await this.addFileExportRecord(dir, file, RecordType.SUCCESS);
+                    await this.addFileExportRecord(
+                        dir,
+                        file,
+                        RecordType.SUCCESS
+                    );
                 } catch (e) {
-                    await this.addFileExportRecord(dir, file, RecordType.FAILED);
-                    logError(e, 'download and save failed for file during export');
+                    await this.addFileExportRecord(
+                        dir,
+                        file,
+                        RecordType.FAILED
+                    );
+                    logError(
+                        e,
+                        'download and save failed for file during export'
+                    );
                 }
                 this.ElectronAPIs.showOnTray({
-                    export_progress:
-                        `${index + 1} / ${files.length} files exported`,
+                    export_progress: `${index + 1} / ${
+                        files.length
+                    } files exported`,
                 });
                 updateProgress({ current: index + 1, total: files.length });
             }
             if (this.stopExport) {
-                this.ElectronAPIs.sendNotification(
-                    ExportNotification.ABORT,
-                );
+                this.ElectronAPIs.sendNotification(ExportNotification.ABORT);
                 this.ElectronAPIs.showOnTray();
             } else if (this.pauseExport) {
-                this.ElectronAPIs.sendNotification(
-                    ExportNotification.PAUSE,
-                );
+                this.ElectronAPIs.sendNotification(ExportNotification.PAUSE);
                 return { paused: true };
             } else if (failedFileCount > 0) {
-                this.ElectronAPIs.sendNotification(
-                    ExportNotification.FAILED,
-                );
+                this.ElectronAPIs.sendNotification(ExportNotification.FAILED);
                 this.ElectronAPIs.showOnTray({
-                    retry_export:
-                        `export failed - retry export`,
+                    retry_export: `export failed - retry export`,
                 });
             } else {
-                this.ElectronAPIs.sendNotification(
-                    ExportNotification.FINISH,
-                );
+                this.ElectronAPIs.sendNotification(ExportNotification.FINISH);
                 this.ElectronAPIs.showOnTray();
             }
             return { paused: false };
@@ -199,13 +231,18 @@ class ExportService {
     async addFileExportRecord(folder: string, file: File, type: RecordType) {
         const fileUID = getExportRecordFileUID(file);
         const exportRecord = await this.getExportRecord(folder);
-        exportRecord.queuedFiles = exportRecord.queuedFiles.filter((queuedFilesUID) => queuedFilesUID !== fileUID);
+        exportRecord.queuedFiles = exportRecord.queuedFiles.filter(
+            (queuedFilesUID) => queuedFilesUID !== fileUID
+        );
         if (type === RecordType.SUCCESS) {
             if (!exportRecord.exportedFiles) {
                 exportRecord.exportedFiles = [];
             }
             exportRecord.exportedFiles.push(fileUID);
-            exportRecord.failedFiles && (exportRecord.failedFiles = exportRecord.failedFiles.filter((FailedFileUID) => FailedFileUID !== fileUID));
+            exportRecord.failedFiles &&
+                (exportRecord.failedFiles = exportRecord.failedFiles.filter(
+                    (FailedFileUID) => FailedFileUID !== fileUID
+                ));
         } else {
             if (!exportRecord.failedFiles) {
                 exportRecord.failedFiles = [];
@@ -229,7 +266,10 @@ class ExportService {
                 }
                 const exportRecord = await this.getExportRecord(folder);
                 const newRecord = { ...exportRecord, ...newData };
-                await this.ElectronAPIs.setExportRecord(`${folder}/${ExportRecordFileName}`, JSON.stringify(newRecord, null, 2));
+                await this.ElectronAPIs.setExportRecord(
+                    `${folder}/${ExportRecordFileName}`,
+                    JSON.stringify(newRecord, null, 2)
+                );
             } catch (e) {
                 logError(e, 'error updating Export Record');
             }
@@ -242,7 +282,9 @@ class ExportService {
             if (!folder) {
                 folder = getData(LS_KEYS.EXPORT)?.folder;
             }
-            const recordFile = await this.ElectronAPIs.getExportRecord(`${folder}/${ExportRecordFileName}`);
+            const recordFile = await this.ElectronAPIs.getExportRecord(
+                `${folder}/${ExportRecordFileName}`
+            );
             if (recordFile) {
                 return JSON.parse(recordFile);
             } else {
@@ -253,37 +295,40 @@ class ExportService {
         }
     }
 
-    async downloadAndSave(file: File, collectionPath:string) {
-        const uid = `${file.id}_${this.sanitizeName(
-            file.metadata.title,
-        )}`;
-        const fileStream = await retryAsyncFunction(()=>downloadManager.downloadFile(file));
-        if (file.metadata.fileType===FILE_TYPE.LIVE_PHOTO) {
+    async downloadAndSave(file: File, collectionPath: string) {
+        const uid = `${file.id}_${this.sanitizeName(file.metadata.title)}`;
+        const fileStream = await retryAsyncFunction(() =>
+            downloadManager.downloadFile(file)
+        );
+        if (file.metadata.fileType === FILE_TYPE.LIVE_PHOTO) {
             this.exportMotionPhoto(fileStream, file, collectionPath);
         } else {
-            this.ElectronAPIs.saveStreamToDisk(`${collectionPath}/${uid}`, fileStream);
+            this.ElectronAPIs.saveStreamToDisk(
+                `${collectionPath}/${uid}`,
+                fileStream
+            );
         }
         this.ElectronAPIs.saveFileToDisk(
             `${collectionPath}/${MetadataFolderName}/${uid}.json`,
-            getGoogleLikeMetadataFile(uid, file.metadata),
+            getGoogleLikeMetadataFile(uid, file.metadata)
         );
     }
 
     private async exportMotionPhoto(
         fileStream: ReadableStream<any>,
         file: File,
-        collectionPath: string,
+        collectionPath: string
     ) {
         const fileBlob = await new Response(fileStream).blob();
         const originalName = fileNameWithoutExtension(file.metadata.title);
         const motionPhoto = await decodeMotionPhoto(fileBlob, originalName);
 
         const imageStream = generateStreamFromArrayBuffer(motionPhoto.image);
-        const imageSavePath=`${collectionPath}/${file.id}_${motionPhoto.imageNameTitle}`;
+        const imageSavePath = `${collectionPath}/${file.id}_${motionPhoto.imageNameTitle}`;
         this.ElectronAPIs.saveStreamToDisk(imageSavePath, imageStream);
 
         const videoStream = generateStreamFromArrayBuffer(motionPhoto.video);
-        const videoSavePath=`${collectionPath}/${file.id}_${motionPhoto.videoNameTitle}`;
+        const videoSavePath = `${collectionPath}/${file.id}_${motionPhoto.videoNameTitle}`;
         this.ElectronAPIs.saveStreamToDisk(videoSavePath, videoStream);
     }
 
@@ -293,6 +338,6 @@ class ExportService {
 
     isExportInProgress = () => {
         return this.exportInProgress !== null;
-    }
+    };
 }
 export default new ExportService();

+ 40 - 21
src/services/fileService.ts

@@ -43,7 +43,10 @@ export const getLocalFiles = async () => {
     return files;
 };
 
-export const syncFiles = async (collections: Collection[], setFiles: (files: File[]) => void) => {
+export const syncFiles = async (
+    collections: Collection[],
+    setFiles: (files: File[]) => void
+) => {
     const localFiles = await getLocalFiles();
     let files = await removeDeletedCollectionFiles(collections, localFiles);
     if (files.length !== localFiles.length) {
@@ -54,11 +57,19 @@ export const syncFiles = async (collections: Collection[], setFiles: (files: Fil
         if (!getToken()) {
             continue;
         }
-        const lastSyncTime = (await localForage.getItem<number>(`${collection.id}-time`)) ?? 0;
+        const lastSyncTime =
+            (await localForage.getItem<number>(`${collection.id}-time`)) ?? 0;
         if (collection.updationTime === lastSyncTime) {
             continue;
         }
-        const fetchedFiles = (await getFiles(collection, lastSyncTime, DIFF_LIMIT, files, setFiles)) ?? [];
+        const fetchedFiles =
+            (await getFiles(
+                collection,
+                lastSyncTime,
+                DIFF_LIMIT,
+                files,
+                setFiles
+            )) ?? [];
         files.push(...fetchedFiles);
         const latestVersionFiles = new Map<string, File>();
         files.forEach((file) => {
@@ -78,17 +89,19 @@ export const syncFiles = async (collections: Collection[], setFiles: (files: Fil
             }
             files.push(file);
         }
-        files=sortFiles(files);
+        files = sortFiles(files);
         await localForage.setItem('files', files);
         await localForage.setItem(
             `${collection.id}-time`,
-            collection.updationTime,
+            collection.updationTime
+        );
+        setFiles(
+            files.map((item) => ({
+                ...item,
+                w: window.innerWidth,
+                h: window.innerHeight,
+            }))
         );
-        setFiles(files.map((item) => ({
-            ...item,
-            w: window.innerWidth,
-            h: window.innerHeight,
-        })));
     }
     return {
         files: files.map((item) => ({
@@ -104,11 +117,12 @@ export const getFiles = async (
     sinceTime: number,
     limit: number,
     files: File[],
-    setFiles: (files: File[]) => void,
+    setFiles: (files: File[]) => void
 ): Promise<File[]> => {
     try {
         const decryptedFiles: File[] = [];
-        let time = sinceTime ||
+        let time =
+            sinceTime ||
             (await localForage.getItem<number>(`${collection.id}-time`)) ||
             0;
         let resp;
@@ -126,7 +140,7 @@ export const getFiles = async (
                 },
                 {
                     'X-Auth-Token': token,
-                },
+                }
             );
 
             decryptedFiles.push(
@@ -136,16 +150,21 @@ export const getFiles = async (
                             file = await decryptFile(file, collection);
                         }
                         return file;
-                    }) as Promise<File>[],
-                )),
+                    }) as Promise<File>[]
+                ))
             );
 
             if (resp.data.diff.length) {
                 time = resp.data.diff.slice(-1)[0].updationTime;
             }
-            setFiles([...(files || []), ...decryptedFiles].filter((item) => !item.isDeleted).sort(
-                (a, b) => b.metadata.creationTime - a.metadata.creationTime,
-            ));
+            setFiles(
+                [...(files || []), ...decryptedFiles]
+                    .filter((item) => !item.isDeleted)
+                    .sort(
+                        (a, b) =>
+                            b.metadata.creationTime - a.metadata.creationTime
+                    )
+            );
         } while (resp.data.diff.length === limit);
         return decryptedFiles;
     } catch (e) {
@@ -155,7 +174,7 @@ export const getFiles = async (
 
 const removeDeletedCollectionFiles = async (
     collections: Collection[],
-    files: File[],
+    files: File[]
 ) => {
     const syncedCollectionIds = new Set<number>();
     for (const collection of collections) {
@@ -168,7 +187,7 @@ const removeDeletedCollectionFiles = async (
 export const deleteFiles = async (
     filesToDelete: number[],
     clearSelection: Function,
-    syncWithRemote: Function,
+    syncWithRemote: Function
 ) => {
     try {
         const token = getToken();
@@ -181,7 +200,7 @@ export const deleteFiles = async (
             null,
             {
                 'X-Auth-Token': token,
-            },
+            }
         );
         clearSelection();
         syncWithRemote();

+ 3 - 3
src/services/motionPhotoService.ts

@@ -10,7 +10,7 @@ class MotionPhoto {
 
 export const decodeMotionPhoto = async (
     zipBlob: Blob,
-    originalName: string,
+    originalName: string
 ) => {
     const zip = await JSZip.loadAsync(zipBlob, { createFolders: true });
 
@@ -20,13 +20,13 @@ export const decodeMotionPhoto = async (
             motionPhoto.imageNameTitle =
                 originalName + fileExtensionWithDot(zipFilename);
             motionPhoto.image = await zip.files[zipFilename].async(
-                'uint8array',
+                'uint8array'
             );
         } else if (zipFilename.startsWith('video')) {
             motionPhoto.videoNameTitle =
                 originalName + fileExtensionWithDot(zipFilename);
             motionPhoto.video = await zip.files[zipFilename].async(
-                'uint8array',
+                'uint8array'
             );
         }
     }

+ 7 - 4
src/services/searchService.ts

@@ -31,14 +31,15 @@ export function parseHumanDate(humanDate: string): DateValue[] {
             return dates.reverse();
         }
         return dates;
-    } if (date1) {
+    }
+    if (date1) {
         return [{ month: date1.getMonth() }];
     }
     return [];
 }
 
 export async function searchLocation(
-    searchPhrase: string,
+    searchPhrase: string
 ): Promise<LocationSearchResponse[]> {
     const resp = await HTTPService.get(
         `${ENDPOINT}/search/location`,
@@ -48,7 +49,7 @@ export async function searchLocation(
         },
         {
             'X-Auth-Token': getToken(),
-        },
+        }
     );
     return resp.data.results;
 }
@@ -75,7 +76,9 @@ export function getHolidaySuggestion(searchPhrase: string): Suggestion[] {
             value: { month: 11, date: 31 },
             type: SuggestionType.DATE,
         },
-    ].filter((suggestion) => suggestion.label.toLowerCase().includes(searchPhrase.toLowerCase()));
+    ].filter((suggestion) =>
+        suggestion.label.toLowerCase().includes(searchPhrase.toLowerCase())
+    );
 }
 
 export function getYearSuggestion(searchPhrase: string): Suggestion[] {

+ 159 - 127
src/services/uploadService.ts

@@ -5,11 +5,7 @@ import { File, fileAttribute } from './fileService';
 import { Collection } from './collectionService';
 import { FILE_TYPE, SetFiles } from 'pages/gallery';
 import { retryAsyncFunction, sleep } from 'utils/common';
-import {
-    handleError,
-    parseError,
-    CustomError,
-} from 'utils/common/errorUtil';
+import { handleError, parseError, CustomError } from 'utils/common/errorUtil';
 import { ComlinkWorker, getDedicatedCryptoWorker } from 'utils/crypto';
 import * as convert from 'xml-js';
 import { ENCRYPTION_CHUNK_SIZE } from 'types';
@@ -50,7 +46,7 @@ export enum FileUploadResults {
     FAILED = -1,
     SKIPPED = -2,
     UNSUPPORTED = -3,
-    BLOCKED=-4,
+    BLOCKED = -4,
     UPLOADED = 100,
 }
 
@@ -121,7 +117,7 @@ interface ProcessedFile {
     metadata: fileAttribute;
     filename: string;
 }
-interface BackupedFile extends Omit<ProcessedFile, 'filename'> { }
+interface BackupedFile extends Omit<ProcessedFile, 'filename'> {}
 
 interface uploadFile extends BackupedFile {
     collectionID: number;
@@ -144,19 +140,19 @@ class UploadService {
     private filesCompleted: number;
     private totalFileCount: number;
     private fileProgress: Map<string, number>;
-    private uploadResult:Map<string, number>;
+    private uploadResult: Map<string, number>;
     private metadataMap: Map<string, Object>;
     private filesToBeUploaded: FileWithCollection[];
     private progressBarProps;
     private failedFiles: FileWithCollection[];
     private existingFilesCollectionWise: Map<number, File[]>;
     private existingFiles: File[];
-    private setFiles:SetFiles;
+    private setFiles: SetFiles;
     public async uploadFiles(
         filesWithCollectionToUpload: FileWithCollection[],
         existingFiles: File[],
         progressBarProps,
-        setFiles:SetFiles,
+        setFiles: SetFiles
     ) {
         try {
             progressBarProps.setUploadStage(UPLOAD_STAGES.START);
@@ -167,10 +163,11 @@ class UploadService {
             this.failedFiles = [];
             this.metadataMap = new Map<string, object>();
             this.progressBarProps = progressBarProps;
-            this.existingFiles=existingFiles;
-            this.existingFilesCollectionWise = sortFilesIntoCollections(existingFiles);
+            this.existingFiles = existingFiles;
+            this.existingFilesCollectionWise =
+                sortFilesIntoCollections(existingFiles);
             this.updateProgressBarUI();
-            this.setFiles=setFiles;
+            this.setFiles = setFiles;
             const metadataFiles: globalThis.File[] = [];
             const actualFiles: FileWithCollection[] = [];
             filesWithCollectionToUpload.forEach((fileWithCollection) => {
@@ -188,7 +185,7 @@ class UploadService {
             this.filesToBeUploaded = actualFiles;
 
             progressBarProps.setUploadStage(
-                UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES,
+                UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES
             );
             this.totalFileCount = metadataFiles.length;
             this.perFileProgress = 100 / metadataFiles.length;
@@ -215,20 +212,16 @@ class UploadService {
                 }
             }
             const uploadProcesses = [];
-            for (
-                let i = 0;
-                i < MAX_CONCURRENT_UPLOADS;
-                i++
-            ) {
-                if (this.filesToBeUploaded.length>0) {
-                    const fileWithCollection= this.filesToBeUploaded.pop();
+            for (let i = 0; i < MAX_CONCURRENT_UPLOADS; i++) {
+                if (this.filesToBeUploaded.length > 0) {
+                    const fileWithCollection = this.filesToBeUploaded.pop();
                     this.cryptoWorkers[i] = getDedicatedCryptoWorker();
                     uploadProcesses.push(
                         this.uploader(
                             await new this.cryptoWorkers[i].comlink(),
                             new FileReader(),
-                            fileWithCollection,
-                        ),
+                            fileWithCollection
+                        )
                     );
                 }
             }
@@ -250,13 +243,13 @@ class UploadService {
     private async uploader(
         worker: any,
         reader: FileReader,
-        fileWithCollection: FileWithCollection,
+        fileWithCollection: FileWithCollection
     ) {
         const { file: rawFile, collection } = fileWithCollection;
         this.fileProgress.set(rawFile.name, 0);
         this.updateProgressBarUI();
-        let file:FileInMemory=null;
-        let encryptedFile: EncryptedFile=null;
+        let file: FileInMemory = null;
+        let encryptedFile: EncryptedFile = null;
         try {
             // read the file into memory
             file = await this.readFile(reader, rawFile);
@@ -267,25 +260,34 @@ class UploadService {
                 this.updateProgressBarUI();
                 await sleep(TwoSecondInMillSeconds);
             } else {
-                encryptedFile = await this.encryptFile(worker, file, collection.key);
+                encryptedFile = await this.encryptFile(
+                    worker,
+                    file,
+                    collection.key
+                );
 
                 const backupedFile: BackupedFile = await this.uploadToBucket(
-                    encryptedFile.file,
+                    encryptedFile.file
                 );
 
                 let uploadFile: uploadFile = this.getUploadFile(
                     collection,
                     backupedFile,
-                    encryptedFile.fileKey,
+                    encryptedFile.fileKey
                 );
 
-
-                const uploadedFile =await this.uploadFile(uploadFile);
-                const decryptedFile=await decryptFile(uploadedFile, collection);
+                const uploadedFile = await this.uploadFile(uploadFile);
+                const decryptedFile = await decryptFile(
+                    uploadedFile,
+                    collection
+                );
 
                 this.existingFiles.push(decryptedFile);
-                this.existingFiles=sortFiles(this.existingFiles);
-                await localForage.setItem('files', removeUnneccessaryFileProps(this.existingFiles));
+                this.existingFiles = sortFiles(this.existingFiles);
+                await localForage.setItem(
+                    'files',
+                    removeUnneccessaryFileProps(this.existingFiles)
+                );
                 this.setFiles(this.existingFiles);
 
                 uploadFile = null;
@@ -296,34 +298,42 @@ class UploadService {
             logError(e, 'file upload failed');
             handleError(e);
             this.failedFiles.push(fileWithCollection);
-            if (e.message ===CustomError.ETAG_MISSING) {
+            if (e.message === CustomError.ETAG_MISSING) {
                 this.fileProgress.set(rawFile.name, FileUploadResults.BLOCKED);
             } else {
                 this.fileProgress.set(rawFile.name, FileUploadResults.FAILED);
             }
         } finally {
-            file=null;
-            encryptedFile=null;
+            file = null;
+            encryptedFile = null;
         }
-        this.uploadResult.set(rawFile.name, this.fileProgress.get(rawFile.name));
+        this.uploadResult.set(
+            rawFile.name,
+            this.fileProgress.get(rawFile.name)
+        );
         this.fileProgress.delete(rawFile.name);
         this.updateProgressBarUI();
 
         if (this.filesToBeUploaded.length > 0) {
-            await this.uploader(
-                worker,
-                reader,
-                this.filesToBeUploaded.pop(),
-            );
+            await this.uploader(worker, reader, this.filesToBeUploaded.pop());
         }
     }
-    async retryFailedFiles(localFiles:File[]) {
-        await this.uploadFiles(this.failedFiles, localFiles, this.progressBarProps, this.setFiles);
+    async retryFailedFiles(localFiles: File[]) {
+        await this.uploadFiles(
+            this.failedFiles,
+            localFiles,
+            this.progressBarProps,
+            this.setFiles
+        );
     }
 
     private updateProgressBarUI() {
-        const { setPercentComplete, setFileCounter, setFileProgress, setUploadResult } =
-            this.progressBarProps;
+        const {
+            setPercentComplete,
+            setFileCounter,
+            setFileProgress,
+            setUploadResult,
+        } = this.progressBarProps;
         setFileCounter({
             finished: this.filesCompleted,
             total: this.totalFileCount,
@@ -346,7 +356,7 @@ class UploadService {
 
     private fileAlreadyInCollection(
         newFile: FileInMemory,
-        collection: Collection,
+        collection: Collection
     ): boolean {
         const collectionFiles =
             this.existingFilesCollectionWise.get(collection.id) ?? [];
@@ -359,7 +369,7 @@ class UploadService {
     }
     private areFilesSame(
         existingFile: MetadataObject,
-        newFile: MetadataObject,
+        newFile: MetadataObject
     ): boolean {
         if (
             existingFile.fileType === newFile.fileType &&
@@ -375,10 +385,8 @@ class UploadService {
 
     private async readFile(reader: FileReader, receivedFile: globalThis.File) {
         try {
-            const { thumbnail, hasStaticThumbnail } = await this.generateThumbnail(
-                reader,
-                receivedFile,
-            );
+            const { thumbnail, hasStaticThumbnail } =
+                await this.generateThumbnail(reader, receivedFile);
 
             let fileType: FILE_TYPE;
             switch (receivedFile.type.split('/')[0]) {
@@ -402,13 +410,13 @@ class UploadService {
             const { location, creationTime } = await this.getExifData(
                 reader,
                 receivedFile,
-                fileType,
+                fileType
             );
             let receivedFileOriginalName = receivedFile.name;
             if (receivedFile.name.endsWith(EDITED_FILE_SUFFIX)) {
                 receivedFileOriginalName = receivedFile.name.slice(
                     0,
-                    -1 * EDITED_FILE_SUFFIX.length,
+                    -1 * EDITED_FILE_SUFFIX.length
                 );
             }
             const metadata = Object.assign(
@@ -421,7 +429,7 @@ class UploadService {
                     longitude: location?.latitude,
                     fileType,
                 },
-                this.metadataMap.get(receivedFileOriginalName),
+                this.metadataMap.get(receivedFileOriginalName)
             );
             if (hasStaticThumbnail) {
                 metadata['hasStaticThumbnail'] = hasStaticThumbnail;
@@ -445,7 +453,7 @@ class UploadService {
     private async encryptFile(
         worker: any,
         file: FileInMemory,
-        encryptionKey: string,
+        encryptionKey: string
     ): Promise<EncryptedFile> {
         try {
             const { key: fileKey, file: encryptedFiledata }: EncryptionResult =
@@ -460,7 +468,7 @@ class UploadService {
 
             const encryptedKey: B64EncryptionResult = await worker.encryptToB64(
                 fileKey,
-                encryptionKey,
+                encryptionKey
             );
 
             const result: EncryptedFile = {
@@ -491,7 +499,7 @@ class UploadService {
                 const encryptedFileChunk = await worker.encryptFileChunk(
                     value,
                     pushState,
-                    ref.pullCount === chunkCount,
+                    ref.pullCount === chunkCount
                 );
                 controller.enqueue(encryptedFileChunk);
                 if (ref.pullCount === chunkCount) {
@@ -515,30 +523,30 @@ class UploadService {
             if (isDataStream(file.file.encryptedData)) {
                 const { chunkCount, stream } = file.file.encryptedData;
                 const uploadPartCount = Math.ceil(
-                    chunkCount / CHUNKS_COMBINED_FOR_UPLOAD,
+                    chunkCount / CHUNKS_COMBINED_FOR_UPLOAD
                 );
                 const filePartUploadURLs = await this.fetchMultipartUploadURLs(
-                    uploadPartCount,
+                    uploadPartCount
                 );
                 fileObjectKey = await this.putFileInParts(
                     filePartUploadURLs,
                     stream,
                     file.filename,
-                    uploadPartCount,
+                    uploadPartCount
                 );
             } else {
                 const fileUploadURL = await this.getUploadURL();
                 fileObjectKey = await this.putFile(
                     fileUploadURL,
                     file.file.encryptedData,
-                    file.filename,
+                    file.filename
                 );
             }
             const thumbnailUploadURL = await this.getUploadURL();
             const thumbnailObjectKey = await this.putFile(
                 thumbnailUploadURL,
                 file.thumbnail.encryptedData as Uint8Array,
-                null,
+                null
             );
 
             const backupedFile: BackupedFile = {
@@ -562,7 +570,7 @@ class UploadService {
     private getUploadFile(
         collection: Collection,
         backupedFile: BackupedFile,
-        fileKey: B64EncryptionResult,
+        fileKey: B64EncryptionResult
     ): uploadFile {
         const uploadFile: uploadFile = {
             collectionID: collection.id,
@@ -574,20 +582,17 @@ class UploadService {
         return uploadFile;
     }
 
-    private async uploadFile(uploadFile: uploadFile):Promise<File> {
+    private async uploadFile(uploadFile: uploadFile): Promise<File> {
         try {
             const token = getToken();
             if (!token) {
                 return;
             }
-            const response = await retryAsyncFunction(()=>HTTPService.post(
-                `${ENDPOINT}/files`,
-                uploadFile,
-                null,
-                {
+            const response = await retryAsyncFunction(() =>
+                HTTPService.post(`${ENDPOINT}/files`, uploadFile, null, {
                     'X-Auth-Token': token,
-                },
-            ));
+                })
+            );
             return response.data;
         } catch (e) {
             logError(e, 'upload Files Failed');
@@ -600,8 +605,10 @@ class UploadService {
             const metadataJSON: object = await new Promise(
                 (resolve, reject) => {
                     const reader = new FileReader();
-                    reader.onabort = () => reject(Error('file reading was aborted'));
-                    reader.onerror = () => reject(Error('file reading has failed'));
+                    reader.onabort = () =>
+                        reject(Error('file reading was aborted'));
+                    reader.onerror = () =>
+                        reject(Error('file reading has failed'));
                     reader.onload = () => {
                         const result =
                             typeof reader.result !== 'string'
@@ -610,7 +617,7 @@ class UploadService {
                         resolve(JSON.parse(result));
                     };
                     reader.readAsText(receivedFile);
-                },
+                }
             );
 
             const metaDataObject = {};
@@ -657,8 +664,8 @@ class UploadService {
     }
     private async generateThumbnail(
         reader: FileReader,
-        file: globalThis.File,
-    ): Promise<{ thumbnail: Uint8Array, hasStaticThumbnail: boolean }> {
+        file: globalThis.File
+    ): Promise<{ thumbnail: Uint8Array; hasStaticThumbnail: boolean }> {
         try {
             let hasStaticThumbnail = false;
             const canvas = document.createElement('canvas');
@@ -672,7 +679,7 @@ class UploadService {
                         file = new globalThis.File(
                             [await convertHEIC2JPEG(file)],
                             null,
-                            null,
+                            null
                         );
                     }
                     let image = new Image();
@@ -682,7 +689,8 @@ class UploadService {
                         image.onload = () => {
                             try {
                                 const thumbnailWidth =
-                                    (image.width * THUMBNAIL_HEIGHT) / image.height;
+                                    (image.width * THUMBNAIL_HEIGHT) /
+                                    image.height;
                                 canvas.width = thumbnailWidth;
                                 canvas.height = THUMBNAIL_HEIGHT;
                                 canvas_CTX.drawImage(
@@ -690,7 +698,7 @@ class UploadService {
                                     0,
                                     0,
                                     thumbnailWidth,
-                                    THUMBNAIL_HEIGHT,
+                                    THUMBNAIL_HEIGHT
                                 );
                                 image = null;
                                 clearTimeout(timeout);
@@ -698,15 +706,23 @@ class UploadService {
                             } catch (e) {
                                 reject(e);
                                 logError(e);
-                                reject(Error(`${CustomError.THUMBNAIL_GENERATION_FAILED} err: ${e}`));
+                                reject(
+                                    Error(
+                                        `${CustomError.THUMBNAIL_GENERATION_FAILED} err: ${e}`
+                                    )
+                                );
                             }
                         };
                         timeout = setTimeout(
                             () =>
                                 reject(
-                                    Error(`wait time exceeded for format ${file.name.split('.').slice(-1)[0]}`),
+                                    Error(
+                                        `wait time exceeded for format ${
+                                            file.name.split('.').slice(-1)[0]
+                                        }`
+                                    )
                                 ),
-                            WAIT_TIME_THUMBNAIL_GENERATION,
+                            WAIT_TIME_THUMBNAIL_GENERATION
                         );
                     });
                 } else {
@@ -728,7 +744,7 @@ class UploadService {
                                     0,
                                     0,
                                     thumbnailWidth,
-                                    THUMBNAIL_HEIGHT,
+                                    THUMBNAIL_HEIGHT
                                 );
                                 video = null;
                                 clearTimeout(timeout);
@@ -736,16 +752,26 @@ class UploadService {
                             } catch (e) {
                                 reject(e);
                                 logError(e);
-                                reject(Error(`${CustomError.THUMBNAIL_GENERATION_FAILED} err: ${e}`));
+                                reject(
+                                    Error(
+                                        `${CustomError.THUMBNAIL_GENERATION_FAILED} err: ${e}`
+                                    )
+                                );
                             }
                         });
                         video.preload = 'metadata';
                         video.src = imageURL;
                         video.currentTime = 3;
-                        timeout=setTimeout(
+                        timeout = setTimeout(
                             () =>
-                                reject(Error(`wait time exceeded for format ${file.name.split('.').slice(-1)[0]}`)),
-                            WAIT_TIME_THUMBNAIL_GENERATION,
+                                reject(
+                                    Error(
+                                        `wait time exceeded for format ${
+                                            file.name.split('.').slice(-1)[0]
+                                        }`
+                                    )
+                                ),
+                            WAIT_TIME_THUMBNAIL_GENERATION
                         );
                     });
                 }
@@ -768,7 +794,7 @@ class UploadService {
                             resolve(blob);
                         },
                         'image/jpeg',
-                        quality,
+                        quality
                     );
                 });
                 thumbnailBlob = thumbnailBlob ?? new Blob([]);
@@ -778,7 +804,7 @@ class UploadService {
             );
             const thumbnail = await this.getUint8ArrayView(
                 reader,
-                thumbnailBlob,
+                thumbnailBlob
             );
             return { thumbnail, hasStaticThumbnail };
         } catch (e) {
@@ -791,7 +817,7 @@ class UploadService {
         const self = this;
         const fileChunkReader = (async function* fileChunkReaderMaker(
             fileSize,
-            self,
+            self
         ) {
             let offset = 0;
             while (offset < fileSize) {
@@ -819,11 +845,12 @@ class UploadService {
 
     private async getUint8ArrayView(
         reader: FileReader,
-        file: Blob,
+        file: Blob
     ): Promise<Uint8Array> {
         try {
             return await new Promise((resolve, reject) => {
-                reader.onabort = () => reject(Error('file reading was aborted'));
+                reader.onabort = () =>
+                    reject(Error('file reading was aborted'));
                 reader.onerror = () => reject(Error('file reading has failed'));
                 reader.onload = () => {
                     // Do whatever you want with the file contents
@@ -861,10 +888,10 @@ class UploadService {
                         {
                             count: Math.min(
                                 MAX_URL_REQUESTS,
-                                (this.totalFileCount - this.filesCompleted) * 2,
+                                (this.totalFileCount - this.filesCompleted) * 2
                             ),
                         },
-                        { 'X-Auth-Token': token },
+                        { 'X-Auth-Token': token }
                     );
                     const response = await this.uploadURLFetchInProgress;
                     this.uploadURLs.push(...response.data['urls']);
@@ -880,7 +907,7 @@ class UploadService {
     }
 
     private async fetchMultipartUploadURLs(
-        count: number,
+        count: number
     ): Promise<MultipartUploadURLs> {
         try {
             const token = getToken();
@@ -892,7 +919,7 @@ class UploadService {
                 {
                     count,
                 },
-                { 'X-Auth-Token': token },
+                { 'X-Auth-Token': token }
             );
 
             return response.data['urls'];
@@ -905,17 +932,17 @@ class UploadService {
     private async putFile(
         fileUploadURL: UploadURL,
         file: Uint8Array,
-        filename: string,
+        filename: string
     ): Promise<string> {
         try {
-            await retryAsyncFunction(()=>
+            await retryAsyncFunction(() =>
                 HTTPService.put(
                     fileUploadURL.url,
                     file,
                     null,
                     null,
-                    this.trackUploadProgress(filename),
-                ),
+                    this.trackUploadProgress(filename)
+                )
             );
             return fileUploadURL.objectKey;
         } catch (e) {
@@ -928,12 +955,12 @@ class UploadService {
         multipartUploadURLs: MultipartUploadURLs,
         file: ReadableStream<Uint8Array>,
         filename: string,
-        uploadPartCount: number,
+        uploadPartCount: number
     ) {
         try {
             const streamEncryptedFileReader = file.getReader();
             const percentPerPart = Math.round(
-                RANDOM_PERCENTAGE_PROGRESS_FOR_PUT() / uploadPartCount,
+                RANDOM_PERCENTAGE_PROGRESS_FOR_PUT() / uploadPartCount
             );
             const resParts = [];
             for (const [
@@ -952,16 +979,20 @@ class UploadService {
                     }
                 }
                 const uploadChunk = Uint8Array.from(combinedChunks);
-                const response=await retryAsyncFunction(async ()=>{
-                    const resp =await HTTPService.put(
+                const response = await retryAsyncFunction(async () => {
+                    const resp = await HTTPService.put(
                         fileUploadURL,
                         uploadChunk,
                         null,
                         null,
-                        this.trackUploadProgress(filename, percentPerPart, index),
+                        this.trackUploadProgress(
+                            filename,
+                            percentPerPart,
+                            index
+                        )
                     );
                     if (!resp?.headers?.etag) {
-                        const err=Error(CustomError.ETAG_MISSING);
+                        const err = Error(CustomError.ETAG_MISSING);
                         logError(err);
                         throw err;
                     }
@@ -975,12 +1006,12 @@ class UploadService {
             const options = { compact: true, ignoreComment: true, spaces: 4 };
             const body = convert.js2xml(
                 { CompleteMultipartUpload: { Part: resParts } },
-                options,
+                options
             );
-            await retryAsyncFunction(()=>
+            await retryAsyncFunction(() =>
                 HTTPService.post(multipartUploadURLs.completeURL, body, null, {
                     'content-type': 'text/xml',
-                }),
+                })
             );
             return multipartUploadURLs.objectKey;
         } catch (e) {
@@ -992,15 +1023,15 @@ class UploadService {
     private trackUploadProgress(
         filename,
         percentPerPart = RANDOM_PERCENTAGE_PROGRESS_FOR_PUT(),
-        index = 0,
+        index = 0
     ) {
-        const cancel={ exec: null };
-        let timeout=null;
-        const resetTimeout=()=>{
+        const cancel = { exec: null };
+        let timeout = null;
+        const resetTimeout = () => {
             if (timeout) {
                 clearTimeout(timeout);
             }
-            timeout=setTimeout(()=>cancel.exec(), 30*1000);
+            timeout = setTimeout(() => cancel.exec(), 30 * 1000);
         };
         return {
             cancel,
@@ -1011,14 +1042,14 @@ class UploadService {
                         Math.min(
                             Math.round(
                                 percentPerPart * index +
-                                (percentPerPart * event.loaded) /
-                                event.total,
+                                    (percentPerPart * event.loaded) /
+                                        event.total
                             ),
-                            98,
-                        ),
+                            98
+                        )
                     );
                 this.updateProgressBarUI();
-                if (event.loaded===event.total) {
+                if (event.loaded === event.total) {
                     clearTimeout(timeout);
                 } else {
                     resetTimeout();
@@ -1029,7 +1060,7 @@ class UploadService {
     private async getExifData(
         reader: FileReader,
         receivedFile: globalThis.File,
-        fileType: FILE_TYPE,
+        fileType: FILE_TYPE
     ): Promise<ParsedEXIFData> {
         try {
             if (fileType === FILE_TYPE.VIDEO) {
@@ -1055,15 +1086,16 @@ class UploadService {
         }
     }
     private getUNIXTime(exifData: any) {
-        const dateString: string = exifData.DateTimeOriginal || exifData.DateTime;
-        if (!dateString || dateString==='0000:00:00 00:00:00') {
+        const dateString: string =
+            exifData.DateTimeOriginal || exifData.DateTime;
+        if (!dateString || dateString === '0000:00:00 00:00:00') {
             return null;
         }
         const parts = dateString.split(' ')[0].split(':');
         const date = new Date(
             Number(parts[0]),
             Number(parts[1]) - 1,
-            Number(parts[2]),
+            Number(parts[2])
         );
         return date.getTime() * 1000;
     }
@@ -1088,14 +1120,14 @@ class UploadService {
             latDegree,
             latMinute,
             latSecond,
-            latDirection,
+            latDirection
         );
 
         const lonFinal = this.convertDMSToDD(
             lonDegree,
             lonMinute,
             lonSecond,
-            lonDirection,
+            lonDirection
         );
         return { latitude: latFinal * 1.0, longitude: lonFinal * 1.0 };
     }

+ 91 - 71
src/services/userService.ts

@@ -36,7 +36,7 @@ export interface EmailVerificationResponse {
     keyAttributes?: KeyAttributes;
     encryptedToken?: string;
     token?: string;
-    twoFactorSessionID: string
+    twoFactorSessionID: string;
 }
 
 export interface TwoFactorVerificationResponse {
@@ -47,27 +47,28 @@ export interface TwoFactorVerificationResponse {
 }
 
 export interface TwoFactorSecret {
-    secretCode: string
-    qrCode: string
+    secretCode: string;
+    qrCode: string;
 }
 
 export interface TwoFactorRecoveryResponse {
-    encryptedSecret: string
-    secretDecryptionNonce: string
+    encryptedSecret: string;
+    secretDecryptionNonce: string;
 }
 
-export interface UserDetails{
-    email:string;
-    usage:number;
-    fileCount:number;
-    sharedCollectionCount:number;
-    subscription:Subscription;
+export interface UserDetails {
+    email: string;
+    usage: number;
+    fileCount: number;
+    sharedCollectionCount: number;
+    subscription: Subscription;
 }
 
-export const getOtt = (email: string) => HTTPService.get(`${ENDPOINT}/users/ott`, {
-    email,
-    client: 'web',
-});
+export const getOtt = (email: string) =>
+    HTTPService.get(`${ENDPOINT}/users/ott`, {
+        email,
+        client: 'web',
+    });
 export const getPublicKey = async (email: string) => {
     const token = getToken();
 
@@ -76,34 +77,28 @@ export const getPublicKey = async (email: string) => {
         { email },
         {
             'X-Auth-Token': token,
-        },
+        }
     );
     return resp.data.publicKey;
 };
 
-export const verifyOtt = (email: string, ott: string) => HTTPService.post(`${ENDPOINT}/users/verify-email`, { email, ott });
+export const verifyOtt = (email: string, ott: string) =>
+    HTTPService.post(`${ENDPOINT}/users/verify-email`, { email, ott });
+
+export const putAttributes = (token: string, keyAttributes: KeyAttributes) =>
+    HTTPService.put(`${ENDPOINT}/users/attributes`, { keyAttributes }, null, {
+        'X-Auth-Token': token,
+    });
 
-export const putAttributes = (token: string, keyAttributes: KeyAttributes) => HTTPService.put(
-    `${ENDPOINT}/users/attributes`,
-    { keyAttributes },
-    null,
-    {
+export const setKeys = (token: string, updatedKey: UpdatedKey) =>
+    HTTPService.put(`${ENDPOINT}/users/keys`, updatedKey, null, {
         'X-Auth-Token': token,
-    },
-);
-
-export const setKeys = (token: string, updatedKey: UpdatedKey) => HTTPService.put(`${ENDPOINT}/users/keys`, updatedKey, null, {
-    'X-Auth-Token': token,
-});
-
-export const setRecoveryKey = (token: string, recoveryKey: RecoveryKey) => HTTPService.put(
-    `${ENDPOINT}/users/recovery-key`,
-    recoveryKey,
-    null,
-    {
+    });
+
+export const setRecoveryKey = (token: string, recoveryKey: RecoveryKey) =>
+    HTTPService.put(`${ENDPOINT}/users/recovery-key`, recoveryKey, null, {
         'X-Auth-Token': token,
-    },
-);
+    });
 
 export const logoutUser = async () => {
     // ignore server logout result as logoutUser can be triggered before sign up or on token expiry
@@ -131,26 +126,46 @@ export const isTokenValid = async () => {
 };
 
 export const setupTwoFactor = async () => {
-    const resp = await HTTPService.post(`${ENDPOINT}/users/two-factor/setup`, null, null, {
-        'X-Auth-Token': getToken(),
-    });
+    const resp = await HTTPService.post(
+        `${ENDPOINT}/users/two-factor/setup`,
+        null,
+        null,
+        {
+            'X-Auth-Token': getToken(),
+        }
+    );
     return resp.data as TwoFactorSecret;
 };
 
-export const enableTwoFactor = async (code: string, recoveryEncryptedTwoFactorSecret: B64EncryptionResult) => {
-    await HTTPService.post(`${ENDPOINT}/users/two-factor/enable`, {
-        code,
-        encryptedTwoFactorSecret: recoveryEncryptedTwoFactorSecret.encryptedData,
-        twoFactorSecretDecryptionNonce: recoveryEncryptedTwoFactorSecret.nonce,
-    }, null, {
-        'X-Auth-Token': getToken(),
-    });
+export const enableTwoFactor = async (
+    code: string,
+    recoveryEncryptedTwoFactorSecret: B64EncryptionResult
+) => {
+    await HTTPService.post(
+        `${ENDPOINT}/users/two-factor/enable`,
+        {
+            code,
+            encryptedTwoFactorSecret:
+                recoveryEncryptedTwoFactorSecret.encryptedData,
+            twoFactorSecretDecryptionNonce:
+                recoveryEncryptedTwoFactorSecret.nonce,
+        },
+        null,
+        {
+            'X-Auth-Token': getToken(),
+        }
+    );
 };
 
 export const verifyTwoFactor = async (code: string, sessionID: string) => {
-    const resp = await HTTPService.post(`${ENDPOINT}/users/two-factor/verify`, {
-        code, sessionID,
-    }, null);
+    const resp = await HTTPService.post(
+        `${ENDPOINT}/users/two-factor/verify`,
+        {
+            code,
+            sessionID,
+        },
+        null
+    );
     return resp.data as TwoFactorVerificationResponse;
 };
 
@@ -163,7 +178,8 @@ export const recoverTwoFactor = async (sessionID: string) => {
 
 export const removeTwoFactor = async (sessionID: string, secret: string) => {
     const resp = await HTTPService.post(`${ENDPOINT}/users/two-factor/remove`, {
-        sessionID, secret,
+        sessionID,
+        secret,
     });
     return resp.data as TwoFactorVerificationResponse;
 };
@@ -175,9 +191,13 @@ export const disableTwoFactor = async () => {
 };
 
 export const getTwoFactorStatus = async () => {
-    const resp = await HTTPService.get(`${ENDPOINT}/users/two-factor/status`, null, {
-        'X-Auth-Token': getToken(),
-    });
+    const resp = await HTTPService.get(
+        `${ENDPOINT}/users/two-factor/status`,
+        null,
+        {
+            'X-Auth-Token': getToken(),
+        }
+    );
     return resp.data['status'];
 };
 
@@ -194,7 +214,7 @@ export const _logout = async () => {
     }
 };
 
-export const getOTTForEmailChange=async (email:string)=>{
+export const getOTTForEmailChange = async (email: string) => {
     if (!getToken()) {
         return null;
     }
@@ -205,28 +225,28 @@ export const getOTTForEmailChange=async (email:string)=>{
     });
 };
 
-
-export const changeEmail=async (email:string, ott:string)=>{
+export const changeEmail = async (email: string, ott: string) => {
     if (!getToken()) {
         return null;
     }
-    await HTTPService.post(`${ENDPOINT}/users/change-email`, {
-        email,
-        ott,
-    }, null, {
-        'X-Auth-Token': getToken(),
-    });
+    await HTTPService.post(
+        `${ENDPOINT}/users/change-email`,
+        {
+            email,
+            ott,
+        },
+        null,
+        {
+            'X-Auth-Token': getToken(),
+        }
+    );
 };
 
-export const getUserDetails = async ():Promise<UserDetails> => {
+export const getUserDetails = async (): Promise<UserDetails> => {
     const token = getToken();
 
-    const resp = await HTTPService.get(
-        `${ENDPOINT}/users/details`,
-        null,
-        {
-            'X-Auth-Token': token,
-        },
-    );
+    const resp = await HTTPService.get(`${ENDPOINT}/users/details`, null, {
+        'X-Auth-Token': token,
+    });
     return resp.data['details'];
 };

+ 2 - 1
src/types.ts

@@ -17,6 +17,7 @@ export const ENCRYPTION_CHUNK_SIZE = 4 * 1024 * 1024;
 export const GAP_BTW_TILES = 4;
 export const DATE_CONTAINER_HEIGHT = 48;
 export const IMAGE_CONTAINER_MAX_HEIGHT = 200;
-export const IMAGE_CONTAINER_MAX_WIDTH = IMAGE_CONTAINER_MAX_HEIGHT - GAP_BTW_TILES;
+export const IMAGE_CONTAINER_MAX_WIDTH =
+    IMAGE_CONTAINER_MAX_HEIGHT - GAP_BTW_TILES;
 export const MIN_COLUMNS = 4;
 export const SPACE_BTW_DATES = 44;

+ 15 - 14
src/utils/billingUtil.ts

@@ -17,14 +17,14 @@ export function convertBytesToGBs(bytes, precision?): string {
     return (bytes / (1024 * 1024 * 1024)).toFixed(precision ?? 2);
 }
 
-export function convertToHumanReadable(bytes:number, precision=2): string {
-    if (bytes===0) {
+export function convertToHumanReadable(bytes: number, precision = 2): string {
+    if (bytes === 0) {
         return '0 MB';
     }
     const i = Math.floor(Math.log(bytes) / Math.log(1024));
     const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
 
-    return (bytes / Math.pow(1024, i)).toFixed(precision)+ ' ' + sizes[i];
+    return (bytes / Math.pow(1024, i)).toFixed(precision) + ' ' + sizes[i];
 }
 
 export function hasPaidSubscription(subscription?: Subscription) {
@@ -89,7 +89,7 @@ export async function updateSubscription(
     plan: Plan,
     setDialogMessage: SetDialogMessage,
     setLoading: SetLoading,
-    closePlanSelectorModal: () => null,
+    closePlanSelectorModal: () => null
 ) {
     try {
         setLoading(true);
@@ -99,7 +99,7 @@ export async function updateSubscription(
         setDialogMessage({
             title: constants.SUCCESS,
             content: constants.SUBSCRIPTION_PURCHASE_SUCCESS(
-                getUserSubscription().expiryTime,
+                getUserSubscription().expiryTime
             ),
             close: { variant: 'success' },
         });
@@ -117,7 +117,7 @@ export async function updateSubscription(
                             null,
 
                             setDialogMessage,
-                            setLoading,
+                            setLoading
                         ),
                     },
                     close: { text: constants.CANCEL },
@@ -146,7 +146,7 @@ export async function updateSubscription(
 export async function cancelSubscription(
     setDialogMessage: SetDialogMessage,
     closePlanSelectorModal: () => null,
-    setLoading: SetLoading,
+    setLoading: SetLoading
 ) {
     try {
         setLoading(true);
@@ -171,7 +171,7 @@ export async function cancelSubscription(
 export async function activateSubscription(
     setDialogMessage: SetDialogMessage,
     closePlanSelectorModal: () => null,
-    setLoading: SetLoading,
+    setLoading: SetLoading
 ) {
     try {
         setLoading(true);
@@ -195,7 +195,7 @@ export async function activateSubscription(
 
 export async function updatePaymentMethod(
     setDialogMessage: SetDialogMessage,
-    setLoading: SetLoading,
+    setLoading: SetLoading
 ) {
     try {
         setLoading(true);
@@ -213,7 +213,7 @@ export async function updatePaymentMethod(
 
 export async function checkSubscriptionPurchase(
     setDialogMessage: SetDialogMessage,
-    router: NextRouter,
+    router: NextRouter
 ) {
     try {
         const urlParams = new URLSearchParams(window.location.search);
@@ -227,13 +227,13 @@ export async function checkSubscriptionPurchase(
         } else if (sessionId) {
             try {
                 const subscription = await billingService.verifySubscription(
-                    sessionId,
+                    sessionId
                 );
                 setDialogMessage({
                     title: constants.SUBSCRIPTION_PURCHASE_SUCCESS_TITLE,
                     close: { variant: 'success' },
                     content: constants.SUBSCRIPTION_PURCHASE_SUCCESS(
-                        subscription?.expiryTime,
+                        subscription?.expiryTime
                     ),
                 });
             } catch (e) {
@@ -251,11 +251,12 @@ export async function checkSubscriptionPurchase(
     }
 }
 
-export function planForSubscription(subscription:Subscription) {
+export function planForSubscription(subscription: Subscription) {
     if (!subscription) {
         return null;
     }
-    return { id: subscription.productID,
+    return {
+        id: subscription.productID,
         storage: subscription.storage,
         price: subscription.price,
         period: subscription.period,

+ 2 - 2
src/utils/collection/index.ts

@@ -15,14 +15,14 @@ export async function addFilesToCollection(
     syncWithRemote: () => Promise<void>,
     selectCollection: (id: number) => void,
     collectionName: string,
-    existingCollection: Collection,
+    existingCollection: Collection
 ) {
     setCollectionSelectorView(false);
     let collection;
     if (!existingCollection) {
         collection = await createCollection(
             collectionName,
-            CollectionType.album,
+            CollectionType.album
         );
     } else {
         collection = existingCollection;

+ 10 - 3
src/utils/common/apiUtil.ts

@@ -1,18 +1,25 @@
 export const getEndpoint = () => {
-    const endPoint = process.env.NEXT_PUBLIC_ENTE_ENDPOINT ?? 'https://api.ente.io';
+    const endPoint =
+        process.env.NEXT_PUBLIC_ENTE_ENDPOINT ?? 'https://api.ente.io';
     return endPoint;
 };
 
 export const getFileUrl = (id: number) => {
     if (process.env.NEXT_PUBLIC_ENTE_ENDPOINT !== undefined) {
-        return `${process.env.NEXT_PUBLIC_ENTE_ENDPOINT}/files/download/${id}` ?? 'https://api.ente.io';
+        return (
+            `${process.env.NEXT_PUBLIC_ENTE_ENDPOINT}/files/download/${id}` ??
+            'https://api.ente.io'
+        );
     }
     return `https://files.ente.workers.dev/?fileID=${id}`;
 };
 
 export const getThumbnailUrl = (id: number) => {
     if (process.env.NEXT_PUBLIC_ENTE_ENDPOINT !== undefined) {
-        return `${process.env.NEXT_PUBLIC_ENTE_ENDPOINT}/files/preview/${id}` ?? 'https://api.ente.io';
+        return (
+            `${process.env.NEXT_PUBLIC_ENTE_ENDPOINT}/files/preview/${id}` ??
+            'https://api.ente.io'
+        );
     }
     return `https://thumbnails.ente.workers.dev/?fileID=${id}`;
 };

+ 5 - 6
src/utils/common/errorUtil.ts

@@ -7,8 +7,7 @@ export const ServerErrorCodes = {
     STORAGE_LIMIT_EXCEEDED: '426',
 };
 
-
-export const CustomError ={
+export const CustomError = {
     SUBSCRIPTION_VERIFICATION_ERROR: 'Subscription verification failed',
     THUMBNAIL_GENERATION_FAILED: 'thumbnail generation failed',
     VIDEO_PLAYBACK_FAILED: 'video playback failed',
@@ -16,7 +15,6 @@ export const CustomError ={
     KEY_MISSING: 'encrypted key missing from localStorage',
 };
 
-
 export function parseError(error) {
     let parsedMessage = null;
     if (error?.status) {
@@ -36,9 +34,10 @@ export function parseError(error) {
     if (parsedMessage) {
         return { parsedError: new Error(parsedMessage), parsed: true };
     } else {
-        return ({
-            parsedError: new Error(`${constants.UNKNOWN_ERROR} ${error}`), parsed: false,
-        });
+        return {
+            parsedError: new Error(`${constants.UNKNOWN_ERROR} ${error}`),
+            parsed: false,
+        };
     }
 }
 

+ 6 - 2
src/utils/common/index.ts

@@ -1,6 +1,7 @@
 import constants from 'utils/strings/constants';
 
-export const DESKTOP_APP_DOWNLOAD_URL = 'https://github.com/ente-io/bhari-frame/releases/latest';
+export const DESKTOP_APP_DOWNLOAD_URL =
+    'https://github.com/ente-io/bhari-frame/releases/latest';
 
 const retrySleepTime = [2000, 5000, 10000];
 
@@ -32,7 +33,10 @@ export function reverseString(title: string) {
         .reduce((reversedString, currWord) => `${currWord} ${reversedString}`);
 }
 
-export async function retryAsyncFunction(func: ()=>Promise<any>, retryCount: number = 3) {
+export async function retryAsyncFunction(
+    func: () => Promise<any>,
+    retryCount: number = 3
+) {
     try {
         const resp = await func();
         return resp;

+ 4 - 3
src/utils/common/key.ts

@@ -7,14 +7,14 @@ import { CustomError } from './errorUtil';
 export const getActualKey = async () => {
     try {
         const encryptionKeyAttributes: B64EncryptionResult = getKey(
-            SESSION_KEYS.ENCRYPTION_KEY,
+            SESSION_KEYS.ENCRYPTION_KEY
         );
 
         const cryptoWorker = await new CryptoWorker();
         const key: string = await cryptoWorker.decryptB64(
             encryptionKeyAttributes.encryptedData,
             encryptionKeyAttributes.nonce,
-            encryptionKeyAttributes.key,
+            encryptionKeyAttributes.key
         );
         return key;
     } catch (e) {
@@ -22,7 +22,8 @@ export const getActualKey = async () => {
     }
 };
 
-export const getStripePublishableKey = () => process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ??
+export const getStripePublishableKey = () =>
+    process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ??
     'pk_live_51HAhqDK59oeucIMOiTI6MDDM2UWUbCAJXJCGsvjJhiO8nYJz38rQq5T4iyQLDMKxqEDUfU5Hopuj4U5U4dff23oT00fHvZeodC';
 
 export const getToken = () => getData(LS_KEYS.USER)?.token;

+ 33 - 18
src/utils/crypto/index.ts

@@ -16,7 +16,9 @@ export interface ComlinkWorker {
 
 export const getDedicatedCryptoWorker = (): ComlinkWorker => {
     if (runningInBrowser()) {
-        const worker = new Worker(new URL('worker/crypto.worker.js', import.meta.url));
+        const worker = new Worker(
+            new URL('worker/crypto.worker.js', import.meta.url)
+        );
         const comlink = Comlink.wrap(worker);
         return { comlink, worker };
     }
@@ -24,7 +26,7 @@ export const getDedicatedCryptoWorker = (): ComlinkWorker => {
 const CryptoWorker: any = getDedicatedCryptoWorker()?.comlink;
 
 export async function generateKeyAttributes(
-    passphrase: string,
+    passphrase: string
 ): Promise<{ keyAttributes: KeyAttributes; masterKey: string }> {
     const cryptoWorker = await new CryptoWorker();
     const masterKey: string = await cryptoWorker.generateEncryptionKey();
@@ -32,12 +34,16 @@ export async function generateKeyAttributes(
     const kekSalt: string = await cryptoWorker.generateSaltToDeriveKey();
     const kek = await cryptoWorker.deriveSensitiveKey(passphrase, kekSalt);
 
-    const masterKeyEncryptedWithKek: B64EncryptionResult = await cryptoWorker.encryptToB64(masterKey, kek.key);
-    const masterKeyEncryptedWithRecoveryKey: B64EncryptionResult = await cryptoWorker.encryptToB64(masterKey, recoveryKey);
-    const recoveryKeyEncryptedWithMasterKey: B64EncryptionResult = await cryptoWorker.encryptToB64(recoveryKey, masterKey);
+    const masterKeyEncryptedWithKek: B64EncryptionResult =
+        await cryptoWorker.encryptToB64(masterKey, kek.key);
+    const masterKeyEncryptedWithRecoveryKey: B64EncryptionResult =
+        await cryptoWorker.encryptToB64(masterKey, recoveryKey);
+    const recoveryKeyEncryptedWithMasterKey: B64EncryptionResult =
+        await cryptoWorker.encryptToB64(recoveryKey, masterKey);
 
     const keyPair = await cryptoWorker.generateKeyPair();
-    const encryptedKeyPairAttributes: B64EncryptionResult = await cryptoWorker.encryptToB64(keyPair.privateKey, masterKey);
+    const encryptedKeyPairAttributes: B64EncryptionResult =
+        await cryptoWorker.encryptToB64(keyPair.privateKey, masterKey);
 
     const keyAttributes: KeyAttributes = {
         kekSalt,
@@ -62,15 +68,17 @@ export async function generateKeyAttributes(
 export async function generateAndSaveIntermediateKeyAttributes(
     passphrase,
     existingKeyAttributes,
-    key,
+    key
 ): Promise<KeyAttributes> {
     const cryptoWorker = await new CryptoWorker();
-    const intermediateKekSalt: string = await cryptoWorker.generateSaltToDeriveKey();
+    const intermediateKekSalt: string =
+        await cryptoWorker.generateSaltToDeriveKey();
     const intermediateKek: KEK = await cryptoWorker.deriveIntermediateKey(
         passphrase,
-        intermediateKekSalt,
+        intermediateKekSalt
     );
-    const encryptedKeyAttributes: B64EncryptionResult = await cryptoWorker.encryptToB64(key, intermediateKek.key);
+    const encryptedKeyAttributes: B64EncryptionResult =
+        await cryptoWorker.encryptToB64(key, intermediateKek.key);
 
     const intermediateKeyAttributes = Object.assign(existingKeyAttributes, {
         kekSalt: intermediateKekSalt,
@@ -104,7 +112,7 @@ export const getRecoveryKey = async () => {
             recoveryKey = await cryptoWorker.decryptB64(
                 recoveryKeyEncryptedWithMasterKey,
                 recoveryKeyDecryptionNonce,
-                masterKey,
+                masterKey
             );
         } else {
             recoveryKey = await createNewRecoveryKey();
@@ -123,8 +131,10 @@ async function createNewRecoveryKey() {
     const cryptoWorker = await new CryptoWorker();
 
     const recoveryKey = await cryptoWorker.generateEncryptionKey();
-    const encryptedMasterKey: B64EncryptionResult = await cryptoWorker.encryptToB64(masterKey, recoveryKey);
-    const encryptedRecoveryKey: B64EncryptionResult = await cryptoWorker.encryptToB64(recoveryKey, masterKey);
+    const encryptedMasterKey: B64EncryptionResult =
+        await cryptoWorker.encryptToB64(masterKey, recoveryKey);
+    const encryptedRecoveryKey: B64EncryptionResult =
+        await cryptoWorker.encryptToB64(recoveryKey, masterKey);
     const recoveryKeyAttributes = {
         masterKeyEncryptedWithRecoveryKey: encryptedMasterKey.encryptedData,
         masterKeyDecryptionNonce: encryptedMasterKey.nonce,
@@ -135,7 +145,7 @@ async function createNewRecoveryKey() {
 
     const updatedKeyAttributes = Object.assign(
         existingAttributes,
-        recoveryKeyAttributes,
+        recoveryKeyAttributes
     );
     setData(LS_KEYS.KEY_ATTRIBUTES, updatedKeyAttributes);
 
@@ -151,14 +161,16 @@ export async function decryptAndStoreToken(masterKey: string) {
         const secretKey = await cryptoWorker.decryptB64(
             keyAttributes.encryptedSecretKey,
             keyAttributes.secretKeyDecryptionNonce,
-            masterKey,
+            masterKey
         );
         const URLUnsafeB64DecryptedToken = await cryptoWorker.boxSealOpen(
             encryptedToken,
             keyAttributes.publicKey,
-            secretKey,
+            secretKey
+        );
+        const decryptedTokenBytes = await cryptoWorker.fromB64(
+            URLUnsafeB64DecryptedToken
         );
-        const decryptedTokenBytes = await cryptoWorker.fromB64(URLUnsafeB64DecryptedToken);
         decryptedToken = await cryptoWorker.toURLSafeB64(decryptedTokenBytes);
         setData(LS_KEYS.USER, {
             ...user,
@@ -172,7 +184,10 @@ export async function encryptWithRecoveryKey(key: string) {
     const cryptoWorker = await new CryptoWorker();
     const hexRecoveryKey = await getRecoveryKey();
     const recoveryKey = await cryptoWorker.fromHex(hexRecoveryKey);
-    const encryptedKey: B64EncryptionResult = await cryptoWorker.encryptToB64(key, recoveryKey);
+    const encryptedKey: B64EncryptionResult = await cryptoWorker.encryptToB64(
+        key,
+        recoveryKey
+    );
     return encryptedKey;
 }
 export default CryptoWorker;

+ 36 - 37
src/utils/crypto/libsodium.ts

@@ -4,17 +4,17 @@ import { ENCRYPTION_CHUNK_SIZE } from 'types';
 export async function decryptChaChaOneShot(
     data: Uint8Array,
     header: Uint8Array,
-    key: string,
+    key: string
 ) {
     await sodium.ready;
     const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
         header,
-        await fromB64(key),
+        await fromB64(key)
     );
     const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
         pullState,
         data,
-        null,
+        null
     );
     return pullResult.message;
 }
@@ -22,14 +22,15 @@ export async function decryptChaChaOneShot(
 export async function decryptChaCha(
     data: Uint8Array,
     header: Uint8Array,
-    key: string,
+    key: string
 ) {
     await sodium.ready;
     const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
         header,
-        await fromB64(key),
+        await fromB64(key)
     );
-    const decryptionChunkSize = ENCRYPTION_CHUNK_SIZE +
+    const decryptionChunkSize =
+        ENCRYPTION_CHUNK_SIZE +
         sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
     let bytesRead = 0;
     const decryptedData = [];
@@ -42,7 +43,7 @@ export async function decryptChaCha(
         const buffer = data.slice(bytesRead, bytesRead + chunkSize);
         const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
             pullState,
-            buffer,
+            buffer
         );
         for (let index = 0; index < pullResult.message.length; index++) {
             decryptedData.push(pullResult.message[index]);
@@ -57,9 +58,10 @@ export async function initChunkDecryption(header: Uint8Array, key: Uint8Array) {
     await sodium.ready;
     const pullState = sodium.crypto_secretstream_xchacha20poly1305_init_pull(
         header,
-        key,
+        key
     );
-    const decryptionChunkSize = ENCRYPTION_CHUNK_SIZE +
+    const decryptionChunkSize =
+        ENCRYPTION_CHUNK_SIZE +
         sodium.crypto_secretstream_xchacha20poly1305_ABYTES;
     const tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
     return { pullState, decryptionChunkSize, tag };
@@ -69,7 +71,7 @@ export async function decryptChunk(data: Uint8Array, pullState: StateAddress) {
     await sodium.ready;
     const pullResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
         pullState,
-        data,
+        data
     );
     const newTag = pullResult.tag;
     return { decryptedData: pullResult.message, newTag };
@@ -81,16 +83,15 @@ export async function encryptChaChaOneShot(data: Uint8Array, key?: string) {
     const uintkey: Uint8Array = key
         ? await fromB64(key)
         : sodium.crypto_secretstream_xchacha20poly1305_keygen();
-    const initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(
-        uintkey,
-    );
+    const initPushResult =
+        sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey);
     const [pushState, header] = [initPushResult.state, initPushResult.header];
 
     const pushResult = sodium.crypto_secretstream_xchacha20poly1305_push(
         pushState,
         data,
         null,
-        sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL,
+        sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
     );
     return {
         key: await toB64(uintkey),
@@ -108,9 +109,8 @@ export async function encryptChaCha(data: Uint8Array, key?: string) {
         ? await fromB64(key)
         : sodium.crypto_secretstream_xchacha20poly1305_keygen();
 
-    const initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(
-        uintkey,
-    );
+    const initPushResult =
+        sodium.crypto_secretstream_xchacha20poly1305_init_push(uintkey);
     const [pushState, header] = [initPushResult.state, initPushResult.header];
     let bytesRead = 0;
     let tag = sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE;
@@ -130,7 +130,7 @@ export async function encryptChaCha(data: Uint8Array, key?: string) {
             pushState,
             buffer,
             null,
-            tag,
+            tag
         );
         for (let index = 0; index < pushResult.length; index++) {
             encryptedData.push(pushResult[index]);
@@ -148,9 +148,8 @@ export async function encryptChaCha(data: Uint8Array, key?: string) {
 export async function initChunkEncryption() {
     await sodium.ready;
     const key = sodium.crypto_secretstream_xchacha20poly1305_keygen();
-    const initPushResult = sodium.crypto_secretstream_xchacha20poly1305_init_push(
-        key,
-    );
+    const initPushResult =
+        sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
     const [pushState, header] = [initPushResult.state, initPushResult.header];
     return {
         key: await toB64(key),
@@ -161,7 +160,7 @@ export async function initChunkEncryption() {
 export async function encryptFileChunk(
     data: Uint8Array,
     pushState: sodium.StateAddress,
-    finalChunk?: boolean,
+    finalChunk?: boolean
 ) {
     await sodium.ready;
 
@@ -172,7 +171,7 @@ export async function encryptFileChunk(
         pushState,
         data,
         null,
-        tag,
+        tag
     );
 
     return pushResult;
@@ -181,7 +180,7 @@ export async function encryptToB64(data: string, key?: string) {
     await sodium.ready;
     const encrypted = await encrypt(
         await fromB64(data),
-        key ? await fromB64(key) : null,
+        key ? await fromB64(key) : null
     );
 
     return {
@@ -200,7 +199,7 @@ export async function decryptB64(data: string, nonce: string, key: string) {
     const decrypted = await decrypt(
         await fromB64(data),
         await fromB64(nonce),
-        await fromB64(key),
+        await fromB64(key)
     );
 
     return await toB64(decrypted);
@@ -211,7 +210,7 @@ export async function decryptToUTF8(data: string, nonce: string, key: string) {
     const decrypted = await decrypt(
         await fromB64(data),
         await fromB64(nonce),
-        await fromB64(key),
+        await fromB64(key)
     );
 
     return sodium.to_string(decrypted);
@@ -232,7 +231,7 @@ export async function encrypt(data: Uint8Array, key?: Uint8Array) {
 export async function decrypt(
     data: Uint8Array,
     nonce: Uint8Array,
-    key: Uint8Array,
+    key: Uint8Array
 ) {
     await sodium.ready;
     return sodium.crypto_secretbox_open_easy(data, nonce, key);
@@ -248,7 +247,7 @@ export async function hash(input: string) {
     return sodium.crypto_pwhash_str(
         await fromB64(input),
         sodium.crypto_pwhash_OPSLIMIT_SENSITIVE,
-        sodium.crypto_pwhash_MEMLIMIT_MODERATE,
+        sodium.crypto_pwhash_MEMLIMIT_MODERATE
     );
 }
 
@@ -256,7 +255,7 @@ export async function deriveKey(
     passphrase: string,
     salt: string,
     opsLimit: number,
-    memLimit: number,
+    memLimit: number
 ) {
     await sodium.ready;
     return await toB64(
@@ -266,8 +265,8 @@ export async function deriveKey(
             await fromB64(salt),
             opsLimit,
             memLimit,
-            sodium.crypto_pwhash_ALG_DEFAULT,
-        ),
+            sodium.crypto_pwhash_ALG_DEFAULT
+        )
     );
 }
 
@@ -300,8 +299,8 @@ export async function deriveIntermediateKey(passphrase: string, salt: string) {
             await fromB64(salt),
             sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
             sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
-            sodium.crypto_pwhash_ALG_DEFAULT,
-        ),
+            sodium.crypto_pwhash_ALG_DEFAULT
+        )
     );
     return {
         key,
@@ -332,22 +331,22 @@ export async function generateKeyPair() {
 export async function boxSealOpen(
     input: string,
     publicKey: string,
-    secretKey: string,
+    secretKey: string
 ) {
     await sodium.ready;
     return await toB64(
         sodium.crypto_box_seal_open(
             await fromB64(input),
             await fromB64(publicKey),
-            await fromB64(secretKey),
-        ),
+            await fromB64(secretKey)
+        )
     );
 }
 
 export async function boxSeal(input: string, publicKey: string) {
     await sodium.ready;
     return await toB64(
-        sodium.crypto_box_seal(await fromB64(input), await fromB64(publicKey)),
+        sodium.crypto_box_seal(await fromB64(input), await fromB64(publicKey))
     );
 }
 

+ 5 - 5
src/utils/export/index.ts

@@ -8,7 +8,7 @@ export const getExportRecordFileUID = (file: File) =>
 
 export const getExportPendingFiles = async (
     allFiles: File[],
-    exportRecord: ExportRecord,
+    exportRecord: ExportRecord
 ) => {
     const queuedFiles = new Set(exportRecord?.queuedFiles);
     const unExportedFiles = allFiles.filter((file) => {
@@ -21,7 +21,7 @@ export const getExportPendingFiles = async (
 
 export const getFilesUploadedAfterLastExport = async (
     allFiles: File[],
-    exportRecord: ExportRecord,
+    exportRecord: ExportRecord
 ) => {
     const exportedFiles = new Set(exportRecord?.exportedFiles);
     const unExportedFiles = allFiles.filter((file) => {
@@ -34,7 +34,7 @@ export const getFilesUploadedAfterLastExport = async (
 
 export const getExportFailedFiles = async (
     allFiles: File[],
-    exportRecord: ExportRecord,
+    exportRecord: ExportRecord
 ) => {
     const failedFiles = new Set(exportRecord?.failedFiles);
     const filesToExport = allFiles.filter((file) => {
@@ -53,7 +53,7 @@ export const dedupe = (files: any[]) => {
 
 export const getGoogleLikeMetadataFile = (
     uid: string,
-    metadata: MetadataObject,
+    metadata: MetadataObject
 ) => {
     const creationTime = metadata.creationTime / 1000000;
     const modificationTime = metadata.modificationTime / 1000000;
@@ -74,6 +74,6 @@ export const getGoogleLikeMetadataFile = (
             },
         },
         null,
-        2,
+        2
     );
 };

+ 18 - 14
src/utils/file/index.ts

@@ -66,9 +66,9 @@ export function getSelectedFiles(selectedFiles, files: File[]): File[] {
     return filesToDelete;
 }
 
-export function checkFileFormatSupport(name :string) {
+export function checkFileFormatSupport(name: string) {
     for (const format of UNSUPPORTED_FORMATS) {
-        if ( name.toLowerCase().endsWith(format)) {
+        if (name.toLowerCase().endsWith(format)) {
             throw Error('unsupported format');
         }
     }
@@ -97,7 +97,7 @@ export function formatDateTime(date: number | Date) {
     return `${dateTimeFormat.format(date)} ${timeFormat.format(date)}`;
 }
 
-export function sortFiles(files:File[]) {
+export function sortFiles(files: File[]) {
     // sort according to modification time first
     files = files.sort((a, b) => {
         if (!b.metadata?.modificationTime) {
@@ -111,29 +111,33 @@ export function sortFiles(files:File[]) {
     });
 
     // then sort according to creation time, maintaining ordering according to modification time for files with creation time
-    files = files.map((file, index) => ({ index, file })).sort((a, b) => {
-        let diff = b.file.metadata.creationTime - a.file.metadata.creationTime;
-        if (diff === 0) {
-            diff = a.index - b.index;
-        }
-        return diff;
-    }).map((file) => file.file);
+    files = files
+        .map((file, index) => ({ index, file }))
+        .sort((a, b) => {
+            let diff =
+                b.file.metadata.creationTime - a.file.metadata.creationTime;
+            if (diff === 0) {
+                diff = a.index - b.index;
+            }
+            return diff;
+        })
+        .map((file) => file.file);
     return files;
 }
 
-export async function decryptFile(file:File, collection:Collection) {
+export async function decryptFile(file: File, collection: Collection) {
     const worker = await new CryptoWorker();
     file.key = await worker.decryptB64(
         file.encryptedKey,
         file.keyDecryptionNonce,
-        collection.key,
+        collection.key
     );
     file.metadata = await worker.decryptMetadata(file);
     return file;
 }
 
-export function removeUnneccessaryFileProps(files:File[]):File[] {
-    const stripedFiles=files.map((file)=>{
+export function removeUnneccessaryFileProps(files: File[]): File[] {
+    const stripedFiles = files.map((file) => {
         delete file.src;
         delete file.msrc;
         delete file.file.objectKey;

+ 6 - 4
src/utils/search/index.ts

@@ -4,7 +4,7 @@ import { Bbox } from 'services/searchService';
 
 export function isInsideBox(
     file: { longitude: number; latitude: number },
-    bbox: Bbox,
+    bbox: Bbox
 ) {
     if (file.latitude === null && file.longitude === null) {
         return false;
@@ -37,10 +37,12 @@ export const isSameDay = (baseDate: DateValue) => (compareDate: Date) => {
 
 export function getFilesWithCreationDay(
     files: File[],
-    searchedDate: DateValue,
+    searchedDate: DateValue
 ) {
     const isSearchedDate = isSameDay(searchedDate);
-    return files.filter((file) => isSearchedDate(new Date(file.metadata.creationTime / 1000)));
+    return files.filter((file) =>
+        isSearchedDate(new Date(file.metadata.creationTime / 1000))
+    );
 }
 export function getFormattedDate(date: DateValue) {
     const options = {};
@@ -48,6 +50,6 @@ export function getFormattedDate(date: DateValue) {
     (date.month || date.month === 0) && (options['month'] = 'long');
     date.year && (options['year'] = 'numeric');
     return new Intl.DateTimeFormat('en-IN', options).format(
-        new Date(date.year ?? 1, date.month ?? 1, date.date ?? 1),
+        new Date(date.year ?? 1, date.month ?? 1, date.date ?? 1)
     );
 }

Some files were not shown because too many files changed in this diff