Merge pull request #607 from ente-io/email-share-menu

Email share menu
This commit is contained in:
Abhinav Kumar 2022-06-21 15:18:13 +05:30 committed by GitHub
commit e838421b72
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 196 additions and 170 deletions

View file

@ -1,44 +1,20 @@
import React, { useState } from 'react'; import React from 'react';
import { COLLECTION_SORT_BY } from 'constants/collection'; 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 SortIcon from '@mui/icons-material/Sort';
import CollectionSortOptions from './options'; import CollectionSortOptions from './options';
import OverflowMenu from 'components/OverflowMenu/menu';
export interface CollectionSortProps { export interface CollectionSortProps {
setCollectionSortBy: (sortBy: COLLECTION_SORT_BY) => void; setCollectionSortBy: (sortBy: COLLECTION_SORT_BY) => void;
activeSortBy: COLLECTION_SORT_BY; 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) { export default function CollectionSort(props: CollectionSortProps) {
const [sortByEl, setSortByEl] = useState(null);
const handleClose = () => setSortByEl(null);
return ( return (
<> <OverflowMenu
<IconButton ariaControls="collection-sort"
onClick={(event) => setSortByEl(event.currentTarget)} triggerButtonIcon={<SortIcon />}>
aria-controls={sortByEl ? 'collection-sort' : undefined} <CollectionSortOptions {...props} />
aria-haspopup="true" </OverflowMenu>
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

@ -1,29 +1,26 @@
import React from 'react'; import React, { useContext } from 'react';
import { MenuItem, ListItemIcon, ListItemText } from '@mui/material';
import { COLLECTION_SORT_BY } from 'constants/collection'; import { COLLECTION_SORT_BY } from 'constants/collection';
import TickIcon from '@mui/icons-material/Done'; import TickIcon from '@mui/icons-material/Done';
import { CollectionSortProps } from '.'; import { CollectionSortProps } from '.';
import { OverflowMenuContext } from 'contexts/overflowMenu';
export interface SortOptionProps extends CollectionSortProps { import { OverflowMenuOption } from 'components/OverflowMenu/option';
close: () => void;
}
const SortByOptionCreator = const SortByOptionCreator =
({ setCollectionSortBy, activeSortBy, close }: SortOptionProps) => ({ setCollectionSortBy, activeSortBy }: CollectionSortProps) =>
(props: { sortBy: COLLECTION_SORT_BY; children: any }) => { (props: { sortBy: COLLECTION_SORT_BY; children: any }) => {
const { close } = useContext(OverflowMenuContext);
const handleClick = () => { const handleClick = () => {
setCollectionSortBy(props.sortBy); setCollectionSortBy(props.sortBy);
close(); close();
}; };
return ( return (
<MenuItem onClick={handleClick} style={{ paddingLeft: '5px' }}> <OverflowMenuOption
<ListItemIcon style={{ minWidth: '25px' }}> onClick={handleClick}
{activeSortBy === props.sortBy && ( startIcon={activeSortBy === props.sortBy && <TickIcon />}>
<TickIcon sx={{ fontSize: 16 }} /> {props.children}
)} </OverflowMenuOption>
</ListItemIcon>
<ListItemText>{props.children}</ListItemText>
</MenuItem>
); );
}; };

View file

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import { MenuList } from '@mui/material';
import { COLLECTION_SORT_BY } from 'constants/collection'; import { COLLECTION_SORT_BY } from 'constants/collection';
import constants from 'utils/strings/constants'; import constants from 'utils/strings/constants';
import SortByOptionCreator, { SortOptionProps } from './optionCreator'; import SortByOptionCreator from './optionCreator';
import { CollectionSortProps } from '.';
export default function CollectionSortOptions(props: SortOptionProps) { export default function CollectionSortOptions(props: CollectionSortProps) {
const SortByOption = SortByOptionCreator(props); const SortByOption = SortByOptionCreator(props);
return ( return (
<MenuList> <>
<SortByOption sortBy={COLLECTION_SORT_BY.NAME}> <SortByOption sortBy={COLLECTION_SORT_BY.NAME}>
{constants.SORT_BY_NAME} {constants.SORT_BY_NAME}
</SortByOption> </SortByOption>
@ -21,6 +21,6 @@ export default function CollectionSortOptions(props: SortOptionProps) {
<SortByOption sortBy={COLLECTION_SORT_BY.UPDATION_TIME_DESCENDING}> <SortByOption sortBy={COLLECTION_SORT_BY.UPDATION_TIME_DESCENDING}>
{constants.SORT_BY_UPDATION_TIME_DESCENDING} {constants.SORT_BY_UPDATION_TIME_DESCENDING}
</SortByOption> </SortByOption>
</MenuList> </>
); );
} }

View file

@ -1,4 +1,4 @@
import React, { useContext, useState } from 'react'; import React, { useContext } from 'react';
import * as CollectionAPI from 'services/collectionService'; import * as CollectionAPI from 'services/collectionService';
import { import {
changeCollectionVisibility, changeCollectionVisibility,
@ -8,15 +8,13 @@ import constants from 'utils/strings/constants';
import { SetCollectionNamerAttributes } from './CollectionNamer'; import { SetCollectionNamerAttributes } from './CollectionNamer';
import { Collection } from 'types/collection'; import { Collection } from 'types/collection';
import { IsArchived } from 'utils/magicMetadata'; import { IsArchived } from 'utils/magicMetadata';
import { InvertedIconButton } from 'components/Container';
import OptionIcon from 'components/icons/OptionIcon-2';
import Paper from '@mui/material/Paper';
import MenuList from '@mui/material/MenuList';
import { ListItem, Menu, MenuItem } from '@mui/material';
import { GalleryContext } from 'pages/gallery'; import { GalleryContext } from 'pages/gallery';
import { logError } from 'utils/sentry'; import { logError } from 'utils/sentry';
import { VISIBILITY_STATE } from 'types/magicMetadata'; import { VISIBILITY_STATE } from 'types/magicMetadata';
import { AppContext } from 'pages/_app'; import { AppContext } from 'pages/_app';
import OverflowMenu from 'components/OverflowMenu/menu';
import { OverflowMenuOption } from 'components/OverflowMenu/option';
import MoreVertIcon from '@mui/icons-material/MoreVert';
interface CollectionOptionsProps { interface CollectionOptionsProps {
setCollectionNamerAttributes: SetCollectionNamerAttributes; setCollectionNamerAttributes: SetCollectionNamerAttributes;
@ -44,9 +42,6 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
useContext(AppContext); useContext(AppContext);
const { syncWithRemote } = useContext(GalleryContext); const { syncWithRemote } = useContext(GalleryContext);
const [optionEl, setOptionEl] = useState(null);
const handleClose = () => setOptionEl(null);
const handleCollectionAction = (action: CollectionActions) => { const handleCollectionAction = (action: CollectionActions) => {
let callback; let callback;
switch (action) { switch (action) {
@ -78,7 +73,6 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
startLoading(); startLoading();
try { try {
await callback(...args); await callback(...args);
handleClose();
} catch (e) { } catch (e) {
setDialogMessage({ setDialogMessage({
title: constants.ERROR, title: constants.ERROR,
@ -155,71 +149,42 @@ const CollectionOptions = (props: CollectionOptionsProps) => {
}; };
return ( return (
<> <OverflowMenu
<InvertedIconButton ariaControls={`collection-options-${props.activeCollection.id}`}
style={{ triggerButtonIcon={<MoreVertIcon />}
transform: 'rotate(90deg)', triggerButtonProps={{
}} sx: {
onClick={(event) => setOptionEl(event.currentTarget)} background: (theme) => theme.palette.background.paper,
aria-controls={optionEl ? 'collection-options' : undefined} },
aria-haspopup="true" }}>
aria-expanded={optionEl ? 'true' : undefined}> <OverflowMenuOption onClick={showRenameCollectionModal}>
<OptionIcon /> {constants.RENAME}
</InvertedIconButton> </OverflowMenuOption>
<Menu <OverflowMenuOption onClick={showCollectionShareModal}>
id="collection-options" {constants.SHARE}
anchorEl={optionEl} </OverflowMenuOption>
open={Boolean(optionEl)} <OverflowMenuOption onClick={confirmDownloadCollection}>
onClose={handleClose} {constants.DOWNLOAD}
MenuListProps={{ </OverflowMenuOption>
disablePadding: true, {IsArchived(activeCollection) ? (
'aria-labelledby': 'collection-options', <OverflowMenuOption
}}> onClick={handleCollectionAction(
<Paper> CollectionActions.UNARCHIVE
<MenuList> )}>
<MenuItem> {constants.UNARCHIVE}
<ListItem onClick={showRenameCollectionModal}> </OverflowMenuOption>
{constants.RENAME} ) : (
</ListItem> <OverflowMenuOption
</MenuItem> onClick={handleCollectionAction(CollectionActions.ARCHIVE)}>
<MenuItem> {constants.ARCHIVE}
<ListItem onClick={showCollectionShareModal}> </OverflowMenuOption>
{constants.SHARE} )}
</ListItem> <OverflowMenuOption
</MenuItem> color="danger"
<MenuItem> onClick={confirmDeleteCollection}>
<ListItem onClick={confirmDownloadCollection}> {constants.DELETE}
{constants.DOWNLOAD} </OverflowMenuOption>
</ListItem> </OverflowMenu>
</MenuItem>
<MenuItem>
{IsArchived(activeCollection) ? (
<ListItem
onClick={handleCollectionAction(
CollectionActions.UNARCHIVE
)}>
{constants.UNARCHIVE}
</ListItem>
) : (
<ListItem
onClick={handleCollectionAction(
CollectionActions.ARCHIVE
)}>
{constants.ARCHIVE}
</ListItem>
)}
</MenuItem>
<MenuItem>
<ListItem
color="danger"
onClick={confirmDeleteCollection}>
{constants.DELETE}
</ListItem>
</MenuItem>
</MenuList>
</Paper>
</Menu>
</>
); );
}; };

View file

@ -1,22 +1,34 @@
import React from 'react'; import React from 'react';
import { IconButton } from '@mui/material';
import { SpaceBetweenFlex } from 'components/Container'; import { SpaceBetweenFlex } from 'components/Container';
import { User } from 'types/user'; import { User } from 'types/user';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import OverflowMenu from 'components/OverflowMenu/menu';
import NotInterestedIcon from '@mui/icons-material/NotInterested';
import constants from 'utils/strings/constants';
import { OverflowMenuOption } from 'components/OverflowMenu/option';
interface IProps { interface IProps {
sharee: User; sharee: User;
collectionUnshare: (sharee: User) => void; collectionUnshare: (sharee: User) => void;
} }
const ShareeRow = ({ sharee, collectionUnshare }: IProps) => ( const ShareeRow = ({ sharee, collectionUnshare }: IProps) => {
<SpaceBetweenFlex> const handleClick = () => collectionUnshare(sharee);
{sharee.email} return (
<IconButton <SpaceBetweenFlex>
sx={{ ml: 2, color: 'text.secondary' }} {sharee.email}
onClick={() => collectionUnshare(sharee)}> <OverflowMenu
<MoreHorizIcon /> ariaControls={`email-share-${sharee.email}`}
</IconButton> triggerButtonIcon={<MoreHorizIcon />}>
</SpaceBetweenFlex> <OverflowMenuOption
); color="danger"
onClick={handleClick}
startIcon={<NotInterestedIcon />}>
{constants.REMOVE}
</OverflowMenuOption>
</OverflowMenu>
</SpaceBetweenFlex>
);
};
export default ShareeRow; export default ShareeRow;

View file

@ -0,0 +1,63 @@
import React, { useState } from 'react';
import Menu from '@mui/material/Menu';
import { IconButton, styled } from '@mui/material';
import { OverflowMenuContext } from 'contexts/overflowMenu';
export interface Iprops {
triggerButtonIcon: React.ReactNode;
triggerButtonProps?: any;
children?: React.ReactNode;
ariaControls: string;
}
const StyledMenu = styled(Menu)`
& .MuiPaper-root {
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.16),
0px 3px 6px rgba(0, 0, 0, 0.12);
}
& .MuiList-root {
padding: 0;
border: none;
}
`;
export default function OverflowMenu({
children,
ariaControls,
triggerButtonIcon,
triggerButtonProps,
}: Iprops) {
const [sortByEl, setSortByEl] = useState(null);
const handleClose = () => setSortByEl(null);
return (
<OverflowMenuContext.Provider value={{ close: handleClose }}>
<IconButton
onClick={(event) => setSortByEl(event.currentTarget)}
aria-controls={sortByEl ? ariaControls : undefined}
aria-haspopup="true"
aria-expanded={sortByEl ? 'true' : undefined}
{...triggerButtonProps}>
{triggerButtonIcon}
</IconButton>
<StyledMenu
id={ariaControls}
anchorEl={sortByEl}
open={Boolean(sortByEl)}
onClose={handleClose}
MenuListProps={{
disablePadding: true,
'aria-labelledby': ariaControls,
}}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'center',
horizontal: 'right',
}}>
{children}
</StyledMenu>
</OverflowMenuContext.Provider>
);
}

View file

@ -0,0 +1,37 @@
import { MenuItem, ListItemIcon, ButtonProps, Typography } from '@mui/material';
import React from 'react';
interface Iprops {
onClick: () => void;
color?: ButtonProps['color'];
startIcon?: React.ReactNode;
children?: any;
}
export function OverflowMenuOption({
onClick,
color = 'primary',
startIcon,
children,
}: Iprops) {
return (
<MenuItem
onClick={onClick}
sx={{
color: (theme) => theme.palette[color].main,
padding: '12px',
}}>
{startIcon && (
<ListItemIcon
sx={{
color: 'inherit',
padding: '0',
paddingRight: '12px',
fontSize: '20px',
}}>
{startIcon}
</ListItemIcon>
)}
<Typography variant="button">{children}</Typography>
</MenuItem>
);
}

View file

@ -1,21 +0,0 @@
import React from 'react';
export default function OptionIcon(props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height={props.height}
viewBox={props.viewBox}
width={props.width}
fill="currentColor">
<path d="M0 0h24v24H0V0z" fill="none" />
<path d="M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z" />{' '}
</svg>
);
}
OptionIcon.defaultProps = {
height: 20,
width: 20,
viewBox: '0 0 24 24',
};

View file

@ -0,0 +1,5 @@
import { createContext } from 'react';
export const OverflowMenuContext = createContext({
close: () => null,
});

View file

@ -77,15 +77,6 @@ const darkThemeOptions = createTheme({
}, },
}, },
}, },
MuiMenu: {
styleOverrides: {
paper: { margin: '10px' },
list: {
padding: 0,
border: 'none',
},
},
},
MuiButton: { MuiButton: {
defaultProps: { defaultProps: {
@ -155,16 +146,17 @@ const darkThemeOptions = createTheme({
typography: { typography: {
body1: { body1: {
fontSize: '16px', fontSize: '16px',
lineHeight: '24px', lineHeight: '20px',
}, },
body2: { body2: {
fontSize: '14px', fontSize: '14px',
lineHeight: '20px', lineHeight: '17px',
}, },
button: { button: {
fontSize: '16px', fontSize: '16px',
lineHeight: '19.36px', lineHeight: '20px',
fontWeight: 'bold', fontWeight: 'bold',
textTransform: 'none',
}, },
title: { title: {
fontSize: '32px', fontSize: '32px',