Merge branch 'ui-redesign' into upload-redesign
This commit is contained in:
commit
7eda72902b
|
@ -80,7 +80,7 @@
|
||||||
"@types/react-select": "^4.0.15",
|
"@types/react-select": "^4.0.15",
|
||||||
"@types/react-window": "^1.8.2",
|
"@types/react-window": "^1.8.2",
|
||||||
"@types/react-window-infinite-loader": "^1.0.3",
|
"@types/react-window-infinite-loader": "^1.0.3",
|
||||||
"@types/styled-components": "^5.1.3",
|
"@types/styled-components": "^5.1.25",
|
||||||
"@types/yup": "^0.29.7",
|
"@types/yup": "^0.29.7",
|
||||||
"babel-plugin-styled-components": "^1.11.1",
|
"babel-plugin-styled-components": "^1.11.1",
|
||||||
"eslint": "^7.27.0",
|
"eslint": "^7.27.0",
|
||||||
|
|
Binary file not shown.
Binary file not shown.
BIN
public/fonts/inter-v11-latin-700.woff
Normal file
BIN
public/fonts/inter-v11-latin-700.woff
Normal file
Binary file not shown.
BIN
public/fonts/inter-v11-latin-700.woff2
Normal file
BIN
public/fonts/inter-v11-latin-700.woff2
Normal file
Binary file not shown.
BIN
public/fonts/inter-v11-latin-900.woff
Normal file
BIN
public/fonts/inter-v11-latin-900.woff
Normal file
Binary file not shown.
BIN
public/fonts/inter-v11-latin-900.woff2
Normal file
BIN
public/fonts/inter-v11-latin-900.woff2
Normal file
Binary file not shown.
BIN
public/fonts/inter-v11-latin-regular.woff
Normal file
BIN
public/fonts/inter-v11-latin-regular.woff
Normal file
Binary file not shown.
BIN
public/fonts/inter-v11-latin-regular.woff2
Normal file
BIN
public/fonts/inter-v11-latin-regular.woff2
Normal file
Binary file not shown.
|
@ -1,5 +1,5 @@
|
||||||
import { Formik, FormikHelpers } from 'formik';
|
import { Formik, FormikHelpers } from 'formik';
|
||||||
import React, { useContext, useEffect, useRef, useState } from 'react';
|
import React, { useContext, useRef, useState } from 'react';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import SubmitButton from 'components/SubmitButton';
|
import SubmitButton from 'components/SubmitButton';
|
||||||
|
@ -11,27 +11,21 @@ import { PAGES } from 'constants/pages';
|
||||||
import { TextField } from '@mui/material';
|
import { TextField } from '@mui/material';
|
||||||
import Container from './Container';
|
import Container from './Container';
|
||||||
import LinkButton from './pages/gallery/LinkButton';
|
import LinkButton from './pages/gallery/LinkButton';
|
||||||
|
import { Alert } from 'react-bootstrap';
|
||||||
|
import FormPaperFooter from './Form/FormPaper/Footer';
|
||||||
|
|
||||||
interface formValues {
|
interface formValues {
|
||||||
email: string;
|
email: string;
|
||||||
ott?: string;
|
ott?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
function ChangeEmailForm() {
|
||||||
showMessage: (value: boolean) => void;
|
|
||||||
setEmail: (email: string) => void;
|
|
||||||
}
|
|
||||||
function ChangeEmailForm(props: Props) {
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [ottInputVisible, setShowOttInputVisibility] = useState(false);
|
const [ottInputVisible, setShowOttInputVisibility] = useState(false);
|
||||||
const ottInputRef = useRef(null);
|
const ottInputRef = useRef(null);
|
||||||
const appContext = useContext(AppContext);
|
const appContext = useContext(AppContext);
|
||||||
|
const [email, setEmail] = useState(null);
|
||||||
useEffect(() => {
|
const [showMessage, setShowMessage] = useState(false);
|
||||||
if (!ottInputVisible) {
|
|
||||||
props.showMessage(false);
|
|
||||||
}
|
|
||||||
}, [ottInputVisible]);
|
|
||||||
|
|
||||||
const requestOTT = async (
|
const requestOTT = async (
|
||||||
{ email }: formValues,
|
{ email }: formValues,
|
||||||
|
@ -40,9 +34,9 @@ function ChangeEmailForm(props: Props) {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await getOTTForEmailChange(email);
|
await getOTTForEmailChange(email);
|
||||||
props.setEmail(email);
|
setEmail(email);
|
||||||
setShowOttInputVisibility(true);
|
setShowOttInputVisibility(true);
|
||||||
props.showMessage(true);
|
setShowMessage(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
ottInputRef.current?.focus();
|
ottInputRef.current?.focus();
|
||||||
}, 250);
|
}, 250);
|
||||||
|
@ -71,6 +65,8 @@ function ChangeEmailForm(props: Props) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const goToGallery = () => router.push(PAGES.GALLERY);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Formik<formValues>
|
<Formik<formValues>
|
||||||
initialValues={{ email: '' }}
|
initialValues={{ email: '' }}
|
||||||
|
@ -78,23 +74,31 @@ function ChangeEmailForm(props: Props) {
|
||||||
email: Yup.string()
|
email: Yup.string()
|
||||||
.email(constants.EMAIL_ERROR)
|
.email(constants.EMAIL_ERROR)
|
||||||
.required(constants.REQUIRED),
|
.required(constants.REQUIRED),
|
||||||
|
ott:
|
||||||
|
ottInputVisible &&
|
||||||
|
Yup.string().required(constants.REQUIRED),
|
||||||
})}
|
})}
|
||||||
validateOnChange={false}
|
validateOnChange={false}
|
||||||
validateOnBlur={false}
|
validateOnBlur={false}
|
||||||
onSubmit={!ottInputVisible ? requestOTT : requestEmailChange}>
|
onSubmit={!ottInputVisible ? requestOTT : requestEmailChange}>
|
||||||
{({ values, errors, handleChange, handleSubmit }) => (
|
{({ values, errors, handleChange, handleSubmit }) => (
|
||||||
<>
|
<>
|
||||||
<form
|
<Alert
|
||||||
noValidate
|
variant="success"
|
||||||
style={{ width: '100%' }}
|
show={showMessage}
|
||||||
onSubmit={handleSubmit}>
|
style={{ paddingBottom: 0 }}
|
||||||
|
transition
|
||||||
|
dismissible
|
||||||
|
onClose={() => setShowMessage(false)}>
|
||||||
|
{constants.EMAIL_SENT({ email })}
|
||||||
|
</Alert>
|
||||||
|
<form noValidate onSubmit={handleSubmit}>
|
||||||
<Container>
|
<Container>
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
InputProps={{
|
InputProps={{
|
||||||
readOnly: ottInputVisible,
|
readOnly: ottInputVisible,
|
||||||
}}
|
}}
|
||||||
margin="normal"
|
|
||||||
type="email"
|
type="email"
|
||||||
label={constants.ENTER_EMAIL}
|
label={constants.ENTER_EMAIL}
|
||||||
value={values.email}
|
value={values.email}
|
||||||
|
@ -117,6 +121,7 @@ function ChangeEmailForm(props: Props) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
sx={{ mt: 2 }}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
buttonText={
|
buttonText={
|
||||||
!ottInputVisible
|
!ottInputVisible
|
||||||
|
@ -126,12 +131,23 @@ function ChangeEmailForm(props: Props) {
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
</form>
|
</form>
|
||||||
{ottInputVisible && (
|
|
||||||
<LinkButton
|
<FormPaperFooter
|
||||||
onClick={() => setShowOttInputVisibility(false)}>
|
style={{
|
||||||
{constants.CHANGE_EMAIL}?
|
justifyContent: ottInputVisible && 'space-between',
|
||||||
|
}}>
|
||||||
|
{ottInputVisible && (
|
||||||
|
<LinkButton
|
||||||
|
onClick={() =>
|
||||||
|
setShowOttInputVisibility(false)
|
||||||
|
}>
|
||||||
|
{constants.CHANGE_EMAIL}?
|
||||||
|
</LinkButton>
|
||||||
|
)}
|
||||||
|
<LinkButton onClick={goToGallery}>
|
||||||
|
{constants.GO_BACK}
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
)}
|
</FormPaperFooter>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
import styled from 'styled-components';
|
|
||||||
import { FreeFlowText, IconButton } from './Container';
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { Tooltip, OverlayTrigger } from 'react-bootstrap';
|
|
||||||
import EnteSpinner from './EnteSpinner';
|
|
||||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
|
||||||
import DoneIcon from '@mui/icons-material/Done';
|
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
|
||||||
position: relative;
|
|
||||||
margin: ${({ theme }) => theme.spacing(2)};
|
|
||||||
border-radius: ${({ theme }) => theme.shape.borderRadius}px;
|
|
||||||
`;
|
|
||||||
const CopyButtonWrapper = styled(IconButton)`
|
|
||||||
position: absolute;
|
|
||||||
top: 0px;
|
|
||||||
right: 0px;
|
|
||||||
background: none !important;
|
|
||||||
margin: 10px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const CodeWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: #1a1919;
|
|
||||||
padding: 37px 40px 20px 20px;
|
|
||||||
color: white;
|
|
||||||
background: ${({ theme }) => theme.palette.accent.dark};
|
|
||||||
border-radius: ${({ theme }) => theme.shape.borderRadius}px;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
type Iprops = React.PropsWithChildren<{
|
|
||||||
code: string;
|
|
||||||
wordBreak?: 'normal' | 'break-all' | 'keep-all' | 'break-word';
|
|
||||||
}>;
|
|
||||||
export const CodeBlock = (props: Iprops) => {
|
|
||||||
const [copied, setCopied] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const copyToClipboardHelper = (text: string) => () => {
|
|
||||||
navigator.clipboard.writeText(text);
|
|
||||||
setCopied(true);
|
|
||||||
setTimeout(() => setCopied(false), 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const RenderCopiedMessage = (props) => {
|
|
||||||
const { style, ...rest } = props;
|
|
||||||
return (
|
|
||||||
<Tooltip
|
|
||||||
{...rest}
|
|
||||||
style={{ ...style, zIndex: 2001 }}
|
|
||||||
id="button-tooltip">
|
|
||||||
copied
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Wrapper>
|
|
||||||
<CodeWrapper>
|
|
||||||
{props.code ? (
|
|
||||||
<FreeFlowText style={{ wordBreak: props.wordBreak }}>
|
|
||||||
{props.code}
|
|
||||||
</FreeFlowText>
|
|
||||||
) : (
|
|
||||||
<EnteSpinner />
|
|
||||||
)}
|
|
||||||
</CodeWrapper>
|
|
||||||
{props.code && (
|
|
||||||
<OverlayTrigger
|
|
||||||
show={copied}
|
|
||||||
placement="bottom"
|
|
||||||
trigger={'click'}
|
|
||||||
overlay={RenderCopiedMessage}
|
|
||||||
delay={{ show: 200, hide: 800 }}>
|
|
||||||
<CopyButtonWrapper
|
|
||||||
onClick={copyToClipboardHelper(props.code)}>
|
|
||||||
{copied ? (
|
|
||||||
<DoneIcon fontSize="small" />
|
|
||||||
) : (
|
|
||||||
<ContentCopyIcon fontSize="small" />
|
|
||||||
)}
|
|
||||||
</CopyButtonWrapper>
|
|
||||||
</OverlayTrigger>
|
|
||||||
)}
|
|
||||||
</Wrapper>
|
|
||||||
);
|
|
||||||
};
|
|
20
src/components/CodeBlock/CopyButton.tsx
Normal file
20
src/components/CodeBlock/CopyButton.tsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import React from 'react';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import { CopyButtonWrapper } from './styledComponents';
|
||||||
|
import DoneIcon from '@mui/icons-material/Done';
|
||||||
|
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||||
|
import { Tooltip } from '@mui/material';
|
||||||
|
|
||||||
|
export default function CopyButton({ code, copied, copyToClipboardHelper }) {
|
||||||
|
return (
|
||||||
|
<Tooltip arrow open={copied} title={constants.COPIED}>
|
||||||
|
<CopyButtonWrapper onClick={copyToClipboardHelper(code)}>
|
||||||
|
{copied ? (
|
||||||
|
<DoneIcon fontSize="small" />
|
||||||
|
) : (
|
||||||
|
<ContentCopyIcon fontSize="small" />
|
||||||
|
)}
|
||||||
|
</CopyButtonWrapper>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
47
src/components/CodeBlock/index.tsx
Normal file
47
src/components/CodeBlock/index.tsx
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { FreeFlowText } from '../Container';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import EnteSpinner from '../EnteSpinner';
|
||||||
|
import { Wrapper, CodeWrapper } from './styledComponents';
|
||||||
|
import CopyButton from './CopyButton';
|
||||||
|
import { BoxProps } from '@mui/material';
|
||||||
|
|
||||||
|
type Iprops = React.PropsWithChildren<{
|
||||||
|
code: string;
|
||||||
|
wordBreak?: 'normal' | 'break-all' | 'keep-all' | 'break-word';
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export default function CodeBlock({
|
||||||
|
code,
|
||||||
|
wordBreak,
|
||||||
|
...props
|
||||||
|
}: BoxProps<'div', Iprops>) {
|
||||||
|
const [copied, setCopied] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const copyToClipboardHelper = (text: string) => () => {
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
setCopied(true);
|
||||||
|
setTimeout(() => setCopied(false), 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!code) {
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<EnteSpinner />
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Wrapper {...props}>
|
||||||
|
<CodeWrapper>
|
||||||
|
<FreeFlowText style={{ wordBreak: wordBreak }}>
|
||||||
|
{code}
|
||||||
|
</FreeFlowText>
|
||||||
|
</CodeWrapper>
|
||||||
|
<CopyButton
|
||||||
|
code={code}
|
||||||
|
copied={copied}
|
||||||
|
copyToClipboardHelper={copyToClipboardHelper}
|
||||||
|
/>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
}
|
21
src/components/CodeBlock/styledComponents.tsx
Normal file
21
src/components/CodeBlock/styledComponents.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { IconButton } from '@mui/material';
|
||||||
|
import { CenteredFlex } from 'components/Container';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Wrapper = styled(CenteredFlex)`
|
||||||
|
position: relative;
|
||||||
|
background: ${({ theme }) => theme.palette.accent.dark};
|
||||||
|
border-radius: ${({ theme }) => theme.shape.borderRadius}px;
|
||||||
|
min-height: 80px;
|
||||||
|
`;
|
||||||
|
export const CopyButtonWrapper = styled(IconButton)`
|
||||||
|
position: absolute;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
margin: ${({ theme }) => theme.spacing(1)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CodeWrapper = styled.div`
|
||||||
|
padding: 36px 36px 16px 16px;
|
||||||
|
border-radius: ${({ theme }) => theme.shape.borderRadius}px;
|
||||||
|
`;
|
|
@ -1,114 +0,0 @@
|
||||||
import { CollectionSummaries } from 'types/collection';
|
|
||||||
|
|
||||||
interface Iprops {
|
|
||||||
isOpen: boolean;
|
|
||||||
close: () => void;
|
|
||||||
collectionSummaries: CollectionSummaries;
|
|
||||||
setActiveCollection: (id?: number) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import DialogContent from '@mui/material/DialogContent';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import constants from 'utils/strings/constants';
|
|
||||||
import { FlexWrapper, SpaceBetweenFlex } from 'components/Container';
|
|
||||||
import { LargerCollectionTile } from './styledComponents';
|
|
||||||
import CollectionCard from './CollectionCard';
|
|
||||||
import Divider from '@mui/material/Divider';
|
|
||||||
import CollectionSort from 'components/pages/gallery/CollectionSort';
|
|
||||||
import { CollectionType, COLLECTION_SORT_BY } from 'constants/collection';
|
|
||||||
import { sortCollectionSummaries } from 'services/collectionService';
|
|
||||||
import {
|
|
||||||
Transition,
|
|
||||||
FloatingDrawer,
|
|
||||||
} from 'components/Collections/FloatingDrawer';
|
|
||||||
import { useLocalState } from 'hooks/useLocalState';
|
|
||||||
import { LS_KEYS } from 'utils/storage/localStorage';
|
|
||||||
import DialogTitleWithCloseButton from 'components/MessageDialog/TitleWithCloseButton';
|
|
||||||
import { DialogTitle } from '@mui/material';
|
|
||||||
|
|
||||||
const LeftSlideTransition = Transition('up');
|
|
||||||
|
|
||||||
export default function AllCollections(props: Iprops) {
|
|
||||||
const { collectionSummaries, isOpen, close, setActiveCollection } = props;
|
|
||||||
|
|
||||||
const onCollectionClick = (collectionID: number) => {
|
|
||||||
setActiveCollection(collectionID);
|
|
||||||
close();
|
|
||||||
};
|
|
||||||
|
|
||||||
const [collectionSortBy, setCollectionSortBy] =
|
|
||||||
useLocalState<COLLECTION_SORT_BY>(
|
|
||||||
LS_KEYS.COLLECTION_SORT_BY,
|
|
||||||
COLLECTION_SORT_BY.UPDATION_TIME_DESCENDING
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<FloatingDrawer
|
|
||||||
position="right"
|
|
||||||
TransitionComponent={LeftSlideTransition}
|
|
||||||
onClose={close}
|
|
||||||
open={isOpen}>
|
|
||||||
<DialogTitleWithCloseButton onClose={close} sx={{ pb: 0 }}>
|
|
||||||
<Typography variant="h6">
|
|
||||||
<strong>{constants.ALL_ALBUMS}</strong>
|
|
||||||
</Typography>
|
|
||||||
</DialogTitleWithCloseButton>
|
|
||||||
<DialogTitle sx={{ pt: 0 }}>
|
|
||||||
<SpaceBetweenFlex>
|
|
||||||
<Typography variant="subtitle1">
|
|
||||||
{`${[...props.collectionSummaries.keys()].length} ${
|
|
||||||
constants.ALBUMS
|
|
||||||
}`}
|
|
||||||
</Typography>
|
|
||||||
<CollectionSort
|
|
||||||
activeSortBy={collectionSortBy}
|
|
||||||
setCollectionSortBy={setCollectionSortBy}
|
|
||||||
/>
|
|
||||||
</SpaceBetweenFlex>
|
|
||||||
</DialogTitle>
|
|
||||||
<Divider />
|
|
||||||
<DialogContent>
|
|
||||||
<FlexWrapper style={{ flexWrap: 'wrap' }}>
|
|
||||||
{sortCollectionSummaries(
|
|
||||||
[...collectionSummaries.values()].filter(
|
|
||||||
(x) =>
|
|
||||||
x.collectionAttributes.type !==
|
|
||||||
CollectionType.system
|
|
||||||
),
|
|
||||||
collectionSortBy
|
|
||||||
).map(
|
|
||||||
({
|
|
||||||
latestFile,
|
|
||||||
collectionAttributes,
|
|
||||||
fileCount,
|
|
||||||
}) => (
|
|
||||||
<CollectionCard
|
|
||||||
key={collectionAttributes.id}
|
|
||||||
latestFile={latestFile}
|
|
||||||
onClick={() =>
|
|
||||||
onCollectionClick(
|
|
||||||
collectionAttributes.id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
customCollectionTile={LargerCollectionTile}>
|
|
||||||
<div>
|
|
||||||
<Typography>
|
|
||||||
<strong>
|
|
||||||
{collectionAttributes.name}
|
|
||||||
</strong>
|
|
||||||
</Typography>
|
|
||||||
<Typography>
|
|
||||||
{fileCount} {constants.PHOTOS}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
</CollectionCard>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</FlexWrapper>
|
|
||||||
</DialogContent>
|
|
||||||
</FloatingDrawer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
37
src/components/Collections/AllCollections/CollectionCard.tsx
Normal file
37
src/components/Collections/AllCollections/CollectionCard.tsx
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Typography } from '@mui/material';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import React from 'react';
|
||||||
|
import CollectionCard from '../CollectionCard';
|
||||||
|
|
||||||
|
export default function AllCollectionCard({
|
||||||
|
onCollectionClick,
|
||||||
|
collectionAttributes,
|
||||||
|
latestFile,
|
||||||
|
fileCount,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<CollectionCard
|
||||||
|
large
|
||||||
|
latestFile={latestFile}
|
||||||
|
onClick={() => onCollectionClick(collectionAttributes.id)}>
|
||||||
|
<div>
|
||||||
|
<Typography
|
||||||
|
css={`
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 20px;
|
||||||
|
`}>
|
||||||
|
{collectionAttributes.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography
|
||||||
|
css={`
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 20px;
|
||||||
|
`}>
|
||||||
|
{fileCount} {constants.PHOTOS}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</CollectionCard>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { COLLECTION_SORT_BY } from 'constants/collection';
|
||||||
|
import Menu from '@mui/material/Menu';
|
||||||
|
import { IconButton, styled } from '@mui/material';
|
||||||
|
import SortIcon from '@mui/icons-material/Sort';
|
||||||
|
import CollectionSortOptions from './options';
|
||||||
|
|
||||||
|
export interface CollectionSortProps {
|
||||||
|
setCollectionSortBy: (sortBy: COLLECTION_SORT_BY) => void;
|
||||||
|
activeSortBy: COLLECTION_SORT_BY;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledMenu = styled(Menu)`
|
||||||
|
& .MuiPaper-root {
|
||||||
|
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.16);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function CollectionSort(props: CollectionSortProps) {
|
||||||
|
const [sortByEl, setSortByEl] = useState(null);
|
||||||
|
const handleClose = () => setSortByEl(null);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<IconButton
|
||||||
|
onClick={(event) => setSortByEl(event.currentTarget)}
|
||||||
|
aria-controls={sortByEl ? 'collection-sort' : undefined}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={sortByEl ? 'true' : undefined}>
|
||||||
|
<SortIcon />
|
||||||
|
</IconButton>
|
||||||
|
<StyledMenu
|
||||||
|
id="collection-sort"
|
||||||
|
anchorEl={sortByEl}
|
||||||
|
open={Boolean(sortByEl)}
|
||||||
|
onClose={handleClose}
|
||||||
|
MenuListProps={{
|
||||||
|
disablePadding: true,
|
||||||
|
'aria-labelledby': 'collection-sort',
|
||||||
|
}}>
|
||||||
|
<CollectionSortOptions {...props} close={handleClose} />
|
||||||
|
</StyledMenu>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { MenuItem, ListItemIcon, ListItemText } from '@mui/material';
|
||||||
|
import { COLLECTION_SORT_BY } from 'constants/collection';
|
||||||
|
import TickIcon from '@mui/icons-material/Done';
|
||||||
|
import { CollectionSortProps } from '.';
|
||||||
|
|
||||||
|
export interface SortOptionProps extends CollectionSortProps {
|
||||||
|
close: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SortByOptionCreator =
|
||||||
|
({ setCollectionSortBy, activeSortBy, close }: SortOptionProps) =>
|
||||||
|
(props: { sortBy: COLLECTION_SORT_BY; children: any }) => {
|
||||||
|
const handleClick = () => {
|
||||||
|
setCollectionSortBy(props.sortBy);
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<MenuItem onClick={handleClick} style={{ paddingLeft: '5px' }}>
|
||||||
|
<ListItemIcon style={{ minWidth: '25px' }}>
|
||||||
|
{activeSortBy === props.sortBy && (
|
||||||
|
<TickIcon
|
||||||
|
css={`
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText>{props.children}</ListItemText>
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SortByOptionCreator;
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { MenuList } from '@mui/material';
|
||||||
|
import { COLLECTION_SORT_BY } from 'constants/collection';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import SortByOptionCreator, { SortOptionProps } from './optionCreator';
|
||||||
|
|
||||||
|
export default function CollectionSortOptions(props: SortOptionProps) {
|
||||||
|
const SortByOption = SortByOptionCreator(props);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MenuList>
|
||||||
|
<SortByOption sortBy={COLLECTION_SORT_BY.NAME}>
|
||||||
|
{constants.SORT_BY_NAME}
|
||||||
|
</SortByOption>
|
||||||
|
<SortByOption sortBy={COLLECTION_SORT_BY.CREATION_TIME_DESCENDING}>
|
||||||
|
{constants.SORT_BY_CREATION_TIME_DESCENDING}
|
||||||
|
</SortByOption>
|
||||||
|
<SortByOption sortBy={COLLECTION_SORT_BY.CREATION_TIME_ASCENDING}>
|
||||||
|
{constants.SORT_BY_CREATION_TIME_ASCENDING}
|
||||||
|
</SortByOption>
|
||||||
|
<SortByOption sortBy={COLLECTION_SORT_BY.UPDATION_TIME_DESCENDING}>
|
||||||
|
{constants.SORT_BY_UPDATION_TIME_DESCENDING}
|
||||||
|
</SortByOption>
|
||||||
|
</MenuList>
|
||||||
|
);
|
||||||
|
}
|
30
src/components/Collections/AllCollections/content.tsx
Normal file
30
src/components/Collections/AllCollections/content.tsx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { DialogContent } from '@mui/material';
|
||||||
|
import { FlexWrapper } from 'components/Container';
|
||||||
|
import AllCollectionCard from './CollectionCard';
|
||||||
|
|
||||||
|
export default function AllCollectionContent({
|
||||||
|
sortedCollectionSummaries,
|
||||||
|
onCollectionClick,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<DialogContent>
|
||||||
|
<FlexWrapper
|
||||||
|
style={{
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
}}>
|
||||||
|
{sortedCollectionSummaries.map(
|
||||||
|
({ latestFile, collectionAttributes, fileCount }) => (
|
||||||
|
<AllCollectionCard
|
||||||
|
onCollectionClick={onCollectionClick}
|
||||||
|
collectionAttributes={collectionAttributes}
|
||||||
|
key={collectionAttributes.id}
|
||||||
|
latestFile={latestFile}
|
||||||
|
fileCount={fileCount}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</FlexWrapper>
|
||||||
|
</DialogContent>
|
||||||
|
);
|
||||||
|
}
|
46
src/components/Collections/AllCollections/header.tsx
Normal file
46
src/components/Collections/AllCollections/header.tsx
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { DialogTitle, IconButton, Typography } from '@mui/material';
|
||||||
|
import { SpaceBetweenFlex } from 'components/Container';
|
||||||
|
import CollectionSort from 'components/Collections/AllCollections/CollectionSort';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import Close from '@mui/icons-material/Close';
|
||||||
|
|
||||||
|
export default function AllCollectionsHeader({
|
||||||
|
onClose,
|
||||||
|
collectionCount,
|
||||||
|
collectionSortBy,
|
||||||
|
setCollectionSortBy,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<DialogTitle>
|
||||||
|
<SpaceBetweenFlex>
|
||||||
|
<Typography
|
||||||
|
css={`
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 36px;
|
||||||
|
`}>
|
||||||
|
{constants.ALL_ALBUMS}
|
||||||
|
</Typography>
|
||||||
|
<IconButton onClick={onClose}>
|
||||||
|
<Close />
|
||||||
|
</IconButton>
|
||||||
|
</SpaceBetweenFlex>
|
||||||
|
<SpaceBetweenFlex>
|
||||||
|
<Typography
|
||||||
|
css={`
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 36px;
|
||||||
|
`}
|
||||||
|
color={'text.secondary'}>
|
||||||
|
{`${collectionCount} ${constants.ALBUMS}`}
|
||||||
|
</Typography>
|
||||||
|
<CollectionSort
|
||||||
|
activeSortBy={collectionSortBy}
|
||||||
|
setCollectionSortBy={setCollectionSortBy}
|
||||||
|
/>
|
||||||
|
</SpaceBetweenFlex>
|
||||||
|
</DialogTitle>
|
||||||
|
);
|
||||||
|
}
|
67
src/components/Collections/AllCollections/index.tsx
Normal file
67
src/components/Collections/AllCollections/index.tsx
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
|
import { CollectionType, COLLECTION_SORT_BY } from 'constants/collection';
|
||||||
|
import { sortCollectionSummaries } from 'services/collectionService';
|
||||||
|
import {
|
||||||
|
Transition,
|
||||||
|
FloatingDrawer,
|
||||||
|
} from 'components/Collections/FloatingDrawer';
|
||||||
|
import { useLocalState } from 'hooks/useLocalState';
|
||||||
|
import { LS_KEYS } from 'utils/storage/localStorage';
|
||||||
|
import AllCollectionsHeader from './header';
|
||||||
|
import { CollectionSummaries } from 'types/collection';
|
||||||
|
import AllCollectionContent from './content';
|
||||||
|
|
||||||
|
interface Iprops {
|
||||||
|
isOpen: boolean;
|
||||||
|
close: () => void;
|
||||||
|
collectionSummaries: CollectionSummaries;
|
||||||
|
setActiveCollection: (id?: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LeftSlideTransition = Transition('up');
|
||||||
|
|
||||||
|
export default function AllCollections(props: Iprops) {
|
||||||
|
const { collectionSummaries, isOpen, close, setActiveCollection } = props;
|
||||||
|
|
||||||
|
const [collectionSortBy, setCollectionSortBy] =
|
||||||
|
useLocalState<COLLECTION_SORT_BY>(
|
||||||
|
LS_KEYS.COLLECTION_SORT_BY,
|
||||||
|
COLLECTION_SORT_BY.UPDATION_TIME_DESCENDING
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortedCollectionSummaries = useMemo(
|
||||||
|
() =>
|
||||||
|
sortCollectionSummaries(
|
||||||
|
[...collectionSummaries.values()].filter(
|
||||||
|
(x) => x.collectionAttributes.type !== CollectionType.system
|
||||||
|
),
|
||||||
|
collectionSortBy
|
||||||
|
),
|
||||||
|
[collectionSortBy, collectionSummaries]
|
||||||
|
);
|
||||||
|
|
||||||
|
const onCollectionClick = (collectionID: number) => {
|
||||||
|
setActiveCollection(collectionID);
|
||||||
|
close();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FloatingDrawer
|
||||||
|
TransitionComponent={LeftSlideTransition}
|
||||||
|
onClose={close}
|
||||||
|
open={isOpen}>
|
||||||
|
<AllCollectionsHeader
|
||||||
|
onClose={close}
|
||||||
|
collectionCount={props.collectionSummaries.size}
|
||||||
|
collectionSortBy={collectionSortBy}
|
||||||
|
setCollectionSortBy={setCollectionSortBy}
|
||||||
|
/>
|
||||||
|
<Divider />
|
||||||
|
<AllCollectionContent
|
||||||
|
sortedCollectionSummaries={sortedCollectionSummaries}
|
||||||
|
onCollectionClick={onCollectionClick}
|
||||||
|
/>
|
||||||
|
</FloatingDrawer>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { EnteFile } from 'types/file';
|
import { EnteFile } from 'types/file';
|
||||||
import { CollectionTileWrapper, ActiveIndicator } from './styledComponents';
|
import { CollectionTileWrapper, ActiveIndicator } from '../styledComponents';
|
||||||
import CollectionCard from './CollectionCard';
|
import CollectionCard from '../CollectionCard';
|
||||||
|
|
||||||
const CollectionCardWithActiveIndicator = React.forwardRef(
|
const CollectionCardWithActiveIndicator = React.forwardRef(
|
||||||
(
|
(
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import constants from 'utils/strings/englishConstants';
|
import constants from 'utils/strings/englishConstants';
|
||||||
import { CollectionTitleWithDashedBorder } from './styledComponents';
|
import { CollectionTitleWithDashedBorder } from '../styledComponents';
|
||||||
|
|
||||||
export const CreateNewCollectionTile = (props) => {
|
export const CreateNewCollectionTile = (props) => {
|
||||||
return (
|
return (
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import NavigateNext from '../icons/NavigateNext';
|
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
|
||||||
|
|
||||||
export enum SCROLL_DIRECTION {
|
export enum SCROLL_DIRECTION {
|
||||||
LEFT = -1,
|
LEFT = -1,
|
||||||
|
@ -8,15 +8,18 @@ export enum SCROLL_DIRECTION {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = styled.button<{ direction: SCROLL_DIRECTION }>`
|
const Wrapper = styled.button<{ direction: SCROLL_DIRECTION }>`
|
||||||
|
position: absolute;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: ${({ theme }) => theme.palette.background.paper};
|
background-color: ${({ theme }) => theme.palette.background.paper};
|
||||||
border: none;
|
|
||||||
color: ${({ theme }) => theme.palette.text.primary};
|
color: ${({ theme }) => theme.palette.text.primary};
|
||||||
position: absolute;
|
|
||||||
${(props) =>
|
${(props) =>
|
||||||
props.direction === SCROLL_DIRECTION.LEFT
|
props.direction === SCROLL_DIRECTION.LEFT
|
||||||
? css`
|
? css`
|
||||||
|
@ -38,15 +41,11 @@ const Wrapper = styled.button<{ direction: SCROLL_DIRECTION }>`
|
||||||
height: 30px;
|
height: 30px;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const NavigationButton = ({ scrollDirection, ...rest }) => (
|
const NavigationButton = ({ scrollDirection, ...rest }) => (
|
||||||
<Wrapper direction={scrollDirection} {...rest}>
|
<Wrapper direction={scrollDirection} {...rest}>
|
||||||
<NavigateNext />
|
<NavigateNextIcon />
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
export default NavigationButton;
|
export default NavigationButton;
|
|
@ -1,6 +1,6 @@
|
||||||
import NavigationButton, {
|
import NavigationButton, {
|
||||||
SCROLL_DIRECTION,
|
SCROLL_DIRECTION,
|
||||||
} from 'components/Collections/NavigationButton';
|
} from 'components/Collections/CollectionBar/NavigationButton';
|
||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Collection, CollectionSummaries } from 'types/collection';
|
import { Collection, CollectionSummaries } from 'types/collection';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
|
@ -12,7 +12,7 @@ import {
|
||||||
ScrollContainer,
|
ScrollContainer,
|
||||||
PaddedSpaceBetweenFlex,
|
PaddedSpaceBetweenFlex,
|
||||||
} from 'components/Collections/styledComponents';
|
} from 'components/Collections/styledComponents';
|
||||||
import CollectionCardWithActiveIndicator from 'components/Collections/CollectionCardWithActiveIndicator';
|
import CollectionCardWithActiveIndicator from 'components/Collections/CollectionBar/CollectionCardWithActiveIndicator';
|
||||||
import useComponentScroll from 'hooks/useComponentScroll';
|
import useComponentScroll from 'hooks/useComponentScroll';
|
||||||
import useWindowSize from 'hooks/useWindowSize';
|
import useWindowSize from 'hooks/useWindowSize';
|
||||||
import LinkButton from 'components/pages/gallery/LinkButton';
|
import LinkButton from 'components/pages/gallery/LinkButton';
|
|
@ -3,15 +3,15 @@ import { GalleryContext } from 'pages/gallery';
|
||||||
import { useState, useContext, useEffect } from 'react';
|
import { useState, useContext, useEffect } from 'react';
|
||||||
import downloadManager from 'services/downloadManager';
|
import downloadManager from 'services/downloadManager';
|
||||||
import { EnteFile } from 'types/file';
|
import { EnteFile } from 'types/file';
|
||||||
import { CollectionTile } from './styledComponents';
|
import { CollectionTile, LargerCollectionTile } from './styledComponents';
|
||||||
|
|
||||||
export default function CollectionCard(props: {
|
export default function CollectionCard(props: {
|
||||||
children?: any;
|
children?: any;
|
||||||
latestFile: EnteFile;
|
latestFile: EnteFile;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
customCollectionTile?: any;
|
large?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { latestFile: file, onClick, children, customCollectionTile } = props;
|
const { latestFile: file, onClick, children, large } = props;
|
||||||
|
|
||||||
const [coverImageURL, setCoverImageURL] = useState(null);
|
const [coverImageURL, setCoverImageURL] = useState(null);
|
||||||
const galleryContext = useContext(GalleryContext);
|
const galleryContext = useContext(GalleryContext);
|
||||||
|
@ -28,7 +28,7 @@ export default function CollectionCard(props: {
|
||||||
};
|
};
|
||||||
main();
|
main();
|
||||||
}, [file]);
|
}, [file]);
|
||||||
const UsedCollectionTile = customCollectionTile ?? CollectionTile;
|
const UsedCollectionTile = large ? LargerCollectionTile : CollectionTile;
|
||||||
return (
|
return (
|
||||||
<UsedCollectionTile coverImgURL={coverImageURL} onClick={onClick}>
|
<UsedCollectionTile coverImgURL={coverImageURL} onClick={onClick}>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -25,10 +25,19 @@ export default function collectionInfo(props: Iprops) {
|
||||||
return (
|
return (
|
||||||
<PaddedSpaceBetweenFlex>
|
<PaddedSpaceBetweenFlex>
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="h5">
|
<Typography
|
||||||
<strong>{collectionAttributes.name}</strong>
|
css={`
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 36px;
|
||||||
|
`}>
|
||||||
|
{collectionAttributes.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="subtitle1">
|
<Typography
|
||||||
|
css={`
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
`}>
|
||||||
{fileCount} {constants.PHOTOS}
|
{fileCount} {constants.PHOTOS}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Dialog, DialogContent, TextField } from '@mui/material';
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
IconButton,
|
||||||
|
TextField,
|
||||||
|
} from '@mui/material';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import SubmitButton from 'components/SubmitButton';
|
import SubmitButton from 'components/SubmitButton';
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import DialogTitleWithCloseButton from 'components/MessageDialog/TitleWithCloseButton';
|
import { SpaceBetweenFlex } from 'components/Container';
|
||||||
|
import Close from '@mui/icons-material/Close';
|
||||||
|
|
||||||
export interface CollectionNamerAttributes {
|
export interface CollectionNamerAttributes {
|
||||||
callback: (name) => void;
|
callback: (name) => void;
|
||||||
|
@ -37,9 +44,14 @@ export default function CollectionNamer({ attributes, ...props }: Props) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={props.show} onClose={props.onHide} maxWidth="xs">
|
<Dialog open={props.show} onClose={props.onHide} maxWidth="xs">
|
||||||
<DialogTitleWithCloseButton onClose={props.onHide}>
|
<DialogTitle>
|
||||||
{attributes?.title}
|
<SpaceBetweenFlex>
|
||||||
</DialogTitleWithCloseButton>
|
{attributes?.title}
|
||||||
|
<IconButton onClick={props.onHide}>
|
||||||
|
<Close />
|
||||||
|
</IconButton>
|
||||||
|
</SpaceBetweenFlex>
|
||||||
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Formik<formValues>
|
<Formik<formValues>
|
||||||
initialValues={{
|
initialValues={{
|
||||||
|
|
|
@ -174,6 +174,7 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
|
||||||
open={Boolean(optionEl)}
|
open={Boolean(optionEl)}
|
||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
MenuListProps={{
|
MenuListProps={{
|
||||||
|
disablePadding: true,
|
||||||
'aria-labelledby': 'collection-options',
|
'aria-labelledby': 'collection-options',
|
||||||
}}>
|
}}>
|
||||||
<Paper>
|
<Paper>
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
} from 'services/collectionService';
|
} from 'services/collectionService';
|
||||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||||
import SubmitButton from '../SubmitButton';
|
import SubmitButton from '../SubmitButton';
|
||||||
import MessageDialog from '../MessageDialog';
|
import DialogBox from '../DialogBox';
|
||||||
import { Collection, PublicURL, UpdatePublicURL } from 'types/collection';
|
import { Collection, PublicURL, UpdatePublicURL } from 'types/collection';
|
||||||
import {
|
import {
|
||||||
appendCollectionKeyToShareURL,
|
appendCollectionKeyToShareURL,
|
||||||
|
@ -25,7 +25,7 @@ import {
|
||||||
shareExpiryOptions,
|
shareExpiryOptions,
|
||||||
} from 'utils/collection';
|
} from 'utils/collection';
|
||||||
import { FlexWrapper, Label, Row, Value } from '../Container';
|
import { FlexWrapper, Label, Row, Value } from '../Container';
|
||||||
import { CodeBlock } from '../CodeBlock';
|
import CodeBlock from '../CodeBlock';
|
||||||
import { ButtonVariant, getVariantColor } from '../pages/gallery/LinkButton';
|
import { ButtonVariant, getVariantColor } from '../pages/gallery/LinkButton';
|
||||||
import { handleSharingErrors } from 'utils/error';
|
import { handleSharingErrors } from 'utils/error';
|
||||||
import { sleep } from 'utils/common';
|
import { sleep } from 'utils/common';
|
||||||
|
@ -351,9 +351,9 @@ function CollectionShare(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
show={props.show}
|
open={props.show}
|
||||||
onHide={props.onHide}
|
onClose={props.onHide}
|
||||||
attributes={{
|
attributes={{
|
||||||
title: constants.SHARE_COLLECTION,
|
title: constants.SHARE_COLLECTION,
|
||||||
staticBackdrop: true,
|
staticBackdrop: true,
|
||||||
|
@ -573,9 +573,9 @@ function CollectionShare(props: Props) {
|
||||||
</OptionValue>
|
</OptionValue>
|
||||||
</OptionRow>
|
</OptionRow>
|
||||||
</section>
|
</section>
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
show={configurePassword}
|
open={configurePassword}
|
||||||
onHide={() => setConfigurePassword(false)}
|
onClose={() => setConfigurePassword(false)}
|
||||||
size="sm"
|
size="sm"
|
||||||
attributes={{
|
attributes={{
|
||||||
title: constants.PASSWORD_LOCK,
|
title: constants.PASSWORD_LOCK,
|
||||||
|
@ -588,7 +588,7 @@ function CollectionShare(props: Props) {
|
||||||
buttonText={constants.LOCK}
|
buttonText={constants.LOCK}
|
||||||
fieldType="password"
|
fieldType="password"
|
||||||
/>
|
/>
|
||||||
</MessageDialog>
|
</DialogBox>
|
||||||
</details>
|
</details>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
@ -601,7 +601,7 @@ function CollectionShare(props: Props) {
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</DeadCenter>
|
</DeadCenter>
|
||||||
</MessageDialog>
|
</DialogBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default CollectionShare;
|
export default CollectionShare;
|
||||||
|
|
|
@ -2,22 +2,20 @@ import { Dialog, Slide, styled } from '@mui/material';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
export const FloatingDrawer = styled(Dialog)<{ position: 'left' | 'right' }>(
|
export const FloatingDrawer = styled(Dialog)(({ theme }) => ({
|
||||||
({ position, theme }) => ({
|
'& .MuiDialog-container': {
|
||||||
'& .MuiDialogContent-root': {
|
justifyContent: 'flex-end',
|
||||||
padding: theme.spacing(2),
|
},
|
||||||
},
|
'& .MuiPaper-root': {
|
||||||
'& .MuiDialogActions-root': {
|
maxWidth: '498px',
|
||||||
padding: theme.spacing(1),
|
},
|
||||||
},
|
'& .MuiDialogTitle-root': {
|
||||||
'& .MuiPaper-root': {
|
padding: theme.spacing(3, 2),
|
||||||
maxWidth: '510px',
|
},
|
||||||
},
|
'& .MuiDialogContent-root': {
|
||||||
'& .MuiDialog-container': {
|
padding: theme.spacing(2),
|
||||||
justifyContent: position === 'left' ? 'flex-start' : 'flex-end',
|
},
|
||||||
},
|
}));
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
FloatingDrawer.propTypes = {
|
FloatingDrawer.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
|
|
|
@ -13,7 +13,7 @@ export const CollectionBarWrapper = styled.div`
|
||||||
@media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
|
@media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * 4}px) {
|
||||||
padding: 0 4px;
|
padding: 0 4px;
|
||||||
}
|
}
|
||||||
border-bottom: 1px solid ${({ theme }) => theme.palette.grey.A400};
|
border-bottom: 1px solid ${({ theme }) => theme.palette.grey.A200};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const PaddedSpaceBetweenFlex = styled(SpaceBetweenFlex)`
|
export const PaddedSpaceBetweenFlex = styled(SpaceBetweenFlex)`
|
||||||
|
@ -37,7 +37,6 @@ export const ScrollContainer = styled.div`
|
||||||
export const CollectionTile = styled.div<{
|
export const CollectionTile = styled.div<{
|
||||||
coverImgURL?: string;
|
coverImgURL?: string;
|
||||||
}>`
|
}>`
|
||||||
flex-shrink: 0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 64px;
|
height: 64px;
|
||||||
|
@ -50,10 +49,12 @@ export const CollectionTile = styled.div<{
|
||||||
background-image: url(${({ coverImgURL }) => coverImgURL});
|
background-image: url(${({ coverImgURL }) => coverImgURL});
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
border: 1px solid ${({ theme }) => theme.palette.grey.A200};
|
border: 1px solid ${({ theme }) => theme.palette.grey.A200};
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CollectionTileWrapper = styled.div`
|
export const CollectionTileWrapper = styled.div`
|
||||||
margin-right: 6px;
|
margin-right: 4px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ActiveIndicator = styled.div`
|
export const ActiveIndicator = styled.div`
|
||||||
|
@ -72,7 +73,7 @@ export const LargerCollectionTile = styled(CollectionTile)`
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin: 4px;
|
margin: 2px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CollectionTitleWithDashedBorder = styled(CollectionTile)`
|
export const CollectionTitleWithDashedBorder = styled(CollectionTile)`
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
import { Box } from '@mui/material';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { default as MuiStyled } from '@mui/styled-engine';
|
import { default as MuiStyled } from '@mui/styled-engine';
|
||||||
import { Container as MuiContainer } from '@mui/material';
|
|
||||||
|
|
||||||
const Container = MuiStyled(MuiContainer)`
|
const VerticallyCentered = MuiStyled(Box)`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -12,7 +12,7 @@ const Container = MuiStyled(MuiContainer)`
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default Container;
|
export default VerticallyCentered;
|
||||||
|
|
||||||
export const DisclaimerContainer = styled.div`
|
export const DisclaimerContainer = styled.div`
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
|
@ -56,9 +56,8 @@ export const Value = styled.div<{ width?: string }>`
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const FlexWrapper = styled.div`
|
export const FlexWrapper = styled(Box)`
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -72,6 +71,14 @@ export const SpaceBetweenFlex = styled(FlexWrapper)`
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const CenteredFlex = styled(FlexWrapper)`
|
||||||
|
justify-content: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FluidContainer = styled(FlexWrapper)`
|
||||||
|
flex: 1;
|
||||||
|
`;
|
||||||
|
|
||||||
export const InvertedIconButton = styled(IconButton)`
|
export const InvertedIconButton = styled(IconButton)`
|
||||||
background-color: ${({ theme }) => theme.palette.primary.main};
|
background-color: ${({ theme }) => theme.palette.primary.main};
|
||||||
color: ${({ theme }) => theme.palette.background.default};
|
color: ${({ theme }) => theme.palette.background.default};
|
||||||
|
|
26
src/components/DialogBox/base.tsx
Normal file
26
src/components/DialogBox/base.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { Dialog, styled } from '@mui/material';
|
||||||
|
|
||||||
|
const DialogBoxBase = styled(Dialog)(({ theme }) => ({
|
||||||
|
'& .MuiDialogTitle-root': {
|
||||||
|
padding: theme.spacing(4, 3, 2),
|
||||||
|
},
|
||||||
|
'& .MuiDialogContent-root': {
|
||||||
|
padding: theme.spacing(0, 3, 2),
|
||||||
|
},
|
||||||
|
'& .MuiDialogActions-root': {
|
||||||
|
padding: theme.spacing(4, 3),
|
||||||
|
},
|
||||||
|
'& .MuiDialogActions-root button': {
|
||||||
|
marginLeft: theme.spacing(2),
|
||||||
|
fontSize: '18px',
|
||||||
|
lineHeight: '21.78px',
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
DialogBoxBase.defaultProps = {
|
||||||
|
fullWidth: true,
|
||||||
|
maxWidth: 'sm',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DialogBoxBase;
|
92
src/components/DialogBox/index.tsx
Normal file
92
src/components/DialogBox/index.tsx
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import React from 'react';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import {
|
||||||
|
Breakpoint,
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogProps,
|
||||||
|
} from '@mui/material';
|
||||||
|
import DialogTitleWithCloseButton from './titleWithCloseButton';
|
||||||
|
import MessageText from './messageText';
|
||||||
|
import DialogBoxBase from './base';
|
||||||
|
import { DialogBoxAttributes } from 'types/dialogBox';
|
||||||
|
|
||||||
|
type IProps = React.PropsWithChildren<
|
||||||
|
Omit<DialogProps, 'onClose' | 'maxSize'> & {
|
||||||
|
onClose: () => void;
|
||||||
|
attributes: DialogBoxAttributes;
|
||||||
|
size?: Breakpoint;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
export default function DialogBox({ attributes, children, ...props }: IProps) {
|
||||||
|
if (!attributes) {
|
||||||
|
return <Dialog open={false} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose: DialogProps['onClose'] = (_, reason) => {
|
||||||
|
if (attributes?.nonClosable) {
|
||||||
|
// no-op
|
||||||
|
} else if (attributes?.staticBackdrop && reason === 'backdropClick') {
|
||||||
|
// no-op
|
||||||
|
} else {
|
||||||
|
props.onClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogBoxBase
|
||||||
|
open={props.open}
|
||||||
|
maxWidth={props.size}
|
||||||
|
onClose={handleClose}
|
||||||
|
{...props}>
|
||||||
|
{attributes.title && (
|
||||||
|
<DialogTitleWithCloseButton
|
||||||
|
onClose={
|
||||||
|
!attributes?.nonClosable &&
|
||||||
|
attributes?.close?.titleCloseButton &&
|
||||||
|
handleClose
|
||||||
|
}>
|
||||||
|
{attributes.title}
|
||||||
|
</DialogTitleWithCloseButton>
|
||||||
|
)}
|
||||||
|
{(children || attributes?.content) && (
|
||||||
|
<DialogContent>
|
||||||
|
{children || (
|
||||||
|
<MessageText>{attributes.content}</MessageText>
|
||||||
|
)}
|
||||||
|
</DialogContent>
|
||||||
|
)}
|
||||||
|
{(attributes.close || attributes.proceed) && (
|
||||||
|
<DialogActions>
|
||||||
|
<>
|
||||||
|
{attributes.close && (
|
||||||
|
<Button
|
||||||
|
color={attributes.close?.variant ?? 'secondary'}
|
||||||
|
onClick={() => {
|
||||||
|
attributes.close.action &&
|
||||||
|
attributes.close?.action();
|
||||||
|
props.onClose();
|
||||||
|
}}>
|
||||||
|
{attributes.close?.text ?? constants.OK}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{attributes.proceed && (
|
||||||
|
<Button
|
||||||
|
color={attributes.proceed?.variant}
|
||||||
|
onClick={() => {
|
||||||
|
attributes.proceed.action();
|
||||||
|
props.onClose();
|
||||||
|
}}
|
||||||
|
disabled={attributes.proceed.disabled}>
|
||||||
|
{attributes.proceed.text}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
</DialogActions>
|
||||||
|
)}
|
||||||
|
</DialogBoxBase>
|
||||||
|
);
|
||||||
|
}
|
9
src/components/DialogBox/messageText.tsx
Normal file
9
src/components/DialogBox/messageText.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { DialogContentText, styled } from '@mui/material';
|
||||||
|
|
||||||
|
const MessageText = styled(DialogContentText)(({ theme }) => ({
|
||||||
|
paddingBottom: theme.spacing(2),
|
||||||
|
fontSize: '20px',
|
||||||
|
lineHeight: '24.2px',
|
||||||
|
}));
|
||||||
|
|
||||||
|
export default MessageText;
|
26
src/components/DialogBox/titleWithCloseButton.tsx
Normal file
26
src/components/DialogBox/titleWithCloseButton.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { DialogTitle, IconButton, Typography } from '@mui/material';
|
||||||
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
import { SpaceBetweenFlex } from 'components/Container';
|
||||||
|
|
||||||
|
const DialogTitleWithCloseButton = (props) => {
|
||||||
|
const { children, onClose, ...other } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogTitle {...other}>
|
||||||
|
<SpaceBetweenFlex>
|
||||||
|
<Typography variant="title">{children}</Typography>
|
||||||
|
{onClose && (
|
||||||
|
<IconButton
|
||||||
|
aria-label="close"
|
||||||
|
onClick={onClose}
|
||||||
|
sx={{ float: 'right' }}>
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
</SpaceBetweenFlex>
|
||||||
|
</DialogTitle>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DialogTitleWithCloseButton;
|
|
@ -1,20 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Spinner } from 'react-bootstrap';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
|
|
||||||
export default function EnteSpinner(props) {
|
export default function EnteSpinner(props) {
|
||||||
const { style, ...others } = props ?? {};
|
return <CircularProgress color="accent" size={32} {...props} />;
|
||||||
return (
|
|
||||||
<Spinner
|
|
||||||
animation="border"
|
|
||||||
style={{
|
|
||||||
width: '36px',
|
|
||||||
height: '36px',
|
|
||||||
borderWidth: '0.20em',
|
|
||||||
color: '#51cd7c',
|
|
||||||
...(style && style),
|
|
||||||
}}
|
|
||||||
{...others}
|
|
||||||
role="status"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import ExportInit from './ExportInit';
|
||||||
import ExportInProgress from './ExportInProgress';
|
import ExportInProgress from './ExportInProgress';
|
||||||
import FolderIcon from './icons/FolderIcon';
|
import FolderIcon from './icons/FolderIcon';
|
||||||
import InProgressIcon from './icons/InProgressIcon';
|
import InProgressIcon from './icons/InProgressIcon';
|
||||||
import MessageDialog from './MessageDialog';
|
import DialogBox from './DialogBox';
|
||||||
import { ExportStage, ExportType } from 'constants/export';
|
import { ExportStage, ExportType } from 'constants/export';
|
||||||
|
|
||||||
const FolderIconWrapper = styled.div`
|
const FolderIconWrapper = styled.div`
|
||||||
|
@ -312,9 +312,9 @@ export default function ExportModal(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
show={props.show}
|
open={props.show}
|
||||||
onHide={props.onHide}
|
onClose={props.onHide}
|
||||||
attributes={{
|
attributes={{
|
||||||
title: constants.EXPORT_DATA,
|
title: constants.EXPORT_DATA,
|
||||||
}}>
|
}}>
|
||||||
|
@ -359,6 +359,6 @@ export default function ExportModal(props: Props) {
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
<ExportDynamicState />
|
<ExportDynamicState />
|
||||||
</MessageDialog>
|
</DialogBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import MessageDialog from '../MessageDialog';
|
import DialogBox from '../DialogBox';
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { updateCreationTimeWithExif } from 'services/updateCreationTimeWithExif';
|
import { updateCreationTimeWithExif } from 'services/updateCreationTimeWithExif';
|
||||||
import { GalleryContext } from 'pages/gallery';
|
import { GalleryContext } from 'pages/gallery';
|
||||||
|
@ -93,9 +93,9 @@ export default function FixCreationTime(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
show={props.isOpen}
|
open={props.isOpen}
|
||||||
onHide={props.hide}
|
onClose={props.hide}
|
||||||
attributes={{
|
attributes={{
|
||||||
title:
|
title:
|
||||||
fixState === FIX_STATE.RUNNING
|
fixState === FIX_STATE.RUNNING
|
||||||
|
@ -147,6 +147,6 @@ export default function FixCreationTime(props: Props) {
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
</div>
|
||||||
</MessageDialog>
|
</DialogBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import MessageDialog from './MessageDialog';
|
import DialogBox from './DialogBox';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { ProgressBar, Button } from 'react-bootstrap';
|
import { ProgressBar, Button } from 'react-bootstrap';
|
||||||
import { ComfySpan } from './ExportInProgress';
|
import { ComfySpan } from './ExportInProgress';
|
||||||
|
@ -136,9 +136,9 @@ export default function FixLargeThumbnails(props: Props) {
|
||||||
setData(LS_KEYS.THUMBNAIL_FIX_STATE, { state: fixState });
|
setData(LS_KEYS.THUMBNAIL_FIX_STATE, { state: fixState });
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
show={props.isOpen}
|
open={props.isOpen}
|
||||||
onHide={props.hide}
|
onClose={props.hide}
|
||||||
attributes={{
|
attributes={{
|
||||||
title: constants.COMPRESS_THUMBNAILS,
|
title: constants.COMPRESS_THUMBNAILS,
|
||||||
staticBackdrop: true,
|
staticBackdrop: true,
|
||||||
|
@ -224,6 +224,6 @@ export default function FixLargeThumbnails(props: Props) {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MessageDialog>
|
</DialogBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import React from 'react';
|
import { styled } from '@mui/material/styles';
|
||||||
import { ContainerProps } from '@mui/material';
|
import VerticallyCentered from 'components/Container';
|
||||||
import { FC } from 'react';
|
|
||||||
import Container from 'components/Container';
|
|
||||||
|
|
||||||
const FormContainer: FC<ContainerProps> = ({ style, children, ...props }) => (
|
const FormContainer = styled(VerticallyCentered)(({ theme }) => ({
|
||||||
<Container
|
alignItems: 'flex-end',
|
||||||
style={{ alignItems: 'flex-end', textAlign: 'left', ...style }}
|
paddingRight: theme.spacing(10),
|
||||||
{...props}>
|
[theme.breakpoints.down('md')]: {
|
||||||
{children}
|
paddingRight: theme.spacing(5),
|
||||||
</Container>
|
},
|
||||||
);
|
}));
|
||||||
|
|
||||||
export default FormContainer;
|
export default FormContainer;
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { ContainerProps } from '@mui/material';
|
import { BoxProps, Divider } from '@mui/material';
|
||||||
import Container from 'components/Container';
|
import Container from 'components/Container';
|
||||||
|
|
||||||
const FormPaperFooter: FC<ContainerProps> = ({ sx, style, ...props }) => {
|
const FormPaperFooter: FC<BoxProps> = ({ sx, style, ...props }) => {
|
||||||
return (
|
return (
|
||||||
<Container
|
<>
|
||||||
disableGutters
|
<Divider />
|
||||||
style={{ flexDirection: 'row', ...style }}
|
<Container
|
||||||
sx={{
|
style={{ flexDirection: 'row', ...style }}
|
||||||
mt: 3,
|
sx={{
|
||||||
...sx,
|
mt: 3,
|
||||||
}}
|
...sx,
|
||||||
{...props}>
|
}}
|
||||||
{props.children}
|
{...props}>
|
||||||
</Container>
|
{props.children}
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
import React, { FC } from 'react';
|
|
||||||
import { Typography, TypographyProps } from '@mui/material';
|
|
||||||
|
|
||||||
const FormPaperHeaderText: FC<TypographyProps> = ({ sx, ...props }) => {
|
|
||||||
return (
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: '32px',
|
|
||||||
fontWeight: '600',
|
|
||||||
textAlign: 'left',
|
|
||||||
mb: 8,
|
|
||||||
...sx,
|
|
||||||
}}
|
|
||||||
{...props}>
|
|
||||||
{props.children}
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FormPaperHeaderText;
|
|
12
src/components/Form/FormPaper/Title.tsx
Normal file
12
src/components/Form/FormPaper/Title.tsx
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Typography, TypographyProps } from '@mui/material';
|
||||||
|
|
||||||
|
const FormPaperTitle: FC<TypographyProps> = ({ sx, ...props }) => {
|
||||||
|
return (
|
||||||
|
<Typography variant="title" sx={{ mb: 8, ...sx }} {...props}>
|
||||||
|
{props.children}
|
||||||
|
</Typography>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormPaperTitle;
|
|
@ -1,11 +1,9 @@
|
||||||
import React from 'react';
|
import { Paper, styled } from '@mui/material';
|
||||||
import { Paper, PaperProps } from '@mui/material';
|
|
||||||
import { FC } from 'react';
|
|
||||||
|
|
||||||
const FormPaper: FC<PaperProps> = ({ sx, children, ...props }) => (
|
|
||||||
<Paper sx={{ maxWidth: '360px', py: 4, px: 2, ...sx }} {...props}>
|
|
||||||
{children}
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
|
|
||||||
|
const FormPaper = styled(Paper)(({ theme }) => ({
|
||||||
|
padding: theme.spacing(4, 2),
|
||||||
|
maxWidth: '360px',
|
||||||
|
width: '100%',
|
||||||
|
textAlign: 'left',
|
||||||
|
}));
|
||||||
export default FormPaper;
|
export default FormPaper;
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import constants from 'utils/strings/constants';
|
|
||||||
import MessageDialog from './MessageDialog';
|
|
||||||
|
|
||||||
export default function IncognitoWarning() {
|
|
||||||
return (
|
|
||||||
<MessageDialog
|
|
||||||
show={true}
|
|
||||||
onHide={() => null}
|
|
||||||
attributes={{
|
|
||||||
title: constants.LOCAL_STORAGE_NOT_ACCESSIBLE,
|
|
||||||
staticBackdrop: true,
|
|
||||||
nonClosable: true,
|
|
||||||
}}>
|
|
||||||
<div>{constants.LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE}</div>
|
|
||||||
</MessageDialog>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,20 +1,13 @@
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { Formik, FormikHelpers } from 'formik';
|
import React, { useEffect } from 'react';
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import * as Yup from 'yup';
|
|
||||||
import { getOtt } from 'services/userService';
|
import { getOtt } from 'services/userService';
|
||||||
import { setData, LS_KEYS, getData } from 'utils/storage/localStorage';
|
import { setData, LS_KEYS, getData } from 'utils/storage/localStorage';
|
||||||
import SubmitButton from 'components/SubmitButton';
|
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import FormPaperHeaderText from './Form/FormPaper/HeaderText';
|
import FormPaperTitle from './Form/FormPaper/Title';
|
||||||
import { Divider, TextField } from '@mui/material';
|
|
||||||
import FormPaperFooter from './Form/FormPaper/Footer';
|
import FormPaperFooter from './Form/FormPaper/Footer';
|
||||||
import LinkButton from './pages/gallery/LinkButton';
|
import LinkButton from './pages/gallery/LinkButton';
|
||||||
|
import SingleInputForm, { SingleInputFormProps } from './SingleInputForm';
|
||||||
interface formValues {
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoginProps {
|
interface LoginProps {
|
||||||
signUp: () => void;
|
signUp: () => void;
|
||||||
|
@ -22,8 +15,6 @@ interface LoginProps {
|
||||||
|
|
||||||
export default function Login(props: LoginProps) {
|
export default function Login(props: LoginProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [waiting, setWaiting] = useState(false);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
|
@ -32,61 +23,33 @@ export default function Login(props: LoginProps) {
|
||||||
if (user?.email) {
|
if (user?.email) {
|
||||||
await router.push(PAGES.VERIFY);
|
await router.push(PAGES.VERIFY);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
|
||||||
};
|
};
|
||||||
main();
|
main();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loginUser = async (
|
const loginUser: SingleInputFormProps['callback'] = async (
|
||||||
{ email }: formValues,
|
email,
|
||||||
{ setFieldError }: FormikHelpers<formValues>
|
setFieldError
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
setWaiting(true);
|
|
||||||
await getOtt(email);
|
await getOtt(email);
|
||||||
setData(LS_KEYS.USER, { email });
|
setData(LS_KEYS.USER, { email });
|
||||||
router.push(PAGES.VERIFY);
|
router.push(PAGES.VERIFY);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setFieldError('email', `${constants.UNKNOWN_ERROR} ${e.message}`);
|
setFieldError(`${constants.UNKNOWN_ERROR} ${e.message}`);
|
||||||
}
|
}
|
||||||
setWaiting(false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormPaperHeaderText>{constants.LOGIN}</FormPaperHeaderText>
|
<FormPaperTitle>{constants.LOGIN}</FormPaperTitle>
|
||||||
<Formik<formValues>
|
<SingleInputForm
|
||||||
initialValues={{ email: '' }}
|
callback={loginUser}
|
||||||
validationSchema={Yup.object().shape({
|
fieldType="email"
|
||||||
email: Yup.string()
|
placeholder={constants.ENTER_EMAIL}
|
||||||
.email(constants.EMAIL_ERROR)
|
buttonText={constants.LOGIN}
|
||||||
.required(constants.REQUIRED),
|
/>
|
||||||
})}
|
|
||||||
validateOnChange={false}
|
|
||||||
validateOnBlur={false}
|
|
||||||
onSubmit={loginUser}>
|
|
||||||
{({ values, errors, handleChange, handleSubmit }) => (
|
|
||||||
<form noValidate onSubmit={handleSubmit}>
|
|
||||||
<TextField
|
|
||||||
fullWidth
|
|
||||||
type="email"
|
|
||||||
label={constants.ENTER_EMAIL}
|
|
||||||
value={values.email}
|
|
||||||
onChange={handleChange('email')}
|
|
||||||
error={Boolean(errors.email)}
|
|
||||||
helperText={errors.email}
|
|
||||||
autoFocus
|
|
||||||
disabled={loading}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SubmitButton
|
|
||||||
buttonText={constants.LOGIN}
|
|
||||||
loading={waiting}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
)}
|
|
||||||
</Formik>
|
|
||||||
<Divider />
|
|
||||||
<FormPaperFooter>
|
<FormPaperFooter>
|
||||||
<LinkButton onClick={props.signUp}>
|
<LinkButton onClick={props.signUp}>
|
||||||
{constants.NO_ACCOUNT}
|
{constants.NO_ACCOUNT}
|
||||||
|
|
|
@ -38,7 +38,7 @@ import { livePhotoBtnHTML } from 'components/LivePhotoBtn';
|
||||||
import { logError } from 'utils/sentry';
|
import { logError } from 'utils/sentry';
|
||||||
|
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import TickIcon from 'components/icons/TickIcon';
|
import TickIcon from '@mui/icons-material/Done';
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import EnteSpinner from 'components/EnteSpinner';
|
import EnteSpinner from 'components/EnteSpinner';
|
||||||
|
|
74
src/components/RecoveryKey/index.tsx
Normal file
74
src/components/RecoveryKey/index.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { downloadAsFile } from 'utils/file';
|
||||||
|
import { getRecoveryKey } from 'utils/crypto';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
import DialogBox from '../DialogBox';
|
||||||
|
import CodeBlock from '../CodeBlock';
|
||||||
|
import { ButtonProps, Typography } from '@mui/material';
|
||||||
|
import * as bip39 from 'bip39';
|
||||||
|
import { DashedBorderWrapper } from './styledComponents';
|
||||||
|
|
||||||
|
// mobile client library only supports english.
|
||||||
|
bip39.setDefaultWordlist('english');
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
show: boolean;
|
||||||
|
onHide: () => void;
|
||||||
|
somethingWentWrong: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RecoveryKey({ somethingWentWrong, ...props }: Props) {
|
||||||
|
const [recoveryKey, setRecoveryKey] = useState(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!props.show) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const main = async () => {
|
||||||
|
try {
|
||||||
|
const recoveryKey = await getRecoveryKey();
|
||||||
|
setRecoveryKey(bip39.entropyToMnemonic(recoveryKey));
|
||||||
|
} catch (e) {
|
||||||
|
somethingWentWrong();
|
||||||
|
props.onHide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
main();
|
||||||
|
}, [props.show]);
|
||||||
|
|
||||||
|
function onSaveClick() {
|
||||||
|
downloadAsFile(constants.RECOVERY_KEY_FILENAME, recoveryKey);
|
||||||
|
props.onHide();
|
||||||
|
}
|
||||||
|
|
||||||
|
const recoveryKeyDialogAttributes = {
|
||||||
|
title: constants.RECOVERY_KEY,
|
||||||
|
close: {
|
||||||
|
text: constants.SAVE_LATER,
|
||||||
|
variant: 'secondary' as ButtonProps['color'],
|
||||||
|
},
|
||||||
|
staticBackdrop: true,
|
||||||
|
proceed: {
|
||||||
|
text: constants.SAVE,
|
||||||
|
action: onSaveClick,
|
||||||
|
disabled: !recoveryKey,
|
||||||
|
variant: 'accent' as ButtonProps['color'],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogBox
|
||||||
|
open={props.show}
|
||||||
|
onClose={props.onHide}
|
||||||
|
size="sm"
|
||||||
|
attributes={recoveryKeyDialogAttributes}>
|
||||||
|
<Typography mb={3}>{constants.RECOVERY_KEY_DESCRIPTION}</Typography>
|
||||||
|
<DashedBorderWrapper>
|
||||||
|
<CodeBlock code={recoveryKey} />
|
||||||
|
<Typography m={2}>
|
||||||
|
{constants.KEY_NOT_STORED_DISCLAIMER}
|
||||||
|
</Typography>
|
||||||
|
</DashedBorderWrapper>
|
||||||
|
</DialogBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default RecoveryKey;
|
6
src/components/RecoveryKey/styledComponents.tsx
Normal file
6
src/components/RecoveryKey/styledComponents.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { Box, styled } from '@mui/material';
|
||||||
|
|
||||||
|
export const DashedBorderWrapper = styled(Box)(({ theme }) => ({
|
||||||
|
border: `1px dashed ${theme.palette.grey.A400}`,
|
||||||
|
borderRadius: theme.spacing(1),
|
||||||
|
}));
|
|
@ -1,74 +0,0 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { downloadAsFile } from 'utils/file';
|
|
||||||
import { getRecoveryKey } from 'utils/crypto';
|
|
||||||
import constants from 'utils/strings/constants';
|
|
||||||
import MessageDialog from './MessageDialog';
|
|
||||||
import { CodeBlock } from './CodeBlock';
|
|
||||||
import { Box, Paper, Typography } from '@mui/material';
|
|
||||||
const bip39 = require('bip39');
|
|
||||||
// mobile client library only supports english.
|
|
||||||
bip39.setDefaultWordlist('english');
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
show: boolean;
|
|
||||||
onHide: () => void;
|
|
||||||
somethingWentWrong: any;
|
|
||||||
}
|
|
||||||
function RecoveryKeyModal({ somethingWentWrong, ...props }: Props) {
|
|
||||||
const [recoveryKey, setRecoveryKey] = useState(null);
|
|
||||||
useEffect(() => {
|
|
||||||
if (!props.show) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const main = async () => {
|
|
||||||
try {
|
|
||||||
const recoveryKey = await getRecoveryKey();
|
|
||||||
setRecoveryKey(bip39.entropyToMnemonic(recoveryKey));
|
|
||||||
} catch (e) {
|
|
||||||
somethingWentWrong();
|
|
||||||
props.onHide();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
main();
|
|
||||||
}, [props.show]);
|
|
||||||
|
|
||||||
function onSaveClick() {
|
|
||||||
downloadAsFile(constants.RECOVERY_KEY_FILENAME, recoveryKey);
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
function onClose() {
|
|
||||||
props.onHide();
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<MessageDialog
|
|
||||||
show={props.show}
|
|
||||||
onHide={onClose}
|
|
||||||
size="xs"
|
|
||||||
attributes={{
|
|
||||||
title: constants.RECOVERY_KEY,
|
|
||||||
close: {
|
|
||||||
text: constants.SAVE_LATER,
|
|
||||||
variant: 'danger',
|
|
||||||
},
|
|
||||||
staticBackdrop: true,
|
|
||||||
proceed: {
|
|
||||||
text: constants.SAVE,
|
|
||||||
action: onSaveClick,
|
|
||||||
disabled: !recoveryKey,
|
|
||||||
variant: 'success',
|
|
||||||
},
|
|
||||||
}}>
|
|
||||||
<p>{constants.RECOVERY_KEY_DESCRIPTION}</p>
|
|
||||||
<Paper
|
|
||||||
component={Box}
|
|
||||||
border={'1px dashed'}
|
|
||||||
borderColor={'grey.A700'}>
|
|
||||||
<CodeBlock code={recoveryKey} />
|
|
||||||
<Typography m="20px">
|
|
||||||
{constants.KEY_NOT_STORED_DISCLAIMER}
|
|
||||||
</Typography>
|
|
||||||
</Paper>
|
|
||||||
</MessageDialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default RecoveryKeyModal;
|
|
|
@ -1,15 +1,17 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import Container from 'components/Container';
|
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { Formik, FormikHelpers } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import SubmitButton from './SubmitButton';
|
import SubmitButton from './SubmitButton';
|
||||||
import { TextField, Typography } from '@mui/material';
|
import { TextField, Typography } from '@mui/material';
|
||||||
|
|
||||||
interface Props {
|
export interface SetPasswordFormProps {
|
||||||
callback: (
|
callback: (
|
||||||
passphrase: string,
|
passphrase: string,
|
||||||
setFieldError: FormikHelpers<SetPasswordFormValues>['setFieldError']
|
setFieldError: (
|
||||||
|
field: keyof SetPasswordFormValues,
|
||||||
|
message: string
|
||||||
|
) => void
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
buttonText: string;
|
buttonText: string;
|
||||||
back: () => void;
|
back: () => void;
|
||||||
|
@ -18,12 +20,19 @@ export interface SetPasswordFormValues {
|
||||||
passphrase: string;
|
passphrase: string;
|
||||||
confirm: string;
|
confirm: string;
|
||||||
}
|
}
|
||||||
function SetPasswordForm(props: Props) {
|
function SetPasswordForm(props: SetPasswordFormProps) {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const onSubmit = async (
|
const onSubmit = async (
|
||||||
values: SetPasswordFormValues,
|
values: SetPasswordFormValues,
|
||||||
{ setFieldError }: FormikHelpers<SetPasswordFormValues>
|
{
|
||||||
|
setFieldError,
|
||||||
|
}: {
|
||||||
|
setFieldError: (
|
||||||
|
field: keyof SetPasswordFormValues,
|
||||||
|
message: string
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
) => {
|
) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
@ -51,46 +60,43 @@ function SetPasswordForm(props: Props) {
|
||||||
validateOnBlur={false}
|
validateOnBlur={false}
|
||||||
onSubmit={onSubmit}>
|
onSubmit={onSubmit}>
|
||||||
{({ values, errors, handleChange, handleSubmit }) => (
|
{({ values, errors, handleChange, handleSubmit }) => (
|
||||||
<form
|
<form noValidate onSubmit={handleSubmit}>
|
||||||
style={{ width: '100%' }}
|
<Typography mb={2} color="text.secondary" variant="body2">
|
||||||
noValidate
|
{constants.ENTER_ENC_PASSPHRASE}
|
||||||
onSubmit={handleSubmit}>
|
</Typography>
|
||||||
<Container disableGutters>
|
|
||||||
<Typography mb={2}>
|
<TextField
|
||||||
{constants.ENTER_ENC_PASSPHRASE}
|
fullWidth
|
||||||
</Typography>
|
variant="filled"
|
||||||
<Typography mb={2}>
|
type="password"
|
||||||
{constants.PASSPHRASE_DISCLAIMER()}
|
label={constants.PASSPHRASE_HINT}
|
||||||
</Typography>
|
value={values.passphrase}
|
||||||
<Container>
|
onChange={handleChange('passphrase')}
|
||||||
<TextField
|
error={Boolean(errors.passphrase)}
|
||||||
margin="normal"
|
helperText={errors.passphrase}
|
||||||
fullWidth
|
autoFocus
|
||||||
type="password"
|
disabled={loading}
|
||||||
label={constants.PASSPHRASE_HINT}
|
/>
|
||||||
value={values.passphrase}
|
<TextField
|
||||||
onChange={handleChange('passphrase')}
|
fullWidth
|
||||||
error={Boolean(errors.passphrase)}
|
variant="filled"
|
||||||
helperText={errors.passphrase}
|
type="password"
|
||||||
autoFocus
|
label={constants.CONFIRM_PASSPHRASE}
|
||||||
disabled={loading}
|
value={values.confirm}
|
||||||
/>
|
onChange={handleChange('confirm')}
|
||||||
<TextField
|
disabled={loading}
|
||||||
fullWidth
|
error={Boolean(errors.confirm)}
|
||||||
type="password"
|
helperText={errors.confirm}
|
||||||
label={constants.CONFIRM_PASSPHRASE}
|
/>
|
||||||
value={values.confirm}
|
|
||||||
onChange={handleChange('confirm')}
|
<Typography my={2} variant="body2">
|
||||||
disabled={loading}
|
{constants.PASSPHRASE_DISCLAIMER()}
|
||||||
error={Boolean(errors.confirm)}
|
</Typography>
|
||||||
helperText={errors.confirm}
|
|
||||||
/>
|
<SubmitButton
|
||||||
<SubmitButton
|
loading={loading}
|
||||||
loading={loading}
|
buttonText={props.buttonText}
|
||||||
buttonText={props.buttonText}
|
/>
|
||||||
/>
|
|
||||||
</Container>
|
|
||||||
</Container>
|
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
|
@ -1,35 +1,38 @@
|
||||||
import React from 'react';
|
import React, { FC } from 'react';
|
||||||
import { Button, ButtonProps } from '@mui/material';
|
import { Button, ButtonProps } from '@mui/material';
|
||||||
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
|
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
|
||||||
|
import { FluidContainer } from 'components/Container';
|
||||||
interface IProps {
|
interface IProps {
|
||||||
children: any;
|
|
||||||
bgDark?: boolean;
|
|
||||||
hideArrow?: boolean;
|
hideArrow?: boolean;
|
||||||
onClick: () => void;
|
smallerArrow?: boolean;
|
||||||
color?: ButtonProps['color'];
|
|
||||||
}
|
}
|
||||||
export default function SidebarButton({
|
const SidebarButton: FC<ButtonProps<'button', IProps>> = ({
|
||||||
children,
|
children,
|
||||||
bgDark,
|
|
||||||
hideArrow,
|
hideArrow,
|
||||||
|
smallerArrow,
|
||||||
|
sx,
|
||||||
...props
|
...props
|
||||||
}: IProps) {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
{...props}
|
|
||||||
variant="text"
|
variant="text"
|
||||||
sx={{
|
fullWidth
|
||||||
width: '100%',
|
sx={{ my: 0.5, px: 1, py: '10px', ...sx }}
|
||||||
marginBottom: '16px',
|
css={`
|
||||||
display: 'flex',
|
font-size: 16px;
|
||||||
justifyContent: 'space-between',
|
font-weight: 600;
|
||||||
bgcolor: bgDark && 'grey.800',
|
line-height: 24px;
|
||||||
padding: '10px',
|
letter-spacing: 0em;
|
||||||
borderRadius: '8px',
|
`}
|
||||||
fontSize: '18px',
|
{...props}>
|
||||||
}}>
|
<FluidContainer>{children}</FluidContainer>
|
||||||
{children}
|
{!hideArrow && (
|
||||||
{!hideArrow && <NavigateNextIcon />}
|
<NavigateNextIcon
|
||||||
|
fontSize={smallerArrow ? 'small' : 'medium'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default SidebarButton;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { Typography, IconButton } from '@mui/material';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
import { SpaceBetweenFlex } from 'components/Container';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeSidebar: () => void;
|
closeSidebar: () => void;
|
||||||
|
@ -9,21 +10,18 @@ interface IProps {
|
||||||
|
|
||||||
export default function HeaderSection({ closeSidebar }: IProps) {
|
export default function HeaderSection({ closeSidebar }: IProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<SpaceBetweenFlex>
|
||||||
<Typography variant="h6">
|
<Typography
|
||||||
<strong>{constants.ENTE}</strong>
|
css={`
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
`}>
|
||||||
|
{constants.ENTE}
|
||||||
</Typography>
|
</Typography>
|
||||||
<IconButton
|
<IconButton aria-label="close" onClick={closeSidebar}>
|
||||||
aria-label="close"
|
<CloseIcon fontSize="small" />
|
||||||
onClick={closeSidebar}
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 16,
|
|
||||||
top: 16,
|
|
||||||
color: (theme) => theme.palette.grey[400],
|
|
||||||
}}>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</>
|
</SpaceBetweenFlex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,16 @@ import { getToken } from 'utils/common/key';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import { downloadApp, initiateEmail } from 'utils/common';
|
import { downloadApp, initiateEmail } from 'utils/common';
|
||||||
import { AppContext } from 'pages/_app';
|
import { AppContext } from 'pages/_app';
|
||||||
|
import { useLocalState } from 'hooks/useLocalState';
|
||||||
|
import { LS_KEYS } from 'utils/storage/localStorage';
|
||||||
|
import { UserDetails } from 'types/user';
|
||||||
|
|
||||||
export default function HelpSection({ userDetails }) {
|
export default function HelpSection() {
|
||||||
const { setDialogMessage } = useContext(AppContext);
|
const [userDetails] = useLocalState<UserDetails>(LS_KEYS.USER_DETAILS);
|
||||||
|
|
||||||
const [exportModalView, setExportModalView] = useState(false);
|
const [exportModalView, setExportModalView] = useState(false);
|
||||||
|
|
||||||
|
const { setDialogMessage } = useContext(AppContext);
|
||||||
|
|
||||||
function openFeedbackURL() {
|
function openFeedbackURL() {
|
||||||
const feedbackURL: string = `${getEndpoint()}/users/feedback?token=${encodeURIComponent(
|
const feedbackURL: string = `${getEndpoint()}/users/feedback?token=${encodeURIComponent(
|
||||||
getToken()
|
getToken()
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { Typography } from '@mui/material';
|
|
||||||
import { SpaceBetweenFlex } from 'components/Container';
|
|
||||||
import ThemeToggler from './ThemeToggler';
|
|
||||||
import { UserDetails } from 'types/user';
|
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
userDetails: UserDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum THEMES {
|
|
||||||
LIGHT,
|
|
||||||
DARK,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function InfoSection({ userDetails }: IProps) {
|
|
||||||
const [theme, setTheme] = useState<THEMES>(THEMES.DARK);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SpaceBetweenFlex style={{ marginBottom: '20px' }}>
|
|
||||||
<Typography pl="5px">{userDetails?.email}</Typography>
|
|
||||||
<ThemeToggler theme={theme} setTheme={setTheme} />
|
|
||||||
</SpaceBetweenFlex>
|
|
||||||
);
|
|
||||||
}
|
|
40
src/components/Sidebar/NavigationButton.tsx
Normal file
40
src/components/Sidebar/NavigationButton.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { Box, ButtonProps } from '@mui/material';
|
||||||
|
import SidebarButton from './Button';
|
||||||
|
import { DotSeparator } from './styledComponents';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
hideArrow?: boolean;
|
||||||
|
icon: JSX.Element;
|
||||||
|
label: JSX.Element | string;
|
||||||
|
count: number;
|
||||||
|
}
|
||||||
|
const NavigationButton: FC<ButtonProps<'button', IProps>> = ({
|
||||||
|
icon,
|
||||||
|
label,
|
||||||
|
count,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<SidebarButton
|
||||||
|
smallerArrow
|
||||||
|
variant="contained"
|
||||||
|
color="secondary"
|
||||||
|
sx={{ px: '12px' }}
|
||||||
|
css={`
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-weight: 500;
|
||||||
|
`}
|
||||||
|
{...props}>
|
||||||
|
<Box mr={'12px'}>{icon}</Box>
|
||||||
|
{label}
|
||||||
|
<DotSeparator />
|
||||||
|
<Box component={'span'} sx={{ color: 'text.secondary' }}>
|
||||||
|
{count}
|
||||||
|
</Box>
|
||||||
|
</SidebarButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavigationButton;
|
|
@ -1,66 +1,46 @@
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import SidebarButton from './Button';
|
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { GalleryContext } from 'pages/gallery';
|
import { GalleryContext } from 'pages/gallery';
|
||||||
import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
|
import { ARCHIVE_SECTION, TRASH_SECTION } from 'constants/collection';
|
||||||
import DeleteIcon from '@mui/icons-material/Delete';
|
import DeleteIcon from '@mui/icons-material/Delete';
|
||||||
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
||||||
import { Box, Typography } from '@mui/material';
|
|
||||||
import { FlexWrapper } from 'components/Container';
|
|
||||||
import { CollectionSummaries } from 'types/collection';
|
import { CollectionSummaries } from 'types/collection';
|
||||||
|
import NavigationButton from './NavigationButton';
|
||||||
interface Iprops {
|
interface Iprops {
|
||||||
closeSidebar: () => void;
|
closeSidebar: () => void;
|
||||||
collectionSummaries: CollectionSummaries;
|
collectionSummaries: CollectionSummaries;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DotSeparator = () => (
|
|
||||||
<Typography color="text.secondary" ml="10px" mr="10px" fontWeight={700}>
|
|
||||||
{'·'}
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
export default function NavigationSection({
|
export default function NavigationSection({
|
||||||
closeSidebar,
|
closeSidebar,
|
||||||
collectionSummaries,
|
collectionSummaries,
|
||||||
}: Iprops) {
|
}: Iprops) {
|
||||||
const galleryContext = useContext(GalleryContext);
|
const galleryContext = useContext(GalleryContext);
|
||||||
const openArchiveSection = () => {
|
|
||||||
galleryContext.setActiveCollection(ARCHIVE_SECTION);
|
|
||||||
closeSidebar();
|
|
||||||
};
|
|
||||||
|
|
||||||
const openTrashSection = () => {
|
const openTrashSection = () => {
|
||||||
galleryContext.setActiveCollection(TRASH_SECTION);
|
galleryContext.setActiveCollection(TRASH_SECTION);
|
||||||
closeSidebar();
|
closeSidebar();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openArchiveSection = () => {
|
||||||
|
galleryContext.setActiveCollection(ARCHIVE_SECTION);
|
||||||
|
closeSidebar();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SidebarButton bgDark onClick={openTrashSection}>
|
<NavigationButton
|
||||||
<FlexWrapper>
|
icon={<DeleteIcon />}
|
||||||
<Box mr="10px">
|
label={constants.TRASH}
|
||||||
<DeleteIcon />
|
count={collectionSummaries.get(TRASH_SECTION)?.fileCount}
|
||||||
</Box>
|
onClick={openTrashSection}
|
||||||
|
/>
|
||||||
{constants.TRASH}
|
<NavigationButton
|
||||||
<DotSeparator />
|
icon={<VisibilityOffIcon />}
|
||||||
<Typography color="text.secondary">
|
label={constants.ARCHIVE}
|
||||||
{collectionSummaries.get(TRASH_SECTION)?.fileCount}
|
count={collectionSummaries.get(ARCHIVE_SECTION)?.fileCount}
|
||||||
</Typography>
|
onClick={openArchiveSection}
|
||||||
</FlexWrapper>
|
/>
|
||||||
</SidebarButton>
|
|
||||||
<SidebarButton bgDark onClick={openArchiveSection}>
|
|
||||||
<FlexWrapper>
|
|
||||||
<Box mr="10px">
|
|
||||||
<VisibilityOffIcon />
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{constants.ARCHIVE}
|
|
||||||
<DotSeparator />
|
|
||||||
<Typography color="text.secondary">
|
|
||||||
{collectionSummaries.get(ARCHIVE_SECTION)?.fileCount}
|
|
||||||
</Typography>
|
|
||||||
</FlexWrapper>
|
|
||||||
</SidebarButton>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,12 @@ interface Iprops {
|
||||||
|
|
||||||
export default function SubscriptionDetails({ userDetails }: Iprops) {
|
export default function SubscriptionDetails({ userDetails }: Iprops) {
|
||||||
return (
|
return (
|
||||||
<Paper component={Box} bgcolor="accent.main" position={'relative'}>
|
<Box
|
||||||
|
display="flex"
|
||||||
|
flexDirection={'column'}
|
||||||
|
height={160}
|
||||||
|
bgcolor="accent.main"
|
||||||
|
position={'relative'}>
|
||||||
{userDetails ? (
|
{userDetails ? (
|
||||||
<>
|
<>
|
||||||
<Box padding={2}>
|
<Box padding={2}>
|
||||||
|
@ -52,7 +57,8 @@ export default function SubscriptionDetails({ userDetails }: Iprops) {
|
||||||
<Paper
|
<Paper
|
||||||
component={Box}
|
component={Box}
|
||||||
position={'relative'}
|
position={'relative'}
|
||||||
zIndex="2"
|
zIndex="100"
|
||||||
|
height="64px"
|
||||||
bgcolor="accent.dark"
|
bgcolor="accent.dark"
|
||||||
padding={2}>
|
padding={2}>
|
||||||
<LinearProgress
|
<LinearProgress
|
||||||
|
@ -84,6 +90,6 @@ export default function SubscriptionDetails({ userDetails }: Iprops) {
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
</Container>
|
</Container>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,19 +2,20 @@ import { ToggleButton, ToggleButtonGroup } from '@mui/material';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||||
import LightModeIcon from '@mui/icons-material/LightMode';
|
import LightModeIcon from '@mui/icons-material/LightMode';
|
||||||
import { THEMES } from './InfoSection';
|
import { THEMES } from 'types/theme';
|
||||||
interface Iprops {
|
interface Iprops {
|
||||||
theme: THEMES;
|
theme: THEMES;
|
||||||
setTheme: (theme: THEMES) => void;
|
setTheme: (theme: THEMES) => void;
|
||||||
}
|
}
|
||||||
export default function ThemeToggler({ theme, setTheme }: Iprops) {
|
export default function ThemeSwitcher({ theme, setTheme }: Iprops) {
|
||||||
const handleChange = (event, theme: THEMES) => {
|
const handleChange = (event, theme: THEMES) => {
|
||||||
setTheme(theme);
|
if (theme !== null) {
|
||||||
|
setTheme(theme);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
color="primary"
|
|
||||||
size="small"
|
size="small"
|
||||||
value={theme}
|
value={theme}
|
||||||
exclusive
|
exclusive
|
|
@ -1,8 +1,8 @@
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useState } from 'react';
|
||||||
import SidebarButton from './Button';
|
import SidebarButton from './Button';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import FixLargeThumbnails from 'components/FixLargeThumbnail';
|
// import FixLargeThumbnails from 'components/FixLargeThumbnail';
|
||||||
import RecoveryKeyModal from 'components/RecoveryKeyModal';
|
import RecoveryKey from 'components/RecoveryKey';
|
||||||
import TwoFactorModal from 'components/TwoFactor/Modal';
|
import TwoFactorModal from 'components/TwoFactor/Modal';
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
|
@ -14,7 +14,7 @@ export default function UtilitySection({ closeSidebar }) {
|
||||||
|
|
||||||
const [recoverModalView, setRecoveryModalView] = useState(false);
|
const [recoverModalView, setRecoveryModalView] = useState(false);
|
||||||
const [twoFactorModalView, setTwoFactorModalView] = useState(false);
|
const [twoFactorModalView, setTwoFactorModalView] = useState(false);
|
||||||
const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false);
|
// const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false);
|
||||||
|
|
||||||
const openRecoveryKeyModal = () => setRecoveryModalView(true);
|
const openRecoveryKeyModal = () => setRecoveryModalView(true);
|
||||||
const closeRecoveryKeyModal = () => setRecoveryModalView(false);
|
const closeRecoveryKeyModal = () => setRecoveryModalView(false);
|
||||||
|
@ -34,7 +34,7 @@ export default function UtilitySection({ closeSidebar }) {
|
||||||
|
|
||||||
const redirectToDeduplicatePage = () => router.push(PAGES.DEDUPLICATE);
|
const redirectToDeduplicatePage = () => router.push(PAGES.DEDUPLICATE);
|
||||||
|
|
||||||
const openThumbnailCompressModal = () => setFixLargeThumbsView(true);
|
// const openThumbnailCompressModal = () => setFixLargeThumbsView(true);
|
||||||
|
|
||||||
const somethingWentWrong = () =>
|
const somethingWentWrong = () =>
|
||||||
setDialogMessage({
|
setDialogMessage({
|
||||||
|
@ -61,11 +61,11 @@ export default function UtilitySection({ closeSidebar }) {
|
||||||
{constants.DEDUPLICATE_FILES}
|
{constants.DEDUPLICATE_FILES}
|
||||||
</SidebarButton>
|
</SidebarButton>
|
||||||
|
|
||||||
<SidebarButton onClick={openThumbnailCompressModal}>
|
{/* <SidebarButton onClick={openThumbnailCompressModal}>
|
||||||
{constants.COMPRESS_THUMBNAILS}
|
{constants.COMPRESS_THUMBNAILS}
|
||||||
</SidebarButton>
|
</SidebarButton> */}
|
||||||
|
|
||||||
<RecoveryKeyModal
|
<RecoveryKey
|
||||||
show={recoverModalView}
|
show={recoverModalView}
|
||||||
onHide={closeRecoveryKeyModal}
|
onHide={closeRecoveryKeyModal}
|
||||||
somethingWentWrong={somethingWentWrong}
|
somethingWentWrong={somethingWentWrong}
|
||||||
|
@ -73,16 +73,15 @@ export default function UtilitySection({ closeSidebar }) {
|
||||||
<TwoFactorModal
|
<TwoFactorModal
|
||||||
show={twoFactorModalView}
|
show={twoFactorModalView}
|
||||||
onHide={closeTwoFactorModalView}
|
onHide={closeTwoFactorModalView}
|
||||||
setDialogMessage={setDialogMessage}
|
|
||||||
closeSidebar={closeSidebar}
|
closeSidebar={closeSidebar}
|
||||||
setLoading={startLoading}
|
setLoading={startLoading}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FixLargeThumbnails
|
{/* <FixLargeThumbnails
|
||||||
isOpen={fixLargeThumbsView}
|
isOpen={fixLargeThumbsView}
|
||||||
hide={() => setFixLargeThumbsView(false)}
|
hide={() => setFixLargeThumbsView(false)}
|
||||||
show={() => setFixLargeThumbsView(true)}
|
show={() => setFixLargeThumbsView(true)}
|
||||||
/>
|
/> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,58 +1,39 @@
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { LS_KEYS, setData } from 'utils/storage/localStorage';
|
|
||||||
|
|
||||||
import { getUserDetails } from 'services/userService';
|
|
||||||
import { UserDetails } from 'types/user';
|
|
||||||
import { getLocalUserDetails } from 'utils/user';
|
|
||||||
import InfoSection from './InfoSection';
|
|
||||||
import NavigationSection from './NavigationSection';
|
import NavigationSection from './NavigationSection';
|
||||||
import UtilitySection from './UtilitySection';
|
import UtilitySection from './UtilitySection';
|
||||||
import HelpSection from './HelpSection';
|
import HelpSection from './HelpSection';
|
||||||
import ExitSection from './ExitSection';
|
import ExitSection from './ExitSection';
|
||||||
import DebugLogs from './DebugLogs';
|
// import DebugLogs from './DebugLogs';
|
||||||
import { DrawerSidebar, DividerWithMargin } from './styledComponents';
|
import { DrawerSidebar, PaddedDivider } from './styledComponents';
|
||||||
import { AppContext } from 'pages/_app';
|
import { AppContext } from 'pages/_app';
|
||||||
import SubscriptionDetails from './SubscriptionDetails';
|
|
||||||
import HeaderSection from './Header';
|
import HeaderSection from './Header';
|
||||||
import { CollectionSummaries } from 'types/collection';
|
import { CollectionSummaries } from 'types/collection';
|
||||||
|
import UserDetailsSection from './userDetailsSection';
|
||||||
|
|
||||||
interface Iprops {
|
interface Iprops {
|
||||||
collectionSummaries: CollectionSummaries;
|
collectionSummaries: CollectionSummaries;
|
||||||
}
|
}
|
||||||
export default function Sidebar({ collectionSummaries }: Iprops) {
|
export default function Sidebar({ collectionSummaries }: Iprops) {
|
||||||
const { sidebarView, closeSidebar } = useContext(AppContext);
|
const { sidebarView, closeSidebar } = useContext(AppContext);
|
||||||
const [userDetails, setUserDetails] = useState<UserDetails>(null);
|
|
||||||
useEffect(() => {
|
|
||||||
setUserDetails(getLocalUserDetails());
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const main = async () => {
|
|
||||||
const userDetails = await getUserDetails();
|
|
||||||
setUserDetails(userDetails);
|
|
||||||
setData(LS_KEYS.USER_DETAILS, userDetails);
|
|
||||||
};
|
|
||||||
main();
|
|
||||||
}, [sidebarView]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DrawerSidebar anchor="left" open={sidebarView} onClose={closeSidebar}>
|
<DrawerSidebar open={sidebarView} onClose={closeSidebar}>
|
||||||
<HeaderSection closeSidebar={closeSidebar} />
|
<HeaderSection closeSidebar={closeSidebar} />
|
||||||
<DividerWithMargin />
|
<PaddedDivider spaced />
|
||||||
<InfoSection userDetails={userDetails} />
|
<UserDetailsSection sidebarView={sidebarView} />
|
||||||
<SubscriptionDetails userDetails={userDetails} />
|
<PaddedDivider invisible />
|
||||||
<DividerWithMargin />
|
|
||||||
<NavigationSection
|
<NavigationSection
|
||||||
closeSidebar={closeSidebar}
|
closeSidebar={closeSidebar}
|
||||||
collectionSummaries={collectionSummaries}
|
collectionSummaries={collectionSummaries}
|
||||||
/>
|
/>
|
||||||
<UtilitySection closeSidebar={closeSidebar} />
|
<UtilitySection closeSidebar={closeSidebar} />
|
||||||
<DividerWithMargin />
|
<PaddedDivider />
|
||||||
<HelpSection userDetails={userDetails} />
|
<HelpSection />
|
||||||
<DividerWithMargin />
|
<PaddedDivider />
|
||||||
<ExitSection />
|
<ExitSection />
|
||||||
<DividerWithMargin />
|
{/* <PaddedDivider />
|
||||||
<DebugLogs />
|
<DebugLogs /> */}
|
||||||
</DrawerSidebar>
|
</DrawerSidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,30 @@
|
||||||
import { Drawer, Divider } from '@mui/material';
|
import { Drawer, Divider, styled } from '@mui/material';
|
||||||
import { default as MuiStyled } from '@mui/styled-engine';
|
import { default as MuiStyled } from '@mui/styled-engine';
|
||||||
|
import CircleIcon from '@mui/icons-material/Circle';
|
||||||
|
|
||||||
export const DrawerSidebar = MuiStyled(Drawer)(() => ({
|
export const DrawerSidebar = MuiStyled(Drawer)(({ theme }) => ({
|
||||||
'& > .MuiPaper-root': {
|
'& .MuiPaper-root': {
|
||||||
width: '320px',
|
width: '320px',
|
||||||
padding: '20px',
|
padding: theme.spacing(2, 1, 4, 1),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const DividerWithMargin = MuiStyled(Divider)(() => ({
|
DrawerSidebar.defaultProps = { anchor: 'left' };
|
||||||
marginTop: '20px',
|
|
||||||
marginBottom: '20px',
|
export const PaddedDivider = MuiStyled(Divider)<{
|
||||||
|
invisible?: boolean;
|
||||||
|
spaced?: boolean;
|
||||||
|
}>(({ theme, invisible, spaced }) => ({
|
||||||
|
margin: theme.spacing(spaced ? 2 : 1, 0),
|
||||||
|
opacity: invisible ? 0 : 1,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
export const DotSeparator = styled(CircleIcon)`
|
||||||
|
height: 4px;
|
||||||
|
width: 4px;
|
||||||
|
left: 86px;
|
||||||
|
top: 18px;
|
||||||
|
border-radius: 0px;
|
||||||
|
margin: 0 ${({ theme }) => theme.spacing(1)};
|
||||||
|
color: ${({ theme }) => theme.palette.text.secondary};
|
||||||
|
`;
|
||||||
|
|
40
src/components/Sidebar/userDetailsSection.tsx
Normal file
40
src/components/Sidebar/userDetailsSection.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { SpaceBetweenFlex } from 'components/Container';
|
||||||
|
import { PaddedDivider } from './styledComponents';
|
||||||
|
import SubscriptionDetails from './SubscriptionDetails';
|
||||||
|
import { getUserDetails } from 'services/userService';
|
||||||
|
import { UserDetails } from 'types/user';
|
||||||
|
import { LS_KEYS } from 'utils/storage/localStorage';
|
||||||
|
import { useLocalState } from 'hooks/useLocalState';
|
||||||
|
import { THEMES } from 'types/theme';
|
||||||
|
import ThemeSwitcher from './ThemeSwitcher';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
export default function UserDetailsSection({ sidebarView }) {
|
||||||
|
const [userDetails, setUserDetails] = useLocalState<UserDetails>(
|
||||||
|
LS_KEYS.USER_DETAILS
|
||||||
|
);
|
||||||
|
const [theme, setTheme] = useLocalState<THEMES>(LS_KEYS.THEME, THEMES.DARK);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!sidebarView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const main = async () => {
|
||||||
|
const userDetails = await getUserDetails();
|
||||||
|
setUserDetails(userDetails);
|
||||||
|
};
|
||||||
|
main();
|
||||||
|
}, [sidebarView]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SpaceBetweenFlex px={1}>
|
||||||
|
<Typography>{userDetails?.email}</Typography>
|
||||||
|
<ThemeSwitcher theme={theme} setTheme={setTheme} />
|
||||||
|
</SpaceBetweenFlex>
|
||||||
|
<PaddedDivider invisible />
|
||||||
|
<SubscriptionDetails userDetails={userDetails} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -17,15 +17,14 @@ import { SESSION_KEYS } from 'utils/storage/sessionStorage';
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Container,
|
|
||||||
Divider,
|
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
TextField,
|
TextField,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import FormPaperHeaderText from './Form/FormPaper/HeaderText';
|
import FormPaperTitle from './Form/FormPaper/Title';
|
||||||
import LinkButton from './pages/gallery/LinkButton';
|
import LinkButton from './pages/gallery/LinkButton';
|
||||||
import FormPaperFooter from './Form/FormPaper/Footer';
|
import FormPaperFooter from './Form/FormPaper/Footer';
|
||||||
|
import VerticallyCentered from './Container';
|
||||||
|
|
||||||
interface FormValues {
|
interface FormValues {
|
||||||
email: string;
|
email: string;
|
||||||
|
@ -93,7 +92,7 @@ export default function SignUp(props: SignUpProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FormPaperHeaderText> {constants.SIGN_UP}</FormPaperHeaderText>
|
<FormPaperTitle> {constants.SIGN_UP}</FormPaperTitle>
|
||||||
<Formik<FormValues>
|
<Formik<FormValues>
|
||||||
initialValues={{
|
initialValues={{
|
||||||
email: '',
|
email: '',
|
||||||
|
@ -117,11 +116,9 @@ export default function SignUp(props: SignUpProps) {
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
}): JSX.Element => (
|
}): JSX.Element => (
|
||||||
<form noValidate onSubmit={handleSubmit}>
|
<form noValidate onSubmit={handleSubmit}>
|
||||||
<Container disableGutters sx={{ mb: 1 }}>
|
<VerticallyCentered sx={{ mb: 1 }}>
|
||||||
<TextField
|
<TextField
|
||||||
variant="filled"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
margin="dense"
|
|
||||||
type="email"
|
type="email"
|
||||||
label={constants.ENTER_EMAIL}
|
label={constants.ENTER_EMAIL}
|
||||||
value={values.email}
|
value={values.email}
|
||||||
|
@ -134,8 +131,6 @@ export default function SignUp(props: SignUpProps) {
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="filled"
|
|
||||||
margin="dense"
|
|
||||||
type="password"
|
type="password"
|
||||||
label={constants.PASSPHRASE_HINT}
|
label={constants.PASSPHRASE_HINT}
|
||||||
value={values.passphrase}
|
value={values.passphrase}
|
||||||
|
@ -147,8 +142,6 @@ export default function SignUp(props: SignUpProps) {
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="filled"
|
|
||||||
margin="dense"
|
|
||||||
type="password"
|
type="password"
|
||||||
label={constants.CONFIRM_PASSPHRASE}
|
label={constants.CONFIRM_PASSPHRASE}
|
||||||
value={values.confirm}
|
value={values.confirm}
|
||||||
|
@ -157,27 +150,30 @@ export default function SignUp(props: SignUpProps) {
|
||||||
helperText={errors.confirm}
|
helperText={errors.confirm}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
/>
|
/>
|
||||||
<FormGroup>
|
<FormGroup sx={{ width: '100%' }}>
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
sx={{
|
sx={{
|
||||||
color: 'text.secondary',
|
color: 'text.secondary',
|
||||||
|
ml: -1,
|
||||||
mt: 2,
|
mt: 2,
|
||||||
}}
|
}}
|
||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
size="small"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
checked={acceptTerms}
|
checked={acceptTerms}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setAcceptTerms(e.target.checked)
|
setAcceptTerms(e.target.checked)
|
||||||
}
|
}
|
||||||
color="success"
|
color="accent"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
label={constants.TERMS_AND_CONDITIONS()}
|
label={constants.TERMS_AND_CONDITIONS()}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Container>
|
</VerticallyCentered>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
sx={{ my: 4 }}
|
||||||
buttonText={constants.CREATE_ACCOUNT}
|
buttonText={constants.CREATE_ACCOUNT}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={!acceptTerms}
|
disabled={!acceptTerms}
|
||||||
|
@ -185,9 +181,9 @@ export default function SignUp(props: SignUpProps) {
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
<Divider />
|
|
||||||
<FormPaperFooter>
|
<FormPaperFooter>
|
||||||
<LinkButton onClick={props.login} color={'text.secondary'}>
|
<LinkButton onClick={props.login}>
|
||||||
{constants.ACCOUNT_EXISTS}
|
{constants.ACCOUNT_EXISTS}
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
</FormPaperFooter>
|
</FormPaperFooter>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { Formik, FormikHelpers } from 'formik';
|
import { Formik, FormikHelpers } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
@ -7,16 +7,19 @@ import TextField from '@mui/material/TextField';
|
||||||
import ShowHidePassword from './Form/ShowHidePassword';
|
import ShowHidePassword from './Form/ShowHidePassword';
|
||||||
|
|
||||||
interface formValues {
|
interface formValues {
|
||||||
passphrase: string;
|
inputValue: string;
|
||||||
}
|
}
|
||||||
interface Props {
|
export interface SingleInputFormProps {
|
||||||
callback: (passphrase: string, setFieldError) => void;
|
callback: (
|
||||||
fieldType: string;
|
inputValue: string,
|
||||||
|
setFieldError: (errorMessage: string) => void
|
||||||
|
) => Promise<void>;
|
||||||
|
fieldType: 'text' | 'email' | 'password';
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
buttonText: string;
|
buttonText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SingleInputForm(props: Props) {
|
export default function SingleInputForm(props: SingleInputFormProps) {
|
||||||
const [loading, SetLoading] = useState(false);
|
const [loading, SetLoading] = useState(false);
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
|
@ -25,7 +28,9 @@ export default function SingleInputForm(props: Props) {
|
||||||
{ setFieldError }: FormikHelpers<formValues>
|
{ setFieldError }: FormikHelpers<formValues>
|
||||||
) => {
|
) => {
|
||||||
SetLoading(true);
|
SetLoading(true);
|
||||||
await props.callback(values.passphrase, setFieldError);
|
await props.callback(values.inputValue, (message) =>
|
||||||
|
setFieldError('inputValue', message)
|
||||||
|
);
|
||||||
SetLoading(false);
|
SetLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -39,25 +44,43 @@ export default function SingleInputForm(props: Props) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validationSchema = useMemo(() => {
|
||||||
|
switch (props.fieldType) {
|
||||||
|
case 'text':
|
||||||
|
return Yup.object().shape({
|
||||||
|
inputValue: Yup.string().required(constants.REQUIRED),
|
||||||
|
});
|
||||||
|
case 'password':
|
||||||
|
return Yup.object().shape({
|
||||||
|
inputValue: Yup.string().required(constants.REQUIRED),
|
||||||
|
});
|
||||||
|
case 'email':
|
||||||
|
return Yup.object().shape({
|
||||||
|
inputValue: Yup.string()
|
||||||
|
.email(constants.EMAIL_ERROR)
|
||||||
|
.required(constants.REQUIRED),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [props.fieldType]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Formik<formValues>
|
<Formik<formValues>
|
||||||
initialValues={{ passphrase: '' }}
|
initialValues={{ inputValue: '' }}
|
||||||
onSubmit={submitForm}
|
onSubmit={submitForm}
|
||||||
validationSchema={Yup.object().shape({
|
validationSchema={validationSchema}
|
||||||
passphrase: Yup.string().required(constants.REQUIRED),
|
|
||||||
})}
|
|
||||||
validateOnChange={false}
|
validateOnChange={false}
|
||||||
validateOnBlur={false}>
|
validateOnBlur={false}>
|
||||||
{({ values, errors, handleChange, handleSubmit }) => (
|
{({ values, errors, handleChange, handleSubmit }) => (
|
||||||
<form noValidate onSubmit={handleSubmit}>
|
<form noValidate onSubmit={handleSubmit}>
|
||||||
<TextField
|
<TextField
|
||||||
|
variant="filled"
|
||||||
fullWidth
|
fullWidth
|
||||||
type={showPassword ? 'text' : props.fieldType}
|
type={showPassword ? 'text' : props.fieldType}
|
||||||
label={props.placeholder}
|
label={props.placeholder}
|
||||||
value={values.passphrase}
|
value={values.inputValue}
|
||||||
onChange={handleChange('passphrase')}
|
onChange={handleChange('inputValue')}
|
||||||
error={Boolean(errors.passphrase)}
|
error={Boolean(errors.inputValue)}
|
||||||
helperText={errors.passphrase}
|
helperText={errors.inputValue}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
autoFocus
|
autoFocus
|
||||||
InputProps={{
|
InputProps={{
|
||||||
|
@ -76,11 +99,10 @@ export default function SingleInputForm(props: Props) {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
sx={{ mt: 2 }}
|
||||||
buttonText={props.buttonText}
|
buttonText={props.buttonText}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<br />
|
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
|
|
@ -12,16 +12,19 @@ const SubmitButton: FC<ButtonProps<'button', Props>> = ({
|
||||||
buttonText,
|
buttonText,
|
||||||
inline,
|
inline,
|
||||||
disabled,
|
disabled,
|
||||||
}: Props) => {
|
sx,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
size="large"
|
size="large"
|
||||||
sx={{ my: 4, p: '12.25px', fontSize: '18px' }}
|
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="success"
|
color="accent"
|
||||||
type="submit"
|
type="submit"
|
||||||
fullWidth={!inline}
|
fullWidth={!inline}
|
||||||
disabled={loading || disabled}>
|
disabled={loading || disabled}
|
||||||
|
sx={{ my: 4, ...sx }}
|
||||||
|
{...props}>
|
||||||
{loading ? <CircularProgress size={25} /> : buttonText}
|
{loading ? <CircularProgress size={25} /> : buttonText}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -57,7 +57,7 @@ export default function TwoFactorModalManageSection(props: Iprops) {
|
||||||
content: constants.UPDATE_TWO_FACTOR_MESSAGE,
|
content: constants.UPDATE_TWO_FACTOR_MESSAGE,
|
||||||
close: { text: constants.CANCEL },
|
close: { text: constants.CANCEL },
|
||||||
proceed: {
|
proceed: {
|
||||||
variant: 'success',
|
variant: 'accent',
|
||||||
text: constants.UPDATE,
|
text: constants.UPDATE,
|
||||||
action: reconfigureTwoFactor,
|
action: reconfigureTwoFactor,
|
||||||
},
|
},
|
||||||
|
@ -78,12 +78,11 @@ export default function TwoFactorModalManageSection(props: Iprops) {
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
textAlign={'center'}>
|
textAlign={'center'}>
|
||||||
<Grid item sm={9} xs={12}>
|
<Grid item sm={9} xs={12}>
|
||||||
{constants.UPDATE_TWO_FACTOR_HINT}
|
{constants.UPDATE_TWO_FACTOR_LABEL}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item sm={3} xs={12}>
|
<Grid item sm={3} xs={12}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
color={'accent'}
|
||||||
color={'success'}
|
|
||||||
onClick={warnTwoFactorReconfigure}
|
onClick={warnTwoFactorReconfigure}
|
||||||
style={{ width: '100%' }}>
|
style={{ width: '100%' }}>
|
||||||
{constants.RECONFIGURE}
|
{constants.RECONFIGURE}
|
||||||
|
@ -97,12 +96,11 @@ export default function TwoFactorModalManageSection(props: Iprops) {
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
textAlign={'center'}>
|
textAlign={'center'}>
|
||||||
<Grid item sm={9} xs={12}>
|
<Grid item sm={9} xs={12}>
|
||||||
{constants.DISABLE_TWO_FACTOR_HINT}{' '}
|
{constants.DISABLE_TWO_FACTOR_LABEL}{' '}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item sm={3} xs={12}>
|
<Grid item sm={3} xs={12}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
|
||||||
color={'danger'}
|
color={'danger'}
|
||||||
onClick={warnTwoFactorDisable}
|
onClick={warnTwoFactorDisable}
|
||||||
style={{ width: '100%' }}>
|
style={{ width: '100%' }}>
|
||||||
|
|
|
@ -3,30 +3,31 @@ import LockIcon from '@mui/icons-material/Lock';
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered from 'components/Container';
|
||||||
import { Button, Typography } from '@mui/material';
|
import { Button, Typography } from '@mui/material';
|
||||||
|
|
||||||
interface Iprops {
|
interface Iprops {
|
||||||
closeSidebar: () => void;
|
close: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TwoFactorModalSetupSection({ closeSidebar }: Iprops) {
|
export default function TwoFactorModalSetupSection({ close }: Iprops) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const redirectToTwoFactorSetup = () => {
|
const redirectToTwoFactorSetup = () => {
|
||||||
closeSidebar();
|
close();
|
||||||
router.push(PAGES.TWO_FACTOR_SETUP);
|
router.push(PAGES.TWO_FACTOR_SETUP);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container disableGutters sx={{ mb: 2 }}>
|
<VerticallyCentered sx={{ mb: 2 }}>
|
||||||
<LockIcon sx={{ fontSize: (theme) => theme.spacing(5), mb: 2 }} />
|
<LockIcon sx={{ fontSize: (theme) => theme.spacing(5), mb: 2 }} />
|
||||||
<Typography mb={2}>{constants.TWO_FACTOR_INFO}</Typography>
|
<Typography mb={4}>{constants.TWO_FACTOR_INFO}</Typography>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="success"
|
color="accent"
|
||||||
|
size="large"
|
||||||
onClick={redirectToTwoFactorSetup}>
|
onClick={redirectToTwoFactorSetup}>
|
||||||
{constants.ENABLE_TWO_FACTOR}
|
{constants.ENABLE_TWO_FACTOR}
|
||||||
</Button>
|
</Button>
|
||||||
</Container>
|
</VerticallyCentered>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,13 @@ import { getTwoFactorStatus } from 'services/userService';
|
||||||
import { SetLoading } from 'types/gallery';
|
import { SetLoading } from 'types/gallery';
|
||||||
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
import { getData, LS_KEYS, setData } from 'utils/storage/localStorage';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import MessageDialog, { SetDialogMessage } from '../../MessageDialog';
|
import DialogBox from '../../DialogBox';
|
||||||
import TwoFactorModalSetupSection from './Setup';
|
import TwoFactorModalSetupSection from './Setup';
|
||||||
import TwoFactorModalManageSection from './Manage';
|
import TwoFactorModalManageSection from './Manage';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
show: boolean;
|
show: boolean;
|
||||||
onHide: () => void;
|
onHide: () => void;
|
||||||
setDialogMessage: SetDialogMessage;
|
|
||||||
setLoading: SetLoading;
|
setLoading: SetLoading;
|
||||||
closeSidebar: () => void;
|
closeSidebar: () => void;
|
||||||
}
|
}
|
||||||
|
@ -42,10 +41,11 @@ function TwoFactorModal(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
size="xs"
|
size="xs"
|
||||||
show={props.show}
|
fullWidth
|
||||||
onHide={props.onHide}
|
open={props.show}
|
||||||
|
onClose={props.onHide}
|
||||||
attributes={{
|
attributes={{
|
||||||
title: constants.TWO_FACTOR_AUTHENTICATION,
|
title: constants.TWO_FACTOR_AUTHENTICATION,
|
||||||
staticBackdrop: true,
|
staticBackdrop: true,
|
||||||
|
@ -54,12 +54,10 @@ function TwoFactorModal(props: Props) {
|
||||||
{isTwoFactorEnabled ? (
|
{isTwoFactorEnabled ? (
|
||||||
<TwoFactorModalManageSection close={close} />
|
<TwoFactorModalManageSection close={close} />
|
||||||
) : (
|
) : (
|
||||||
<TwoFactorModalSetupSection
|
<TwoFactorModalSetupSection close={close} />
|
||||||
closeSidebar={props.closeSidebar}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</MessageDialog>
|
</DialogBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default TwoFactorModal;
|
export default TwoFactorModal;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered from 'components/Container';
|
||||||
import { SetupMode } from 'pages/two-factor/setup';
|
import { SetupMode } from 'pages/two-factor/setup';
|
||||||
import SetupManualMode from 'pages/two-factor/setup/ManualMode';
|
import SetupManualMode from 'pages/two-factor/setup/ManualMode';
|
||||||
import SetupQRMode from 'pages/two-factor/setup/QRMode';
|
import SetupQRMode from 'pages/two-factor/setup/QRMode';
|
||||||
|
@ -16,7 +16,7 @@ export function TwoFactorSetup({ twoFactorSecret }: Iprops) {
|
||||||
const changeToQRMode = () => setSetupMode(SetupMode.QR_CODE);
|
const changeToQRMode = () => setSetupMode(SetupMode.QR_CODE);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container sx={{ mb: 3 }}>
|
<VerticallyCentered sx={{ mb: 3 }}>
|
||||||
{setupMode === SetupMode.QR_CODE ? (
|
{setupMode === SetupMode.QR_CODE ? (
|
||||||
<SetupQRMode
|
<SetupQRMode
|
||||||
twoFactorSecret={twoFactorSecret}
|
twoFactorSecret={twoFactorSecret}
|
||||||
|
@ -28,6 +28,6 @@ export function TwoFactorSetup({ twoFactorSecret }: Iprops) {
|
||||||
changeToQRMode={changeToQRMode}
|
changeToQRMode={changeToQRMode}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</VerticallyCentered>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import React, { FC, useRef, useState } from 'react';
|
||||||
import OtpInput from 'react-otp-input';
|
import OtpInput from 'react-otp-input';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import SubmitButton from 'components/SubmitButton';
|
import SubmitButton from 'components/SubmitButton';
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered, { CenteredFlex } from 'components/Container';
|
||||||
import { Box, Typography, TypographyProps } from '@mui/material';
|
import { Box, Typography, TypographyProps } from '@mui/material';
|
||||||
import InvalidInputMessage from './InvalidInputMessage';
|
import InvalidInputMessage from './InvalidInputMessage';
|
||||||
|
|
||||||
|
@ -54,10 +54,10 @@ export default function VerifyTwoFactor(props: Props) {
|
||||||
noValidate
|
noValidate
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
style={{ width: '100%' }}>
|
style={{ width: '100%' }}>
|
||||||
<Typography mb={2} color="text.secondary">
|
<Typography mb={2} variant="body2" color="text.secondary">
|
||||||
{constants.ENTER_TWO_FACTOR_OTP}
|
{constants.ENTER_TWO_FACTOR_OTP}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ my: 2 }}>
|
<Box my={2}>
|
||||||
<OtpInput
|
<OtpInput
|
||||||
ref={otpInputRef}
|
ref={otpInputRef}
|
||||||
shouldAutoFocus
|
shouldAutoFocus
|
||||||
|
@ -69,9 +69,11 @@ export default function VerifyTwoFactor(props: Props) {
|
||||||
className={'otp-input'}
|
className={'otp-input'}
|
||||||
/>
|
/>
|
||||||
{errors.otp && (
|
{errors.otp && (
|
||||||
<InvalidInputMessage>
|
<CenteredFlex sx={{ mt: 1 }}>
|
||||||
{constants.INCORRECT_CODE}
|
<InvalidInputMessage>
|
||||||
</InvalidInputMessage>
|
{constants.INCORRECT_CODE}
|
||||||
|
</InvalidInputMessage>
|
||||||
|
</CenteredFlex>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<SubmitButton
|
<SubmitButton
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered from 'components/Container';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const QRCode = styled.img`
|
export const QRCode = styled.img(
|
||||||
|
({ theme }) => `
|
||||||
height: 200px;
|
height: 200px;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
margin: 1rem;
|
margin: ${theme.spacing(2)};
|
||||||
`;
|
`
|
||||||
|
);
|
||||||
|
|
||||||
export const LoadingQRCode = styled(Container)(({ theme }) => ({
|
export const LoadingQRCode = styled(VerticallyCentered)(
|
||||||
flex: '0 0 200px',
|
({ theme }) => `
|
||||||
border: `1px solid ${theme.palette.grey[700]}`,
|
width:200px;
|
||||||
width: 200,
|
aspect-ratio:1;
|
||||||
margin: theme.spacing(2),
|
border: 1px solid ${theme.palette.grey.A200};
|
||||||
}));
|
margin: ${theme.spacing(2)};
|
||||||
|
`
|
||||||
|
);
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function NavigateNext(props) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
height="40"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
width="24px"
|
|
||||||
fill="currentColor"
|
|
||||||
{...props}>
|
|
||||||
<path d="M0 0h24v24H0z" fill="none" />
|
|
||||||
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigateNext.defaultProps = {
|
|
||||||
height: 24,
|
|
||||||
width: 24,
|
|
||||||
viewBox: '0 0 24 24',
|
|
||||||
};
|
|
|
@ -1,23 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function SortIcon(props) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
height={props.height}
|
|
||||||
viewBox={props.viewBox}
|
|
||||||
width={props.width}>
|
|
||||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
|
||||||
<path
|
|
||||||
d="M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SortIcon.defaultProps = {
|
|
||||||
height: 24,
|
|
||||||
width: 24,
|
|
||||||
viewBox: '0 0 24 24',
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function TickIcon(props) {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
height={props.height}
|
|
||||||
viewBox={props.viewBox}
|
|
||||||
width={props.width}
|
|
||||||
fill="currentColor">
|
|
||||||
<path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z" />
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TickIcon.defaultProps = {
|
|
||||||
height: 20,
|
|
||||||
width: 20,
|
|
||||||
viewBox: '0 0 24 24',
|
|
||||||
};
|
|
|
@ -1,90 +0,0 @@
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { COLLECTION_SORT_BY } from 'constants/collection';
|
|
||||||
import Menu from '@mui/material/Menu';
|
|
||||||
import { IconButton, MenuItem } from '@mui/material';
|
|
||||||
import SortIcon from 'components/icons/SortIcon';
|
|
||||||
import TickIcon from 'components/icons/TickIcon';
|
|
||||||
import constants from 'utils/strings/constants';
|
|
||||||
import { ListItemIcon, ListItemText, MenuList, Paper } from '@mui/material';
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
setCollectionSortBy: (sortBy: COLLECTION_SORT_BY) => void;
|
|
||||||
activeSortBy: COLLECTION_SORT_BY;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OptionProps extends Props {
|
|
||||||
close: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SortByOptionCreator =
|
|
||||||
({ setCollectionSortBy, activeSortBy, close }: OptionProps) =>
|
|
||||||
(props: { sortBy: COLLECTION_SORT_BY; children: any }) => {
|
|
||||||
const handleClick = () => {
|
|
||||||
setCollectionSortBy(props.sortBy);
|
|
||||||
close();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<MenuItem onClick={handleClick}>
|
|
||||||
<ListItemIcon
|
|
||||||
sx={{
|
|
||||||
minWidth: '30px',
|
|
||||||
}}>
|
|
||||||
{activeSortBy === props.sortBy && <TickIcon />}
|
|
||||||
</ListItemIcon>
|
|
||||||
|
|
||||||
<ListItemText>{props.children}</ListItemText>
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const CollectionSortOptions = (props: OptionProps) => {
|
|
||||||
const SortByOption = SortByOptionCreator(props);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper sx={{ maxWidth: '100%' }}>
|
|
||||||
<MenuList>
|
|
||||||
<SortByOption sortBy={COLLECTION_SORT_BY.NAME}>
|
|
||||||
{constants.SORT_BY_NAME}
|
|
||||||
</SortByOption>
|
|
||||||
<SortByOption
|
|
||||||
sortBy={COLLECTION_SORT_BY.CREATION_TIME_DESCENDING}>
|
|
||||||
{constants.SORT_BY_CREATION_TIME_DESCENDING}
|
|
||||||
</SortByOption>
|
|
||||||
<SortByOption
|
|
||||||
sortBy={COLLECTION_SORT_BY.CREATION_TIME_ASCENDING}>
|
|
||||||
{constants.SORT_BY_CREATION_TIME_ASCENDING}
|
|
||||||
</SortByOption>
|
|
||||||
<SortByOption
|
|
||||||
sortBy={COLLECTION_SORT_BY.UPDATION_TIME_DESCENDING}>
|
|
||||||
{constants.SORT_BY_UPDATION_TIME_DESCENDING}
|
|
||||||
</SortByOption>
|
|
||||||
</MenuList>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function CollectionSort(props: Props) {
|
|
||||||
const [sortByEl, setSortByEl] = useState(null);
|
|
||||||
const handleClose = () => setSortByEl(null);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<IconButton
|
|
||||||
onClick={(event) => setSortByEl(event.currentTarget)}
|
|
||||||
aria-controls={sortByEl ? 'collection-sort' : undefined}
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded={sortByEl ? 'true' : undefined}>
|
|
||||||
<SortIcon />
|
|
||||||
</IconButton>
|
|
||||||
<Menu
|
|
||||||
id="collection-sort"
|
|
||||||
anchorEl={sortByEl}
|
|
||||||
open={Boolean(sortByEl)}
|
|
||||||
onClose={handleClose}
|
|
||||||
MenuListProps={{
|
|
||||||
'aria-labelledby': 'collection-sort',
|
|
||||||
}}>
|
|
||||||
<CollectionSortOptions {...props} close={handleClose} />
|
|
||||||
</Menu>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -20,7 +20,6 @@ import {
|
||||||
hasPaypalSubscription,
|
hasPaypalSubscription,
|
||||||
} from 'utils/billing';
|
} from 'utils/billing';
|
||||||
import { reverseString } from 'utils/common';
|
import { reverseString } from 'utils/common';
|
||||||
import { SetDialogMessage } from 'components/MessageDialog';
|
|
||||||
import ArrowEast from 'components/icons/ArrowEast';
|
import ArrowEast from 'components/icons/ArrowEast';
|
||||||
import LinkButton from './LinkButton';
|
import LinkButton from './LinkButton';
|
||||||
import { DeadCenter, GalleryContext } from 'pages/gallery';
|
import { DeadCenter, GalleryContext } from 'pages/gallery';
|
||||||
|
@ -79,7 +78,7 @@ export const PlanIcon = styled.div<{ currentlySubscribed: boolean }>`
|
||||||
interface Props {
|
interface Props {
|
||||||
modalView: boolean;
|
modalView: boolean;
|
||||||
closeModal: any;
|
closeModal: any;
|
||||||
setDialogMessage: SetDialogMessage;
|
|
||||||
setLoading: SetLoading;
|
setLoading: SetLoading;
|
||||||
}
|
}
|
||||||
enum PLAN_PERIOD {
|
enum PLAN_PERIOD {
|
||||||
|
@ -128,7 +127,7 @@ function PlanSelector(props: Props) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, 'plan selector modal open failed');
|
logError(e, 'plan selector modal open failed');
|
||||||
props.closeModal();
|
props.closeModal();
|
||||||
props.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: constants.OPEN_PLAN_SELECTOR_MODAL_FAILED,
|
title: constants.OPEN_PLAN_SELECTOR_MODAL_FAILED,
|
||||||
content: constants.UNKNOWN_ERROR,
|
content: constants.UNKNOWN_ERROR,
|
||||||
close: { text: 'close', variant: 'danger' },
|
close: { text: 'close', variant: 'danger' },
|
||||||
|
@ -150,7 +149,7 @@ function PlanSelector(props: Props) {
|
||||||
hasMobileSubscription(subscription) &&
|
hasMobileSubscription(subscription) &&
|
||||||
!isSubscriptionCancelled(subscription)
|
!isSubscriptionCancelled(subscription)
|
||||||
) {
|
) {
|
||||||
props.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: constants.ERROR,
|
title: constants.ERROR,
|
||||||
content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE,
|
content: constants.CANCEL_SUBSCRIPTION_ON_MOBILE,
|
||||||
close: { variant: 'danger' },
|
close: { variant: 'danger' },
|
||||||
|
@ -159,13 +158,13 @@ function PlanSelector(props: Props) {
|
||||||
hasPaypalSubscription(subscription) &&
|
hasPaypalSubscription(subscription) &&
|
||||||
!isSubscriptionCancelled(subscription)
|
!isSubscriptionCancelled(subscription)
|
||||||
) {
|
) {
|
||||||
props.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: constants.MANAGE_PLAN,
|
title: constants.MANAGE_PLAN,
|
||||||
content: constants.PAYPAL_MANAGE_NOT_SUPPORTED_MESSAGE(),
|
content: constants.PAYPAL_MANAGE_NOT_SUPPORTED_MESSAGE(),
|
||||||
close: { variant: 'danger' },
|
close: { variant: 'danger' },
|
||||||
});
|
});
|
||||||
} else if (hasStripeSubscription(subscription)) {
|
} else if (hasStripeSubscription(subscription)) {
|
||||||
props.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: `${constants.CONFIRM} ${reverseString(
|
title: `${constants.CONFIRM} ${reverseString(
|
||||||
constants.UPDATE_SUBSCRIPTION
|
constants.UPDATE_SUBSCRIPTION
|
||||||
)}`,
|
)}`,
|
||||||
|
@ -176,7 +175,7 @@ function PlanSelector(props: Props) {
|
||||||
action: updateSubscription.bind(
|
action: updateSubscription.bind(
|
||||||
null,
|
null,
|
||||||
plan,
|
plan,
|
||||||
props.setDialogMessage,
|
appContext.setDialogMessage,
|
||||||
props.setLoading,
|
props.setLoading,
|
||||||
props.closeModal
|
props.closeModal
|
||||||
),
|
),
|
||||||
|
@ -190,7 +189,7 @@ function PlanSelector(props: Props) {
|
||||||
await billingService.buySubscription(plan.stripeID);
|
await billingService.buySubscription(plan.stripeID);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
props.setLoading(false);
|
props.setLoading(false);
|
||||||
props.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: constants.ERROR,
|
title: constants.ERROR,
|
||||||
content: constants.SUBSCRIPTION_PURCHASE_FAILED,
|
content: constants.SUBSCRIPTION_PURCHASE_FAILED,
|
||||||
close: { variant: 'danger' },
|
close: { variant: 'danger' },
|
||||||
|
@ -320,7 +319,7 @@ function PlanSelector(props: Props) {
|
||||||
<LinkButton
|
<LinkButton
|
||||||
color={'success'}
|
color={'success'}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
props.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: constants.CONFIRM_ACTIVATE_SUBSCRIPTION,
|
title: constants.CONFIRM_ACTIVATE_SUBSCRIPTION,
|
||||||
content:
|
content:
|
||||||
constants.ACTIVATE_SUBSCRIPTION_MESSAGE(
|
constants.ACTIVATE_SUBSCRIPTION_MESSAGE(
|
||||||
|
@ -331,7 +330,7 @@ function PlanSelector(props: Props) {
|
||||||
text: constants.ACTIVATE_SUBSCRIPTION,
|
text: constants.ACTIVATE_SUBSCRIPTION,
|
||||||
action: activateSubscription.bind(
|
action: activateSubscription.bind(
|
||||||
null,
|
null,
|
||||||
props.setDialogMessage,
|
appContext.setDialogMessage,
|
||||||
props.closeModal,
|
props.closeModal,
|
||||||
props.setLoading
|
props.setLoading
|
||||||
),
|
),
|
||||||
|
@ -348,7 +347,7 @@ function PlanSelector(props: Props) {
|
||||||
<LinkButton
|
<LinkButton
|
||||||
color="danger"
|
color="danger"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
props.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: constants.CONFIRM_CANCEL_SUBSCRIPTION,
|
title: constants.CONFIRM_CANCEL_SUBSCRIPTION,
|
||||||
content:
|
content:
|
||||||
constants.CANCEL_SUBSCRIPTION_MESSAGE(),
|
constants.CANCEL_SUBSCRIPTION_MESSAGE(),
|
||||||
|
@ -357,7 +356,7 @@ function PlanSelector(props: Props) {
|
||||||
text: constants.CANCEL_SUBSCRIPTION,
|
text: constants.CANCEL_SUBSCRIPTION,
|
||||||
action: cancelSubscription.bind(
|
action: cancelSubscription.bind(
|
||||||
null,
|
null,
|
||||||
props.setDialogMessage,
|
appContext.setDialogMessage,
|
||||||
props.closeModal,
|
props.closeModal,
|
||||||
props.setLoading
|
props.setLoading
|
||||||
),
|
),
|
||||||
|
@ -375,7 +374,7 @@ function PlanSelector(props: Props) {
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={updatePaymentMethod.bind(
|
onClick={updatePaymentMethod.bind(
|
||||||
null,
|
null,
|
||||||
props.setDialogMessage,
|
appContext.setDialogMessage,
|
||||||
props.setLoading
|
props.setLoading
|
||||||
)}
|
)}
|
||||||
style={{ marginTop: '20px' }}>
|
style={{ marginTop: '20px' }}>
|
||||||
|
|
|
@ -153,7 +153,6 @@ const Cont = styled.div<{ disabled: boolean }>`
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
border-radius: ${({ theme }) => theme.shape.borderRadius}px;
|
|
||||||
cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')};
|
cursor: ${(props) => (props.disabled ? 'not-allowed' : 'pointer')};
|
||||||
|
|
||||||
& > img {
|
& > img {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import React, { useContext } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { DeduplicateContext } from 'pages/deduplicate';
|
import { DeduplicateContext } from 'pages/deduplicate';
|
||||||
import LeftArrow from 'components/icons/LeftArrow';
|
import LeftArrow from 'components/icons/LeftArrow';
|
||||||
import { SetDialogMessage } from 'components/MessageDialog';
|
|
||||||
import { IconWithMessage } from 'components/IconWithMessage';
|
import { IconWithMessage } from 'components/IconWithMessage';
|
||||||
|
import { AppContext } from 'pages/_app';
|
||||||
|
|
||||||
const VerticalLine = styled.div`
|
const VerticalLine = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -19,18 +19,17 @@ const VerticalLine = styled.div`
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
deleteFileHelper: () => void;
|
deleteFileHelper: () => void;
|
||||||
setDialogMessage: SetDialogMessage;
|
|
||||||
close: () => void;
|
close: () => void;
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DeduplicateOptions({
|
export default function DeduplicateOptions({
|
||||||
setDialogMessage,
|
|
||||||
deleteFileHelper,
|
deleteFileHelper,
|
||||||
close,
|
close,
|
||||||
count,
|
count,
|
||||||
}: IProps) {
|
}: IProps) {
|
||||||
const deduplicateContext = useContext(DeduplicateContext);
|
const deduplicateContext = useContext(DeduplicateContext);
|
||||||
|
const { setDialogMessage } = useContext(AppContext);
|
||||||
|
|
||||||
const trashHandler = () =>
|
const trashHandler = () =>
|
||||||
setDialogMessage({
|
setDialogMessage({
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { SetDialogMessage } from 'components/MessageDialog';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { SetCollectionSelectorAttributes } from '../CollectionSelector';
|
import { SetCollectionSelectorAttributes } from '../CollectionSelector';
|
||||||
import DeleteIcon from 'components/icons/DeleteIcon';
|
import DeleteIcon from 'components/icons/DeleteIcon';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
@ -25,13 +24,13 @@ import DownloadIcon from 'components/icons/DownloadIcon';
|
||||||
import { User } from 'types/user';
|
import { User } from 'types/user';
|
||||||
import { IconWithMessage } from 'components/IconWithMessage';
|
import { IconWithMessage } from 'components/IconWithMessage';
|
||||||
import { SelectionBar, SelectionContainer } from '.';
|
import { SelectionBar, SelectionContainer } from '.';
|
||||||
|
import { AppContext } from 'pages/_app';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
addToCollectionHelper: (collection: Collection) => void;
|
addToCollectionHelper: (collection: Collection) => void;
|
||||||
moveToCollectionHelper: (collection: Collection) => void;
|
moveToCollectionHelper: (collection: Collection) => void;
|
||||||
restoreToCollectionHelper: (collection: Collection) => void;
|
restoreToCollectionHelper: (collection: Collection) => void;
|
||||||
showCreateCollectionModal: (opsType: COLLECTION_OPS_TYPE) => () => void;
|
showCreateCollectionModal: (opsType: COLLECTION_OPS_TYPE) => () => void;
|
||||||
setDialogMessage: SetDialogMessage;
|
|
||||||
setCollectionSelectorAttributes: SetCollectionSelectorAttributes;
|
setCollectionSelectorAttributes: SetCollectionSelectorAttributes;
|
||||||
deleteFileHelper: (permanent?: boolean) => void;
|
deleteFileHelper: (permanent?: boolean) => void;
|
||||||
removeFromCollectionHelper: () => void;
|
removeFromCollectionHelper: () => void;
|
||||||
|
@ -52,7 +51,6 @@ const SelectedFileOptions = ({
|
||||||
showCreateCollectionModal,
|
showCreateCollectionModal,
|
||||||
removeFromCollectionHelper,
|
removeFromCollectionHelper,
|
||||||
fixTimeHelper,
|
fixTimeHelper,
|
||||||
setDialogMessage,
|
|
||||||
setCollectionSelectorAttributes,
|
setCollectionSelectorAttributes,
|
||||||
deleteFileHelper,
|
deleteFileHelper,
|
||||||
downloadHelper,
|
downloadHelper,
|
||||||
|
@ -63,8 +61,8 @@ const SelectedFileOptions = ({
|
||||||
activeCollection,
|
activeCollection,
|
||||||
isFavoriteCollection,
|
isFavoriteCollection,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
|
const { setDialogMessage } = useContext(AppContext);
|
||||||
const [showFixCreationTime, setShowFixCreationTime] = useState(false);
|
const [showFixCreationTime, setShowFixCreationTime] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const user: User = getData(LS_KEYS.USER);
|
const user: User = getData(LS_KEYS.USER);
|
||||||
const showFixCreationTime =
|
const showFixCreationTime =
|
||||||
|
|
|
@ -3,7 +3,6 @@ import React, { useContext, useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { syncCollections, createAlbum } from 'services/collectionService';
|
import { syncCollections, createAlbum } from 'services/collectionService';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { SetDialogMessage } from 'components/MessageDialog';
|
|
||||||
import UploadProgress from './UploadProgress';
|
import UploadProgress from './UploadProgress';
|
||||||
|
|
||||||
import UploadStrategyChoiceModal from './UploadStrategyChoiceModal';
|
import UploadStrategyChoiceModal from './UploadStrategyChoiceModal';
|
||||||
|
@ -35,7 +34,6 @@ interface Props {
|
||||||
setCollectionSelectorAttributes: SetCollectionSelectorAttributes;
|
setCollectionSelectorAttributes: SetCollectionSelectorAttributes;
|
||||||
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
setCollectionNamerAttributes: SetCollectionNamerAttributes;
|
||||||
setLoading: SetLoading;
|
setLoading: SetLoading;
|
||||||
setDialogMessage: SetDialogMessage;
|
|
||||||
setUploadInProgress: any;
|
setUploadInProgress: any;
|
||||||
showCollectionSelector: () => void;
|
showCollectionSelector: () => void;
|
||||||
fileRejections: FileRejection[];
|
fileRejections: FileRejection[];
|
||||||
|
@ -280,7 +278,7 @@ export default function Upload(props: Props) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setProgressView(false);
|
setProgressView(false);
|
||||||
logError(e, 'Failed to create album');
|
logError(e, 'Failed to create album');
|
||||||
props.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: constants.ERROR,
|
title: constants.ERROR,
|
||||||
staticBackdrop: true,
|
staticBackdrop: true,
|
||||||
close: { variant: 'danger' },
|
close: { variant: 'danger' },
|
||||||
|
@ -425,8 +423,8 @@ export default function Upload(props: Props) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<UploadStrategyChoiceModal
|
<UploadStrategyChoiceModal
|
||||||
show={choiceModalView}
|
open={choiceModalView}
|
||||||
onHide={() => setChoiceModalView(false)}
|
onClose={() => setChoiceModalView(false)}
|
||||||
uploadToSingleCollection={() =>
|
uploadToSingleCollection={() =>
|
||||||
uploadToSingleNewCollection(
|
uploadToSingleNewCollection(
|
||||||
analysisResult.suggestedCollectionName
|
analysisResult.suggestedCollectionName
|
||||||
|
|
|
@ -300,7 +300,6 @@ export default function UploadProgress(props: Props) {
|
||||||
height: '2px',
|
height: '2px',
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
}}
|
}}
|
||||||
color="negative"
|
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={props.now}
|
value={props.now}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import MessageDialog from 'components/MessageDialog';
|
import DialogBox from 'components/DialogBox';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Button } from 'react-bootstrap';
|
import { Button } from 'react-bootstrap';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
uploadToMultipleCollection: () => void;
|
uploadToMultipleCollection: () => void;
|
||||||
show: boolean;
|
open: boolean;
|
||||||
onHide: () => void;
|
onClose: () => void;
|
||||||
uploadToSingleCollection: () => void;
|
uploadToSingleCollection: () => void;
|
||||||
}
|
}
|
||||||
function UploadStrategyChoiceModal({
|
function UploadStrategyChoiceModal({
|
||||||
|
@ -15,7 +15,7 @@ function UploadStrategyChoiceModal({
|
||||||
...props
|
...props
|
||||||
}: Props) {
|
}: Props) {
|
||||||
return (
|
return (
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
{...props}
|
{...props}
|
||||||
attributes={{ title: constants.MULTI_FOLDER_UPLOAD }}>
|
attributes={{ title: constants.MULTI_FOLDER_UPLOAD }}>
|
||||||
<p
|
<p
|
||||||
|
@ -37,7 +37,7 @@ function UploadStrategyChoiceModal({
|
||||||
<Button
|
<Button
|
||||||
variant="outline-success"
|
variant="outline-success"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.onHide();
|
props.onClose();
|
||||||
uploadToSingleCollection();
|
uploadToSingleCollection();
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
|
@ -58,7 +58,7 @@ function UploadStrategyChoiceModal({
|
||||||
<Button
|
<Button
|
||||||
variant="outline-success"
|
variant="outline-success"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.onHide();
|
props.onClose();
|
||||||
uploadToMultipleCollection();
|
uploadToMultipleCollection();
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
|
@ -69,7 +69,7 @@ function UploadStrategyChoiceModal({
|
||||||
{constants.UPLOAD_STRATEGY_COLLECTION_PER_FOLDER}
|
{constants.UPLOAD_STRATEGY_COLLECTION_PER_FOLDER}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</MessageDialog>
|
</DialogBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default UploadStrategyChoiceModal;
|
export default UploadStrategyChoiceModal;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import MessageDialog from 'components/MessageDialog';
|
import DialogBox from 'components/DialogBox';
|
||||||
import SubmitButton from 'components/SubmitButton';
|
import SubmitButton from 'components/SubmitButton';
|
||||||
import { REPORT_REASON } from 'constants/publicCollection';
|
import { REPORT_REASON } from 'constants/publicCollection';
|
||||||
import { Formik, FormikHelpers } from 'formik';
|
import { Formik, FormikHelpers } from 'formik';
|
||||||
|
@ -98,10 +98,10 @@ export function AbuseReportForm({ show, close, url }: Iprops) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
show={show}
|
open={show}
|
||||||
size="lg"
|
size="lg"
|
||||||
onHide={close}
|
onClose={close}
|
||||||
attributes={{
|
attributes={{
|
||||||
title: constants.ABUSE_REPORT,
|
title: constants.ABUSE_REPORT,
|
||||||
staticBackdrop: true,
|
staticBackdrop: true,
|
||||||
|
@ -557,6 +557,6 @@ export function AbuseReportForm({ show, close, url }: Iprops) {
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
</MessageDialog>
|
</DialogBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
import { createTheme } from '@mui/material/styles';
|
|
||||||
|
|
||||||
declare module '@mui/material/styles' {
|
|
||||||
interface Palette {
|
|
||||||
accent: Palette['primary'];
|
|
||||||
danger: Palette['primary'];
|
|
||||||
negative: Palette['primary'];
|
|
||||||
}
|
|
||||||
interface PaletteOptions {
|
|
||||||
accent?: PaletteOptions['primary'];
|
|
||||||
danger?: PaletteOptions['primary'];
|
|
||||||
negative?: PaletteOptions['primary'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '@mui/material/Button' {
|
|
||||||
export interface ButtonPropsColorOverrides {
|
|
||||||
danger: true;
|
|
||||||
negative: true;
|
|
||||||
accent: true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
declare module '@mui/material/LinearProgress' {
|
|
||||||
export interface LinearProgressPropsColorOverrides {
|
|
||||||
danger: true;
|
|
||||||
negative: true;
|
|
||||||
accent: true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a theme instance.
|
|
||||||
const darkThemeOptions = createTheme({
|
|
||||||
components: {
|
|
||||||
MuiPaper: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: { backgroundColor: '#0f0f0f' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiList: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: { padding: 0 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiLink: {
|
|
||||||
defaultProps: {
|
|
||||||
color: 'inherit',
|
|
||||||
},
|
|
||||||
styleOverrides: {
|
|
||||||
root: {
|
|
||||||
textDecorationColor: 'inherit',
|
|
||||||
'&:hover': {
|
|
||||||
color: 'hsla(141, 66%, 50%, 1)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MuiButton: {
|
|
||||||
styleOverrides: {
|
|
||||||
root: {
|
|
||||||
fontSize: '18px',
|
|
||||||
lineHeight: '21.78px',
|
|
||||||
padding: '16px',
|
|
||||||
color: '#fff',
|
|
||||||
textTransform: 'none',
|
|
||||||
borderRadius: '8px',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
palette: {
|
|
||||||
mode: 'dark',
|
|
||||||
primary: {
|
|
||||||
main: 'hsla(0, 0%, 11%, 1)',
|
|
||||||
},
|
|
||||||
negative: {
|
|
||||||
main: '#fff',
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
main: 'hsla(0, 0%, 100%, 0.2)',
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
primary: 'hsla(0, 0%, 100%, 1)',
|
|
||||||
secondary: 'hsla(0, 0%, 100%, 0.5)',
|
|
||||||
},
|
|
||||||
accent: {
|
|
||||||
main: 'hsla(141, 66%, 50%, 1)',
|
|
||||||
dark: 'hsla(141, 73%, 42%, 1)',
|
|
||||||
},
|
|
||||||
|
|
||||||
danger: {
|
|
||||||
main: '#c93f3f',
|
|
||||||
},
|
|
||||||
background: { default: '#000', paper: '#000' },
|
|
||||||
},
|
|
||||||
|
|
||||||
shape: {
|
|
||||||
borderRadius: 12,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default darkThemeOptions;
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { SCROLL_DIRECTION } from 'components/Collections/NavigationButton';
|
import { SCROLL_DIRECTION } from 'components/Collections/CollectionBar/NavigationButton';
|
||||||
import { useRef, useState, useEffect } from 'react';
|
import { useRef, useState, useEffect } from 'react';
|
||||||
|
|
||||||
export default function useComponentScroll({
|
export default function useComponentScroll({
|
||||||
|
@ -15,14 +15,17 @@ export default function useComponentScroll({
|
||||||
}>({});
|
}>({});
|
||||||
|
|
||||||
const updateScrollObj = () => {
|
const updateScrollObj = () => {
|
||||||
if (componentRef.current) {
|
if (!componentRef.current) {
|
||||||
const { scrollLeft, scrollWidth, clientWidth } =
|
return;
|
||||||
componentRef.current;
|
|
||||||
setScrollObj({ scrollLeft, scrollWidth, clientWidth });
|
|
||||||
}
|
}
|
||||||
|
const { scrollLeft, scrollWidth, clientWidth } = componentRef.current;
|
||||||
|
setScrollObj({ scrollLeft, scrollWidth, clientWidth });
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!componentRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Add event listener
|
// Add event listener
|
||||||
componentRef.current?.addEventListener('scroll', updateScrollObj);
|
componentRef.current?.addEventListener('scroll', updateScrollObj);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered from 'components/Container';
|
||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { AppContext } from './_app';
|
import { AppContext } from './_app';
|
||||||
|
@ -11,12 +11,12 @@ export default function NotFound() {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Container>
|
<VerticallyCentered>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<span className="sr-only">Loading...</span>
|
<span className="sr-only">Loading...</span>
|
||||||
) : (
|
) : (
|
||||||
constants.NOT_FOUND
|
constants.NOT_FOUND
|
||||||
)}
|
)}
|
||||||
</Container>
|
</VerticallyCentered>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import styled, { ThemeProvider as SThemeProvider } from 'styled-components';
|
||||||
import Navbar from 'components/Navbar';
|
import Navbar from 'components/Navbar';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered from 'components/Container';
|
||||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||||
import 'photoswipe/dist/photoswipe.css';
|
import 'photoswipe/dist/photoswipe.css';
|
||||||
import 'styles/global.css';
|
import 'styles/global.css';
|
||||||
|
@ -19,14 +19,14 @@ import { getAlbumSiteHost, PAGES } from 'constants/pages';
|
||||||
import GoToEnte from 'components/pages/sharedAlbum/GoToEnte';
|
import GoToEnte from 'components/pages/sharedAlbum/GoToEnte';
|
||||||
import { logUploadInfo } from 'utils/upload';
|
import { logUploadInfo } from 'utils/upload';
|
||||||
import LoadingBar from 'react-top-loading-bar';
|
import LoadingBar from 'react-top-loading-bar';
|
||||||
import MessageDialog, {
|
import DialogBox from 'components/DialogBox';
|
||||||
MessageAttributes,
|
|
||||||
SetDialogMessage,
|
|
||||||
} from 'components/MessageDialog';
|
|
||||||
import { ThemeProvider as MThemeProvider } from '@mui/material/styles';
|
import { ThemeProvider as MThemeProvider } from '@mui/material/styles';
|
||||||
import darkThemeOptions from 'darkThemeOptions';
|
import darkThemeOptions from 'themes/darkThemeOptions';
|
||||||
import { CssBaseline } from '@mui/material';
|
import { CssBaseline } from '@mui/material';
|
||||||
import SidebarToggler from 'components/Navbar/SidebarToggler';
|
import SidebarToggler from 'components/Navbar/SidebarToggler';
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
import * as types from 'styled-components/cssprop';
|
||||||
|
import { SetDialogBoxAttributes, DialogBoxAttributes } from 'types/dialogBox';
|
||||||
|
|
||||||
export const LogoImage = styled.img`
|
export const LogoImage = styled.img`
|
||||||
max-height: 28px;
|
max-height: 28px;
|
||||||
|
@ -64,7 +64,7 @@ type AppContextType = {
|
||||||
startLoading: () => void;
|
startLoading: () => void;
|
||||||
finishLoading: () => void;
|
finishLoading: () => void;
|
||||||
closeMessageDialog: () => void;
|
closeMessageDialog: () => void;
|
||||||
setDialogMessage: SetDialogMessage;
|
setDialogMessage: SetDialogBoxAttributes;
|
||||||
sidebarView: boolean;
|
sidebarView: boolean;
|
||||||
closeSidebar: () => void;
|
closeSidebar: () => void;
|
||||||
};
|
};
|
||||||
|
@ -100,7 +100,7 @@ export default function App({ Component, err }) {
|
||||||
const [isAlbumsDomain, setIsAlbumsDomain] = useState(false);
|
const [isAlbumsDomain, setIsAlbumsDomain] = useState(false);
|
||||||
const isLoadingBarRunning = useRef(false);
|
const isLoadingBarRunning = useRef(false);
|
||||||
const loadingBar = useRef(null);
|
const loadingBar = useRef(null);
|
||||||
const [dialogMessage, setDialogMessage] = useState<MessageAttributes>();
|
const [dialogMessage, setDialogMessage] = useState<DialogBoxAttributes>();
|
||||||
const [messageDialogView, setMessageDialogView] = useState(false);
|
const [messageDialogView, setMessageDialogView] = useState(false);
|
||||||
const [sidebarView, setSidebarView] = useState(false);
|
const [sidebarView, setSidebarView] = useState(false);
|
||||||
|
|
||||||
|
@ -280,10 +280,9 @@ export default function App({ Component, err }) {
|
||||||
)}
|
)}
|
||||||
<LoadingBar color="#51cd7c" ref={loadingBar} />
|
<LoadingBar color="#51cd7c" ref={loadingBar} />
|
||||||
|
|
||||||
<MessageDialog
|
<DialogBox
|
||||||
size="sm"
|
open={messageDialogView}
|
||||||
show={messageDialogView}
|
onClose={closeMessageDialog}
|
||||||
onHide={closeMessageDialog}
|
|
||||||
attributes={dialogMessage}
|
attributes={dialogMessage}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -303,11 +302,11 @@ export default function App({ Component, err }) {
|
||||||
closeSidebar,
|
closeSidebar,
|
||||||
}}>
|
}}>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Container>
|
<VerticallyCentered>
|
||||||
<EnteSpinner>
|
<EnteSpinner>
|
||||||
<span className="sr-only">Loading...</span>
|
<span className="sr-only">Loading...</span>
|
||||||
</EnteSpinner>
|
</EnteSpinner>
|
||||||
</Container>
|
</VerticallyCentered>
|
||||||
) : (
|
) : (
|
||||||
<Component err={err} setLoading={setLoading} />
|
<Component err={err} setLoading={setLoading} />
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,20 +1,14 @@
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered from 'components/Container';
|
||||||
import LogoImg from 'components/LogoImg';
|
import React, { useEffect } from 'react';
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { Alert } from 'react-bootstrap';
|
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import router from 'next/router';
|
import router from 'next/router';
|
||||||
import ChangeEmailForm from 'components/ChangeEmail';
|
import ChangeEmailForm from 'components/ChangeEmail';
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||||
import { Box, Card, CardContent } from '@mui/material';
|
import FormPaper from 'components/Form/FormPaper';
|
||||||
import LinkButton from 'components/pages/gallery/LinkButton';
|
import FormPaperTitle from 'components/Form/FormPaper/Title';
|
||||||
|
|
||||||
function ChangeEmailPage() {
|
function ChangeEmailPage() {
|
||||||
const [email, setEmail] = useState(null);
|
|
||||||
const [showMessage, setShowMessage] = useState(false);
|
|
||||||
const [showBigDialog, setShowBigDialog] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const user = getData(LS_KEYS.USER);
|
const user = getData(LS_KEYS.USER);
|
||||||
if (!user?.token) {
|
if (!user?.token) {
|
||||||
|
@ -22,40 +16,13 @@ function ChangeEmailPage() {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const goToGallery = () => router.push(PAGES.GALLERY);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<VerticallyCentered>
|
||||||
<Card sx={{ minWidth: showBigDialog ? '460px' : '320px' }}>
|
<FormPaper>
|
||||||
<CardContent>
|
<FormPaperTitle>{constants.CHANGE_EMAIL}</FormPaperTitle>
|
||||||
<Container disableGutters sx={{ py: 2 }}>
|
<ChangeEmailForm />
|
||||||
<Box mb={2}>
|
</FormPaper>
|
||||||
<LogoImg src="/icon.svg" />
|
</VerticallyCentered>
|
||||||
{constants.CHANGE_EMAIL}
|
|
||||||
</Box>
|
|
||||||
<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}
|
|
||||||
/>
|
|
||||||
<LinkButton onClick={goToGallery}>
|
|
||||||
{constants.GO_BACK}
|
|
||||||
</LinkButton>
|
|
||||||
</Container>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,16 +10,16 @@ import CryptoWorker, {
|
||||||
import { getActualKey } from 'utils/common/key';
|
import { getActualKey } from 'utils/common/key';
|
||||||
import { setKeys } from 'services/userService';
|
import { setKeys } from 'services/userService';
|
||||||
import SetPasswordForm, {
|
import SetPasswordForm, {
|
||||||
SetPasswordFormValues,
|
SetPasswordFormProps,
|
||||||
} from 'components/SetPasswordForm';
|
} from 'components/SetPasswordForm';
|
||||||
import { SESSION_KEYS } from 'utils/storage/sessionStorage';
|
import { SESSION_KEYS } from 'utils/storage/sessionStorage';
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import { KEK, UpdatedKey } from 'types/user';
|
import { KEK, UpdatedKey } from 'types/user';
|
||||||
import { FormikHelpers } from 'formik';
|
|
||||||
import Container from 'components/Container';
|
|
||||||
import { CardContent, Box, Card } from '@mui/material';
|
|
||||||
import LogoImg from 'components/LogoImg';
|
|
||||||
import LinkButton from 'components/pages/gallery/LinkButton';
|
import LinkButton from 'components/pages/gallery/LinkButton';
|
||||||
|
import VerticallyCentered from 'components/Container';
|
||||||
|
import FormPaper from 'components/Form/FormPaper';
|
||||||
|
import FormPaperFooter from 'components/Form/FormPaper/Footer';
|
||||||
|
import FormPaperTitle from 'components/Form/FormPaper/Title';
|
||||||
|
|
||||||
export default function ChangePassword() {
|
export default function ChangePassword() {
|
||||||
const [token, setToken] = useState<string>();
|
const [token, setToken] = useState<string>();
|
||||||
|
@ -34,9 +34,9 @@ export default function ChangePassword() {
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onSubmit = async (
|
const onSubmit: SetPasswordFormProps['callback'] = async (
|
||||||
passphrase: string,
|
passphrase,
|
||||||
setFieldError: FormikHelpers<SetPasswordFormValues>['setFieldError']
|
setFieldError
|
||||||
) => {
|
) => {
|
||||||
const cryptoWorker = await new CryptoWorker();
|
const cryptoWorker = await new CryptoWorker();
|
||||||
const key: string = await getActualKey();
|
const key: string = await getActualKey();
|
||||||
|
@ -78,29 +78,25 @@ export default function ChangePassword() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<VerticallyCentered>
|
||||||
<Card sx={{ maxWidth: '520px' }}>
|
<FormPaper>
|
||||||
<CardContent>
|
<FormPaperTitle>{constants.CHANGE_PASSWORD}</FormPaperTitle>
|
||||||
<Container disableGutters sx={{ pt: 3 }}>
|
<SetPasswordForm
|
||||||
<Box mb={4}>
|
callback={onSubmit}
|
||||||
<LogoImg src="/icon.svg" />
|
buttonText={constants.CHANGE_PASSWORD}
|
||||||
{constants.CHANGE_PASSWORD}
|
back={
|
||||||
</Box>
|
getData(LS_KEYS.SHOW_BACK_BUTTON)?.value
|
||||||
<SetPasswordForm
|
? redirectToGallery
|
||||||
callback={onSubmit}
|
: null
|
||||||
buttonText={constants.CHANGE_PASSWORD}
|
}
|
||||||
back={
|
/>
|
||||||
getData(LS_KEYS.SHOW_BACK_BUTTON)?.value
|
|
||||||
? redirectToGallery
|
<FormPaperFooter>
|
||||||
: null
|
<LinkButton onClick={router.back}>
|
||||||
}
|
{constants.GO_BACK}
|
||||||
/>
|
</LinkButton>
|
||||||
<LinkButton sx={{ mt: 2 }} onClick={router.back}>
|
</FormPaperFooter>
|
||||||
{constants.GO_BACK}
|
</FormPaper>
|
||||||
</LinkButton>
|
</VerticallyCentered>
|
||||||
</Container>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,15 @@ import CryptoWorker, {
|
||||||
} from 'utils/crypto';
|
} from 'utils/crypto';
|
||||||
import { logoutUser } from 'services/userService';
|
import { logoutUser } from 'services/userService';
|
||||||
import { isFirstLogin } from 'utils/storage';
|
import { isFirstLogin } from 'utils/storage';
|
||||||
import SingleInputForm from 'components/SingleInputForm';
|
import SingleInputForm, {
|
||||||
|
SingleInputFormProps,
|
||||||
|
} from 'components/SingleInputForm';
|
||||||
import { AppContext } from 'pages/_app';
|
import { AppContext } from 'pages/_app';
|
||||||
import { logError } from 'utils/sentry';
|
import { logError } from 'utils/sentry';
|
||||||
import { KeyAttributes } from 'types/user';
|
import { KeyAttributes } from 'types/user';
|
||||||
import FormContainer from 'components/Form/FormContainer';
|
import FormContainer from 'components/Form/FormContainer';
|
||||||
import FormPaper from 'components/Form/FormPaper';
|
import FormPaper from 'components/Form/FormPaper';
|
||||||
import FormPaperHeaderText from 'components/Form/FormPaper/HeaderText';
|
import FormPaperTitle from 'components/Form/FormPaper/Title';
|
||||||
import FormPaperFooter from 'components/Form/FormPaper/Footer';
|
import FormPaperFooter from 'components/Form/FormPaper/Footer';
|
||||||
import LinkButton from 'components/pages/gallery/LinkButton';
|
import LinkButton from 'components/pages/gallery/LinkButton';
|
||||||
|
|
||||||
|
@ -48,7 +50,10 @@ export default function Credentials() {
|
||||||
appContext.showNavBar(false);
|
appContext.showNavBar(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const verifyPassphrase = async (passphrase, setFieldError) => {
|
const verifyPassphrase: SingleInputFormProps['callback'] = async (
|
||||||
|
passphrase,
|
||||||
|
setFieldError
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const cryptoWorker = await new CryptoWorker();
|
const cryptoWorker = await new CryptoWorker();
|
||||||
let kek: string = null;
|
let kek: string = null;
|
||||||
|
@ -84,13 +89,10 @@ export default function Credentials() {
|
||||||
router.push(redirectURL ?? PAGES.GALLERY);
|
router.push(redirectURL ?? PAGES.GALLERY);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, 'user entered a wrong password');
|
logError(e, 'user entered a wrong password');
|
||||||
setFieldError('passphrase', constants.INCORRECT_PASSPHRASE);
|
setFieldError(constants.INCORRECT_PASSPHRASE);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setFieldError(
|
setFieldError(`${constants.UNKNOWN_ERROR} ${e.message}`);
|
||||||
'passphrase',
|
|
||||||
`${constants.UNKNOWN_ERROR} ${e.message}`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,19 +101,20 @@ export default function Credentials() {
|
||||||
return (
|
return (
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<FormPaper style={{ minWidth: '320px' }}>
|
<FormPaper style={{ minWidth: '320px' }}>
|
||||||
<FormPaperHeaderText> {constants.PASSWORD}</FormPaperHeaderText>
|
<FormPaperTitle>{constants.PASSWORD}</FormPaperTitle>
|
||||||
<SingleInputForm
|
<SingleInputForm
|
||||||
callback={verifyPassphrase}
|
callback={verifyPassphrase}
|
||||||
placeholder={constants.RETURN_PASSPHRASE_HINT}
|
placeholder={constants.RETURN_PASSPHRASE_HINT}
|
||||||
buttonText={constants.VERIFY_PASSPHRASE}
|
buttonText={constants.VERIFY_PASSPHRASE}
|
||||||
fieldType="password"
|
fieldType="password"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormPaperFooter style={{ justifyContent: 'space-between' }}>
|
<FormPaperFooter style={{ justifyContent: 'space-between' }}>
|
||||||
<LinkButton onClick={redirectToRecoverPage}>
|
<LinkButton onClick={redirectToRecoverPage}>
|
||||||
{constants.FORGOT_PASSWORD}
|
{constants.FORGOT_PASSWORD}
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
<LinkButton onClick={logoutUser}>
|
<LinkButton onClick={logoutUser}>
|
||||||
{constants.GO_BACK}
|
{constants.CHANGE_EMAIL}
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
</FormPaperFooter>
|
</FormPaperFooter>
|
||||||
</FormPaper>
|
</FormPaper>
|
||||||
|
|
|
@ -168,7 +168,6 @@ export default function Deduplicate() {
|
||||||
activeCollection={ALL_SECTION}
|
activeCollection={ALL_SECTION}
|
||||||
/>
|
/>
|
||||||
<DeduplicateOptions
|
<DeduplicateOptions
|
||||||
setDialogMessage={setDialogMessage}
|
|
||||||
deleteFileHelper={deleteFileHelper}
|
deleteFileHelper={deleteFileHelper}
|
||||||
count={selected.count}
|
count={selected.count}
|
||||||
close={closeDeduplication}
|
close={closeDeduplication}
|
||||||
|
|
|
@ -596,7 +596,6 @@ export default function Gallery() {
|
||||||
<PlanSelector
|
<PlanSelector
|
||||||
modalView={planModalView}
|
modalView={planModalView}
|
||||||
closeModal={() => setPlanModalView(false)}
|
closeModal={() => setPlanModalView(false)}
|
||||||
setDialogMessage={setDialogMessage}
|
|
||||||
setLoading={setBlockingLoad}
|
setLoading={setBlockingLoad}
|
||||||
/>
|
/>
|
||||||
<AlertBanner bannerMessage={bannerMessage} />
|
<AlertBanner bannerMessage={bannerMessage} />
|
||||||
|
@ -658,7 +657,6 @@ export default function Gallery() {
|
||||||
)}
|
)}
|
||||||
setLoading={setBlockingLoad}
|
setLoading={setBlockingLoad}
|
||||||
setCollectionNamerAttributes={setCollectionNamerAttributes}
|
setCollectionNamerAttributes={setCollectionNamerAttributes}
|
||||||
setDialogMessage={setDialogMessage}
|
|
||||||
setUploadInProgress={setUploadInProgress}
|
setUploadInProgress={setUploadInProgress}
|
||||||
fileRejections={fileRejections}
|
fileRejections={fileRejections}
|
||||||
setFiles={setFiles}
|
setFiles={setFiles}
|
||||||
|
@ -719,7 +717,6 @@ export default function Gallery() {
|
||||||
showCreateCollectionModal={
|
showCreateCollectionModal={
|
||||||
showCreateCollectionModal
|
showCreateCollectionModal
|
||||||
}
|
}
|
||||||
setDialogMessage={setDialogMessage}
|
|
||||||
setCollectionSelectorAttributes={
|
setCollectionSelectorAttributes={
|
||||||
setCollectionSelectorAttributes
|
setCollectionSelectorAttributes
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,16 @@ import {
|
||||||
} from 'utils/crypto';
|
} from 'utils/crypto';
|
||||||
import SetPasswordForm from 'components/SetPasswordForm';
|
import SetPasswordForm from 'components/SetPasswordForm';
|
||||||
import { justSignedUp, setJustSignedUp } from 'utils/storage';
|
import { justSignedUp, setJustSignedUp } from 'utils/storage';
|
||||||
import RecoveryKeyModal from 'components/RecoveryKeyModal';
|
import RecoveryKey from 'components/RecoveryKey';
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered from 'components/Container';
|
||||||
import EnteSpinner from 'components/EnteSpinner';
|
import EnteSpinner from 'components/EnteSpinner';
|
||||||
import { AppContext } from 'pages/_app';
|
import { AppContext } from 'pages/_app';
|
||||||
import { logError } from 'utils/sentry';
|
import { logError } from 'utils/sentry';
|
||||||
import { KeyAttributes, User } from 'types/user';
|
import { KeyAttributes, User } from 'types/user';
|
||||||
|
import FormContainer from 'components/Form/FormContainer';
|
||||||
|
import FormPaper from 'components/Form/FormPaper';
|
||||||
|
import FormTitle from 'components/Form/FormPaper/Title';
|
||||||
|
|
||||||
export default function Generate() {
|
export default function Generate() {
|
||||||
const [token, setToken] = useState<string>();
|
const [token, setToken] = useState<string>();
|
||||||
|
@ -78,13 +81,13 @@ export default function Generate() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<Container>
|
<VerticallyCentered>
|
||||||
<EnteSpinner>
|
<EnteSpinner>
|
||||||
<span className="sr-only">Loading...</span>
|
<span className="sr-only">Loading...</span>
|
||||||
</EnteSpinner>
|
</EnteSpinner>
|
||||||
</Container>
|
</VerticallyCentered>
|
||||||
) : recoverModalView ? (
|
) : recoverModalView ? (
|
||||||
<RecoveryKeyModal
|
<RecoveryKey
|
||||||
show={recoverModalView}
|
show={recoverModalView}
|
||||||
onHide={() => {
|
onHide={() => {
|
||||||
setRecoveryModalView(false);
|
setRecoveryModalView(false);
|
||||||
|
@ -93,11 +96,16 @@ export default function Generate() {
|
||||||
somethingWentWrong={() => null}
|
somethingWentWrong={() => null}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<SetPasswordForm
|
<FormContainer>
|
||||||
callback={onSubmit}
|
<FormPaper>
|
||||||
buttonText={constants.SET_PASSPHRASE}
|
<FormTitle>{constants.SET_PASSPHRASE}</FormTitle>
|
||||||
back={logoutUser}
|
<SetPasswordForm
|
||||||
/>
|
callback={onSubmit}
|
||||||
|
buttonText={constants.SET_PASSPHRASE}
|
||||||
|
back={logoutUser}
|
||||||
|
/>
|
||||||
|
</FormPaper>
|
||||||
|
</FormContainer>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,6 @@ import EnteSpinner from 'components/EnteSpinner';
|
||||||
import SignUp from 'components/SignUp';
|
import SignUp from 'components/SignUp';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import localForage from 'utils/storage/localForage';
|
import localForage from 'utils/storage/localForage';
|
||||||
import IncognitoWarning from 'components/IncognitoWarning';
|
|
||||||
import { logError } from 'utils/sentry';
|
import { logError } from 'utils/sentry';
|
||||||
import { getAlbumSiteHost, PAGES } from 'constants/pages';
|
import { getAlbumSiteHost, PAGES } from 'constants/pages';
|
||||||
|
|
||||||
|
@ -102,7 +101,7 @@ export default function LandingPage() {
|
||||||
const appContext = useContext(AppContext);
|
const appContext = useContext(AppContext);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [showLogin, setShowLogin] = useState(true);
|
const [showLogin, setShowLogin] = useState(true);
|
||||||
const [blockUsage, setBlockUsage] = useState(false);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
appContext.showNavBar(false);
|
appContext.showNavBar(false);
|
||||||
const currentURL = new URL(window.location.href);
|
const currentURL = new URL(window.location.href);
|
||||||
|
@ -143,7 +142,12 @@ export default function LandingPage() {
|
||||||
await localForage.ready();
|
await localForage.ready();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e, 'usage in incognito mode tried');
|
logError(e, 'usage in incognito mode tried');
|
||||||
setBlockUsage(true);
|
appContext.setDialogMessage({
|
||||||
|
title: constants.LOCAL_STORAGE_NOT_ACCESSIBLE,
|
||||||
|
staticBackdrop: true,
|
||||||
|
nonClosable: true,
|
||||||
|
content: constants.LOCAL_STORAGE_NOT_ACCESSIBLE_MESSAGE,
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
@ -216,7 +220,6 @@ export default function LandingPage() {
|
||||||
)}
|
)}
|
||||||
</SideBox>
|
</SideBox>
|
||||||
</DesktopBox>
|
</DesktopBox>
|
||||||
{blockUsage && <IncognitoWarning />}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Container>
|
</Container>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
|
||||||
import EnteSpinner from 'components/EnteSpinner';
|
import EnteSpinner from 'components/EnteSpinner';
|
||||||
import { AppContext } from 'pages/_app';
|
import { AppContext } from 'pages/_app';
|
||||||
import Login from 'components/Login';
|
import Login from 'components/Login';
|
||||||
import Container from 'components/Container';
|
import VerticallyCentered from 'components/Container';
|
||||||
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
||||||
import { PAGES } from 'constants/pages';
|
import { PAGES } from 'constants/pages';
|
||||||
import FormContainer from 'components/Form/FormContainer';
|
import FormContainer from 'components/Form/FormContainer';
|
||||||
|
@ -30,14 +30,14 @@ export default function Home() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return loading ? (
|
return loading ? (
|
||||||
<Container>
|
<VerticallyCentered>
|
||||||
<EnteSpinner>
|
<EnteSpinner>
|
||||||
<span className="sr-only">Loading...</span>
|
<span className="sr-only">Loading...</span>
|
||||||
</EnteSpinner>
|
</EnteSpinner>
|
||||||
</Container>
|
</VerticallyCentered>
|
||||||
) : (
|
) : (
|
||||||
<FormContainer>
|
<FormContainer>
|
||||||
<FormPaper sx={{ minWidth: '320px' }}>
|
<FormPaper>
|
||||||
<Login signUp={register} />
|
<Login signUp={register} />
|
||||||
</FormPaper>
|
</FormPaper>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
|
|
|
@ -13,14 +13,15 @@ import CryptoWorker, {
|
||||||
SaveKeyInSessionStore,
|
SaveKeyInSessionStore,
|
||||||
} from 'utils/crypto';
|
} from 'utils/crypto';
|
||||||
import SingleInputForm from 'components/SingleInputForm';
|
import SingleInputForm from 'components/SingleInputForm';
|
||||||
import MessageDialog from 'components/MessageDialog';
|
import VerticallyCentered from 'components/Container';
|
||||||
import Container from 'components/Container';
|
import { Button } from 'react-bootstrap';
|
||||||
import { Card, Button } from 'react-bootstrap';
|
|
||||||
import { AppContext } from 'pages/_app';
|
import { AppContext } from 'pages/_app';
|
||||||
import LogoImg from 'components/LogoImg';
|
|
||||||
import { logError } from 'utils/sentry';
|
import { logError } from 'utils/sentry';
|
||||||
import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage';
|
import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage';
|
||||||
import { KeyAttributes, User } from 'types/user';
|
import { KeyAttributes, User } from 'types/user';
|
||||||
|
import FormPaper from 'components/Form/FormPaper';
|
||||||
|
import FormPaperTitle from 'components/Form/FormPaper/Title';
|
||||||
|
import FormPaperFooter from 'components/Form/FormPaper/Footer';
|
||||||
const bip39 = require('bip39');
|
const bip39 = require('bip39');
|
||||||
// mobile client library only supports english.
|
// mobile client library only supports english.
|
||||||
bip39.setDefaultWordlist('english');
|
bip39.setDefaultWordlist('english');
|
||||||
|
@ -28,7 +29,6 @@ bip39.setDefaultWordlist('english');
|
||||||
export default function Recover() {
|
export default function Recover() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>();
|
const [keyAttributes, setKeyAttributes] = useState<KeyAttributes>();
|
||||||
const [messageDialogView, SetMessageDialogView] = useState(false);
|
|
||||||
const appContext = useContext(AppContext);
|
const appContext = useContext(AppContext);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -78,49 +78,33 @@ export default function Recover() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showNoRecoveryKeyMessage = () => {
|
||||||
|
appContext.setDialogMessage({
|
||||||
|
title: constants.SORRY,
|
||||||
|
close: {},
|
||||||
|
content: constants.NO_RECOVERY_KEY_MESSAGE,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<VerticallyCentered>
|
||||||
<Container>
|
<FormPaper>
|
||||||
<Card style={{ minWidth: '320px' }} className="text-center">
|
<FormPaperTitle>{constants.RECOVER_ACCOUNT}</FormPaperTitle>
|
||||||
<Card.Body style={{ padding: '40px 30px' }}>
|
<SingleInputForm
|
||||||
<Card.Title style={{ marginBottom: '32px' }}>
|
callback={recover}
|
||||||
<LogoImg src="/icon.svg" />
|
fieldType="text"
|
||||||
{constants.RECOVER_ACCOUNT}
|
placeholder={constants.RECOVERY_KEY_HINT}
|
||||||
</Card.Title>
|
buttonText={constants.RECOVER}
|
||||||
<SingleInputForm
|
/>
|
||||||
callback={recover}
|
<FormPaperFooter style={{ justifyContent: 'space-between' }}>
|
||||||
fieldType="text"
|
<Button variant="link" onClick={showNoRecoveryKeyMessage}>
|
||||||
placeholder={constants.RETURN_RECOVERY_KEY_HINT}
|
{constants.NO_RECOVERY_KEY}
|
||||||
buttonText={constants.RECOVER}
|
</Button>
|
||||||
/>
|
<Button variant="link" onClick={router.back}>
|
||||||
<div
|
{constants.GO_BACK}
|
||||||
style={{
|
</Button>
|
||||||
display: 'flex',
|
</FormPaperFooter>
|
||||||
flexDirection: 'column',
|
</FormPaper>
|
||||||
marginTop: '12px',
|
</VerticallyCentered>
|
||||||
}}>
|
|
||||||
<Button
|
|
||||||
variant="link"
|
|
||||||
onClick={() => SetMessageDialogView(true)}>
|
|
||||||
{constants.NO_RECOVERY_KEY}
|
|
||||||
</Button>
|
|
||||||
<Button variant="link" onClick={router.back}>
|
|
||||||
{constants.GO_BACK}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Card.Body>
|
|
||||||
</Card>
|
|
||||||
</Container>
|
|
||||||
<MessageDialog
|
|
||||||
size="lg"
|
|
||||||
show={messageDialogView}
|
|
||||||
onHide={() => SetMessageDialogView(false)}
|
|
||||||
attributes={{
|
|
||||||
title: constants.SORRY,
|
|
||||||
close: {},
|
|
||||||
content: constants.NO_RECOVERY_KEY_MESSAGE,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue