Merge branch 'ui-redesign' into upload-redesign

This commit is contained in:
Abhinav 2022-05-24 14:20:58 +05:30
commit 7eda72902b
115 changed files with 1669 additions and 1440 deletions

View file

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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>
<FormPaperFooter
style={{
justifyContent: ottInputVisible && 'space-between',
}}>
{ottInputVisible && ( {ottInputVisible && (
<LinkButton <LinkButton
onClick={() => setShowOttInputVisibility(false)}> onClick={() =>
setShowOttInputVisibility(false)
}>
{constants.CHANGE_EMAIL}? {constants.CHANGE_EMAIL}?
</LinkButton> </LinkButton>
)} )}
<LinkButton onClick={goToGallery}>
{constants.GO_BACK}
</LinkButton>
</FormPaperFooter>
</> </>
)} )}
</Formik> </Formik>

View file

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

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

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

View 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;
`;

View file

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

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

View file

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

View file

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

View file

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

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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>
<SpaceBetweenFlex>
{attributes?.title} {attributes?.title}
</DialogTitleWithCloseButton> <IconButton onClick={props.onHide}>
<Close />
</IconButton>
</SpaceBetweenFlex>
</DialogTitle>
<DialogContent> <DialogContent>
<Formik<formValues> <Formik<formValues>
initialValues={{ initialValues={{

View file

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

View file

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

View file

@ -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': {
justifyContent: 'flex-end',
},
'& .MuiPaper-root': {
maxWidth: '498px',
},
'& .MuiDialogTitle-root': {
padding: theme.spacing(3, 2),
},
'& .MuiDialogContent-root': { '& .MuiDialogContent-root': {
padding: theme.spacing(2), padding: theme.spacing(2),
}, },
'& .MuiDialogActions-root': { }));
padding: theme.spacing(1),
},
'& .MuiPaper-root': {
maxWidth: '510px',
},
'& .MuiDialog-container': {
justifyContent: position === 'left' ? 'flex-start' : 'flex-end',
},
})
);
FloatingDrawer.propTypes = { FloatingDrawer.propTypes = {
children: PropTypes.node, children: PropTypes.node,

View file

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

View file

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

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

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,12 @@
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 (
<>
<Divider />
<Container <Container
disableGutters
style={{ flexDirection: 'row', ...style }} style={{ flexDirection: 'row', ...style }}
sx={{ sx={{
mt: 3, mt: 3,
@ -14,6 +15,7 @@ const FormPaperFooter: FC<ContainerProps> = ({ sx, style, ...props }) => {
{...props}> {...props}>
{props.children} {props.children}
</Container> </Container>
</>
); );
}; };

View file

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

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

View file

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

View file

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

View file

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

View file

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

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

View 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),
}));

View file

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

View file

@ -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,21 +60,14 @@ 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
onSubmit={handleSubmit}>
<Container disableGutters>
<Typography mb={2}>
{constants.ENTER_ENC_PASSPHRASE} {constants.ENTER_ENC_PASSPHRASE}
</Typography> </Typography>
<Typography mb={2}>
{constants.PASSPHRASE_DISCLAIMER()}
</Typography>
<Container>
<TextField <TextField
margin="normal"
fullWidth fullWidth
variant="filled"
type="password" type="password"
label={constants.PASSPHRASE_HINT} label={constants.PASSPHRASE_HINT}
value={values.passphrase} value={values.passphrase}
@ -77,6 +79,7 @@ function SetPasswordForm(props: Props) {
/> />
<TextField <TextField
fullWidth fullWidth
variant="filled"
type="password" type="password"
label={constants.CONFIRM_PASSPHRASE} label={constants.CONFIRM_PASSPHRASE}
value={values.confirm} value={values.confirm}
@ -85,12 +88,15 @@ function SetPasswordForm(props: Props) {
error={Boolean(errors.confirm)} error={Boolean(errors.confirm)}
helperText={errors.confirm} helperText={errors.confirm}
/> />
<Typography my={2} variant="body2">
{constants.PASSPHRASE_DISCLAIMER()}
</Typography>
<SubmitButton <SubmitButton
loading={loading} loading={loading}
buttonText={props.buttonText} buttonText={props.buttonText}
/> />
</Container>
</Container>
</form> </form>
)} )}
</Formik> </Formik>

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

@ -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) => {
if (theme !== null) {
setTheme(theme); setTheme(theme);
}
}; };
return ( return (
<ToggleButtonGroup <ToggleButtonGroup
color="primary"
size="small" size="small"
value={theme} value={theme}
exclusive exclusive

View file

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

View file

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

View file

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

View 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} />
</>
);
}

View file

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

View file

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

View file

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

View file

@ -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%' }}>

View file

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

View file

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

View file

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

View file

@ -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 && (
<CenteredFlex sx={{ mt: 1 }}>
<InvalidInputMessage> <InvalidInputMessage>
{constants.INCORRECT_CODE} {constants.INCORRECT_CODE}
</InvalidInputMessage> </InvalidInputMessage>
</CenteredFlex>
)} )}
</Box> </Box>
<SubmitButton <SubmitButton

View file

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

View file

@ -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',
};

View file

@ -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',
};

View file

@ -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',
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,14 +78,9 @@ export default function ChangePassword() {
}; };
return ( return (
<Container> <VerticallyCentered>
<Card sx={{ maxWidth: '520px' }}> <FormPaper>
<CardContent> <FormPaperTitle>{constants.CHANGE_PASSWORD}</FormPaperTitle>
<Container disableGutters sx={{ pt: 3 }}>
<Box mb={4}>
<LogoImg src="/icon.svg" />
{constants.CHANGE_PASSWORD}
</Box>
<SetPasswordForm <SetPasswordForm
callback={onSubmit} callback={onSubmit}
buttonText={constants.CHANGE_PASSWORD} buttonText={constants.CHANGE_PASSWORD}
@ -95,12 +90,13 @@ export default function ChangePassword() {
: null : null
} }
/> />
<LinkButton sx={{ mt: 2 }} onClick={router.back}>
<FormPaperFooter>
<LinkButton onClick={router.back}>
{constants.GO_BACK} {constants.GO_BACK}
</LinkButton> </LinkButton>
</Container> </FormPaperFooter>
</CardContent> </FormPaper>
</Card> </VerticallyCentered>
</Container>
); );
} }

View file

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

View file

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

View file

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

View file

@ -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}
/> />
) : ( ) : (
<FormContainer>
<FormPaper>
<FormTitle>{constants.SET_PASSPHRASE}</FormTitle>
<SetPasswordForm <SetPasswordForm
callback={onSubmit} callback={onSubmit}
buttonText={constants.SET_PASSPHRASE} buttonText={constants.SET_PASSPHRASE}
back={logoutUser} back={logoutUser}
/> />
</FormPaper>
</FormContainer>
)} )}
</> </>
); );

View file

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

View file

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

View file

@ -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' }}>
<Card.Title style={{ marginBottom: '32px' }}>
<LogoImg src="/icon.svg" />
{constants.RECOVER_ACCOUNT}
</Card.Title>
<SingleInputForm <SingleInputForm
callback={recover} callback={recover}
fieldType="text" fieldType="text"
placeholder={constants.RETURN_RECOVERY_KEY_HINT} placeholder={constants.RECOVERY_KEY_HINT}
buttonText={constants.RECOVER} buttonText={constants.RECOVER}
/> />
<div <FormPaperFooter style={{ justifyContent: 'space-between' }}>
style={{ <Button variant="link" onClick={showNoRecoveryKeyMessage}>
display: 'flex',
flexDirection: 'column',
marginTop: '12px',
}}>
<Button
variant="link"
onClick={() => SetMessageDialogView(true)}>
{constants.NO_RECOVERY_KEY} {constants.NO_RECOVERY_KEY}
</Button> </Button>
<Button variant="link" onClick={router.back}> <Button variant="link" onClick={router.back}>
{constants.GO_BACK} {constants.GO_BACK}
</Button> </Button>
</div> </FormPaperFooter>
</Card.Body> </FormPaper>
</Card> </VerticallyCentered>
</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