refactor manageParticipants
This commit is contained in:
parent
c55dc07913
commit
061d0a6d61
|
@ -357,7 +357,9 @@
|
|||
"MODIFY_SHARING": "Modify sharing",
|
||||
"ADD_COLLABORATORS": "Add collaborators",
|
||||
"ADD_NEW_EMAIL": "Add a new email",
|
||||
"SHARE_WITH_PEOPLE":"Share with specific people",
|
||||
"shared_with_people_zero" :"Share with specific people",
|
||||
"shared_with_people_one": "Shared with 1 person",
|
||||
"shared_with_people_other": "Shared with {{count, number}} people",
|
||||
"ADD_VIEWERS":"Add viewers",
|
||||
"PARTICIPANTS": " Participants",
|
||||
"CHANGE_PERMISSIONS_TO_VIEWER": "<p>{{selectedEmail}} will not be able to add more photos to the album</p> <p>They will still be able to remove photos added by them</p>",
|
||||
|
@ -373,6 +375,7 @@
|
|||
"REMOVE_PARTICIPANT_HEAD": "Remove participant",
|
||||
"OWNER": "Owner",
|
||||
"COLLABORATORS": "Collaborators",
|
||||
"ADD_MORE": "Add more",
|
||||
"VIEWERS": "Viewers",
|
||||
"OR_ADD_EXISTING": "or an add existing one",
|
||||
"REMOVE_PARTICIPANT_MESSAGE": "<p>{{selectedEmail}} will be removed from the album</p> <p>Any photos added by them will also be removed from the album</p>",
|
||||
|
|
|
@ -141,56 +141,64 @@ export default function AddParticipantForm(props: AddParticipantFormProps) {
|
|||
autoComplete={props.autoComplete}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<MenuSectionTitle title={t('OR_ADD_EXISTING')} />
|
||||
<MenuItemGroup>
|
||||
{updatedOptionsList.map((item, index) => (
|
||||
<>
|
||||
<EnteMenuItem
|
||||
fontWeight="normal"
|
||||
key={item}
|
||||
onClick={() => {
|
||||
if (
|
||||
|
||||
{updatedOptionsList.length > 0 && (
|
||||
<Stack>
|
||||
<MenuSectionTitle
|
||||
title={t('OR_ADD_EXISTING')}
|
||||
/>
|
||||
<MenuItemGroup>
|
||||
{updatedOptionsList.map((item, index) => (
|
||||
<>
|
||||
<EnteMenuItem
|
||||
fontWeight="normal"
|
||||
key={item}
|
||||
onClick={() => {
|
||||
if (
|
||||
values.selectedOptions.includes(
|
||||
item
|
||||
)
|
||||
) {
|
||||
setFieldValue(
|
||||
'selectedOptions',
|
||||
values.selectedOptions.filter(
|
||||
(
|
||||
selectedOption
|
||||
) =>
|
||||
selectedOption !==
|
||||
item
|
||||
)
|
||||
);
|
||||
} else {
|
||||
setFieldValue(
|
||||
'selectedOptions',
|
||||
[
|
||||
...values.selectedOptions,
|
||||
item,
|
||||
]
|
||||
);
|
||||
}
|
||||
}}
|
||||
label={item}
|
||||
startIcon={
|
||||
<Avatar email={item} />
|
||||
}
|
||||
endIcon={
|
||||
values.selectedOptions.includes(
|
||||
item
|
||||
)
|
||||
) {
|
||||
setFieldValue(
|
||||
'selectedOptions',
|
||||
values.selectedOptions.filter(
|
||||
(selectedOption) =>
|
||||
selectedOption !==
|
||||
item
|
||||
)
|
||||
);
|
||||
} else {
|
||||
setFieldValue(
|
||||
'selectedOptions',
|
||||
[
|
||||
...values.selectedOptions,
|
||||
item,
|
||||
]
|
||||
);
|
||||
) ? (
|
||||
<DoneIcon />
|
||||
) : null
|
||||
}
|
||||
}}
|
||||
label={item}
|
||||
startIcon={<Avatar email={item} />}
|
||||
endIcon={
|
||||
values.selectedOptions.includes(
|
||||
item
|
||||
) ? (
|
||||
<DoneIcon />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
{index !==
|
||||
props.optionsList.length - 1 && (
|
||||
<MenuItemDivider />
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</MenuItemGroup>
|
||||
</Stack>
|
||||
/>
|
||||
{index !==
|
||||
props.optionsList.length -
|
||||
1 && <MenuItemDivider />}
|
||||
</>
|
||||
))}
|
||||
</MenuItemGroup>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<FormHelperText
|
||||
sx={{
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
import { Box, Typography } from '@mui/material';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { t } from 'i18next';
|
||||
import { Collection } from 'types/collection';
|
||||
import { MenuItemGroup } from 'components/Menu/MenuItemGroup';
|
||||
import { EnteMenuItem } from 'components/Menu/EnteMenuItem';
|
||||
import Avatar from 'components/pages/gallery/Avatar';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import MenuItemDivider from 'components/Menu/MenuItemDivider';
|
||||
import AddCollab from './AddCollab';
|
||||
import ModeEditIcon from '@mui/icons-material/ModeEdit';
|
||||
import ManageParticipantsRole from './ManageParticipantsRole';
|
||||
|
||||
interface Iprops {
|
||||
collection: Collection;
|
||||
onRootClose: () => void;
|
||||
collectionUnshare: (email: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export function CollaboratorParticipants({
|
||||
collection,
|
||||
onRootClose,
|
||||
collectionUnshare,
|
||||
}: Iprops) {
|
||||
if (!collection.sharees?.length) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const [collaborators, setCollaborators] = useState([]);
|
||||
const [participantRoleView, setParticipantRoleView] = useState(false);
|
||||
const [selectedEmail, setSelectedEmail] = useState('');
|
||||
|
||||
const openParticipantRoleView = (email) => {
|
||||
setParticipantRoleView(true);
|
||||
setSelectedEmail(email);
|
||||
};
|
||||
const closeParticipantRoleView = () => {
|
||||
setParticipantRoleView(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const collaboratorEmails =
|
||||
collection.sharees
|
||||
?.filter((sharee) => sharee.role === 'COLLABORATOR')
|
||||
.map((sharee) => sharee.email) || [];
|
||||
setCollaborators(collaboratorEmails);
|
||||
}, [collection.sharees]);
|
||||
|
||||
return (
|
||||
<Box mb={3}>
|
||||
<Typography color="text.muted" variant="small" padding={1}>
|
||||
<ModeEditIcon style={{ fontSize: 20, marginRight: 8 }} />
|
||||
{t('Collaborators')}
|
||||
</Typography>
|
||||
|
||||
<MenuItemGroup>
|
||||
{collaborators.map((item, index) => (
|
||||
<>
|
||||
<EnteMenuItem
|
||||
fontWeight="normal"
|
||||
key={item}
|
||||
onClick={() => openParticipantRoleView(item)}
|
||||
label={item}
|
||||
startIcon={<Avatar email={item} />}
|
||||
endIcon={<ChevronRightIcon />}
|
||||
/>
|
||||
{index !== collaborators.length - 1 && (
|
||||
<MenuItemDivider />
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
<AddCollab
|
||||
open
|
||||
onClose={() => {}}
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
/>
|
||||
</MenuItemGroup>
|
||||
<ManageParticipantsRole
|
||||
collectionUnshare={collectionUnshare}
|
||||
open={participantRoleView}
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
onClose={closeParticipantRoleView}
|
||||
selectedEmail={selectedEmail} // Pass the selected email to ManageParticipantsRole
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1,100 +1,222 @@
|
|||
import { Stack, Typography, styled } from '@mui/material';
|
||||
import { Stack } from '@mui/material';
|
||||
import { COLLECTION_ROLE, Collection } from 'types/collection';
|
||||
import { EnteDrawer } from 'components/EnteDrawer';
|
||||
import { t } from 'i18next';
|
||||
import { DialogProps } from '@mui/material';
|
||||
import Titlebar from 'components/Titlebar';
|
||||
import { useContext, useState } from 'react';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import { unshareCollection } from 'services/collectionService';
|
||||
import { EnteMenuItem } from 'components/Menu/EnteMenuItem';
|
||||
import { MenuItemGroup } from 'components/Menu/MenuItemGroup';
|
||||
import { Collection } from 'types/collection';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import { useState } from 'react';
|
||||
import { t } from 'i18next';
|
||||
import Avatar from 'components/pages/gallery/Avatar';
|
||||
import MenuSectionTitle from 'components/Menu/MenuSectionTitle';
|
||||
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
|
||||
import ModeEditIcon from '@mui/icons-material/ModeEdit';
|
||||
import MenuItemDivider from 'components/Menu/MenuItemDivider';
|
||||
import NumberAvatar from '@mui/material/Avatar';
|
||||
import ManageParticipantsOptions from './ManageParticipantsOptions';
|
||||
import Workspaces from '@mui/icons-material/Workspaces';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import ModifyParticipant from './ModifyParticipant';
|
||||
import AddParticipant from './AddParticipant';
|
||||
import { useRef } from 'react';
|
||||
import Add from '@mui/icons-material/Add';
|
||||
|
||||
interface Iprops {
|
||||
collection: Collection;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onRootClose: () => void;
|
||||
peopleCount: number;
|
||||
}
|
||||
|
||||
const AvatarContainer = styled('div')({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginLeft: -5,
|
||||
});
|
||||
|
||||
const AvatarContainerOuter = styled('div')({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginLeft: 8,
|
||||
});
|
||||
const AvatarCounter = styled(NumberAvatar)({
|
||||
height: 20,
|
||||
width: 20,
|
||||
fontSize: 10,
|
||||
color: '#fff',
|
||||
});
|
||||
|
||||
export default function ManageParticipants({
|
||||
open,
|
||||
collection,
|
||||
onClose,
|
||||
onRootClose,
|
||||
peopleCount,
|
||||
}: Iprops) {
|
||||
const [manageAddViewer, setManageAddViewer] = useState(false);
|
||||
const closeManageAddViewer = () => setManageAddViewer(false);
|
||||
const openManageAddViewer = () => setManageAddViewer(true);
|
||||
const peopleCount = collection.sharees.length;
|
||||
const appContext = useContext(AppContext);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
|
||||
const [addParticipantView, setAddParticipantView] = useState(false);
|
||||
const [participantRoleView, setParticipantRoleView] = useState(false);
|
||||
const [selectedEmail, setSelectedEmail] = useState('');
|
||||
|
||||
const closeAddParticipant = () => setAddParticipantView(false);
|
||||
const openAddParticipant = () => setAddParticipantView(true);
|
||||
|
||||
const participantType = useRef<
|
||||
COLLECTION_ROLE.COLLABORATOR | COLLECTION_ROLE.VIEWER
|
||||
>();
|
||||
|
||||
const openAddCollab = () => {
|
||||
participantType.current = COLLECTION_ROLE.COLLABORATOR;
|
||||
openAddParticipant();
|
||||
};
|
||||
|
||||
const openAddViewer = () => {
|
||||
participantType.current = COLLECTION_ROLE.VIEWER;
|
||||
openAddParticipant();
|
||||
};
|
||||
|
||||
const handleRootClose = () => {
|
||||
onClose();
|
||||
onRootClose();
|
||||
};
|
||||
const handleDrawerClose: DialogProps['onClose'] = (_, reason) => {
|
||||
if (reason === 'backdropClick') {
|
||||
handleRootClose();
|
||||
} else {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const collectionUnshare = async (email: string) => {
|
||||
try {
|
||||
appContext.startLoading();
|
||||
await unshareCollection(collection, email);
|
||||
await galleryContext.syncWithRemote(false, true);
|
||||
} finally {
|
||||
appContext.finishLoading();
|
||||
}
|
||||
};
|
||||
|
||||
const ownerEmail =
|
||||
galleryContext.user.id === collection.owner?.id
|
||||
? galleryContext.user.email
|
||||
: collection.owner?.email;
|
||||
|
||||
const isOwner = galleryContext.user.id === collection.owner?.id;
|
||||
|
||||
const collaborators = collection.sharees
|
||||
?.filter((sharee) => sharee.role === COLLECTION_ROLE.COLLABORATOR)
|
||||
.map((sharee) => sharee.email);
|
||||
|
||||
const viewers =
|
||||
collection.sharees
|
||||
?.filter((sharee) => sharee.role === COLLECTION_ROLE.VIEWER)
|
||||
.map((sharee) => sharee.email) || [];
|
||||
|
||||
const openParticipantRoleView = (email) => {
|
||||
setParticipantRoleView(true);
|
||||
setSelectedEmail(email);
|
||||
};
|
||||
const closeParticipantRoleView = () => {
|
||||
setParticipantRoleView(false);
|
||||
};
|
||||
|
||||
const shareeCount = collection.sharees?.length || 0;
|
||||
const extraShareeCount = Math.max(0, shareeCount - 6);
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<Typography color="text.muted" variant="small" padding={1}>
|
||||
<Workspaces style={{ fontSize: 17, marginRight: 8 }} />
|
||||
{t(`Shared with ${peopleCount} people`)}
|
||||
</Typography>
|
||||
<MenuItemGroup>
|
||||
<EnteMenuItem
|
||||
startIcon={
|
||||
<AvatarContainerOuter>
|
||||
{collection.sharees
|
||||
?.slice(0, 6)
|
||||
.map((sharee) => (
|
||||
<AvatarContainer key={sharee.email}>
|
||||
<Avatar
|
||||
key={sharee.email}
|
||||
email={sharee.email}
|
||||
/>
|
||||
</AvatarContainer>
|
||||
))}
|
||||
{extraShareeCount > 0 && (
|
||||
<AvatarContainer key="extra-count">
|
||||
<AvatarCounter>
|
||||
+{extraShareeCount}
|
||||
</AvatarCounter>
|
||||
</AvatarContainer>
|
||||
)}
|
||||
</AvatarContainerOuter>
|
||||
}
|
||||
onClick={openManageAddViewer}
|
||||
label={
|
||||
collection.sharees.length === 1
|
||||
? t(collection.sharees[0]?.email)
|
||||
: null
|
||||
}
|
||||
endIcon={<ChevronRightIcon />}
|
||||
<EnteDrawer anchor="right" open={open} onClose={handleDrawerClose}>
|
||||
<Stack spacing={'4px'} py={'12px'}>
|
||||
<Titlebar
|
||||
onClose={onClose}
|
||||
title={collection.name}
|
||||
onRootClose={handleRootClose}
|
||||
caption={`${peopleCount}${t('PARTICIPANTS')} `}
|
||||
/>
|
||||
<MenuItemDivider hasIcon={true} />
|
||||
</MenuItemGroup>
|
||||
</Stack>
|
||||
<Stack py={'20px'} px={'12px'} spacing={'24px'}>
|
||||
<Stack>
|
||||
<MenuSectionTitle
|
||||
title={t('OWNER')}
|
||||
icon={<AdminPanelSettingsIcon />}
|
||||
/>
|
||||
<MenuItemGroup>
|
||||
<EnteMenuItem
|
||||
fontWeight="normal"
|
||||
onClick={() => {}}
|
||||
label={isOwner ? t('YOU') : ownerEmail}
|
||||
startIcon={<Avatar email={ownerEmail} />}
|
||||
/>
|
||||
</MenuItemGroup>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<MenuSectionTitle
|
||||
title={t('COLLABORATORS')}
|
||||
icon={<ModeEditIcon />}
|
||||
/>
|
||||
<MenuItemGroup>
|
||||
{collaborators.map((item, index) => (
|
||||
<>
|
||||
<EnteMenuItem
|
||||
key={item}
|
||||
onClick={() =>
|
||||
openParticipantRoleView(item)
|
||||
}
|
||||
label={item}
|
||||
startIcon={<Avatar email={item} />}
|
||||
endIcon={<ChevronRightIcon />}
|
||||
/>
|
||||
{index !== collaborators.length - 1 && (
|
||||
<MenuItemDivider />
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
|
||||
<ManageParticipantsOptions
|
||||
peopleCount={peopleCount}
|
||||
open={manageAddViewer}
|
||||
onClose={closeManageAddViewer}
|
||||
<EnteMenuItem
|
||||
startIcon={<Add />}
|
||||
fontWeight={'bold'}
|
||||
onClick={openAddCollab}
|
||||
label={
|
||||
collaborators?.length
|
||||
? t('ADD_MORE')
|
||||
: t('ADD_COLLABORATORS')
|
||||
}
|
||||
/>
|
||||
</MenuItemGroup>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<MenuSectionTitle
|
||||
title={t('VIEWERS')}
|
||||
icon={<ModeEditIcon />}
|
||||
/>
|
||||
<MenuItemGroup>
|
||||
{viewers.map((item, index) => (
|
||||
<>
|
||||
<EnteMenuItem
|
||||
key={item}
|
||||
onClick={() =>
|
||||
openParticipantRoleView(item)
|
||||
}
|
||||
label={item}
|
||||
startIcon={<Avatar email={item} />}
|
||||
endIcon={<ChevronRightIcon />}
|
||||
/>
|
||||
{index !== collaborators.length - 1 && (
|
||||
<MenuItemDivider />
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
<EnteMenuItem
|
||||
startIcon={<Add />}
|
||||
fontWeight={'bold'}
|
||||
onClick={openAddViewer}
|
||||
label={
|
||||
viewers?.length
|
||||
? t('ADD_MORE')
|
||||
: t('ADD_VIEWERS')
|
||||
}
|
||||
/>
|
||||
</MenuItemGroup>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</EnteDrawer>
|
||||
<ModifyParticipant
|
||||
collectionUnshare={collectionUnshare}
|
||||
open={participantRoleView}
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
onClose={closeParticipantRoleView}
|
||||
selectedEmail={selectedEmail} // Pass the selected email to ManageParticipantsRole
|
||||
/>
|
||||
<AddParticipant
|
||||
open={addParticipantView}
|
||||
onClose={closeAddParticipant}
|
||||
onRootClose={onRootClose}
|
||||
collection={collection}
|
||||
type={participantType.current}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
import { Box } from '@mui/material';
|
||||
import React, { useContext } from 'react';
|
||||
import { Collection } from 'types/collection';
|
||||
import { OwnerParticipant } from './OwnerParticipant';
|
||||
import { ViewerParticipants } from './ViewerParticipants';
|
||||
import { CollaboratorParticipants } from './CollaboratorParticipants';
|
||||
import { unshareCollection } from 'services/collectionService';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
|
||||
interface Iprops {
|
||||
collection: Collection;
|
||||
onRootClose: () => void;
|
||||
}
|
||||
|
||||
export function ManageParticipantsList({ collection, onRootClose }: Iprops) {
|
||||
if (!collection.sharees?.length) {
|
||||
return <></>;
|
||||
}
|
||||
const appContext = useContext(AppContext);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
|
||||
const collectionUnshare = async (email: string) => {
|
||||
try {
|
||||
appContext.startLoading();
|
||||
await unshareCollection(collection, email);
|
||||
await galleryContext.syncWithRemote(false, true);
|
||||
} finally {
|
||||
appContext.finishLoading();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box mb={3}>
|
||||
<OwnerParticipant collection={collection}></OwnerParticipant>
|
||||
<CollaboratorParticipants
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
collectionUnshare={
|
||||
collectionUnshare
|
||||
}></CollaboratorParticipants>
|
||||
<ViewerParticipants
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
collectionUnshare={collectionUnshare}></ViewerParticipants>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
import { Stack } from '@mui/material';
|
||||
import { Collection } from 'types/collection';
|
||||
import { EnteDrawer } from 'components/EnteDrawer';
|
||||
import { t } from 'i18next';
|
||||
import { DialogProps } from '@mui/material';
|
||||
import Titlebar from 'components/Titlebar';
|
||||
import { ManageParticipantsList } from './ManageParticipantsList';
|
||||
|
||||
interface Iprops {
|
||||
collection: Collection;
|
||||
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onRootClose: () => void;
|
||||
|
||||
peopleCount: number;
|
||||
}
|
||||
|
||||
export default function ManageParticipantsOptions({
|
||||
open,
|
||||
collection,
|
||||
onClose,
|
||||
onRootClose,
|
||||
peopleCount,
|
||||
}: Iprops) {
|
||||
const handleDrawerClose: DialogProps['onClose'] = (_, reason) => {
|
||||
if (reason === 'backdropClick') {
|
||||
onRootClose();
|
||||
} else {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<EnteDrawer anchor="right" open={open} onClose={handleDrawerClose}>
|
||||
<Stack spacing={'4px'} py={'12px'}>
|
||||
<Titlebar
|
||||
onClose={onClose}
|
||||
title={collection.name}
|
||||
onRootClose={onRootClose}
|
||||
caption={`${peopleCount}${t('PARTICIPANTS')} `}
|
||||
/>
|
||||
<Stack py={'20px'} px={'8px'} spacing={'8px'}>
|
||||
<ManageParticipantsList
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</EnteDrawer>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -28,7 +28,7 @@ interface Iprops {
|
|||
collectionUnshare: (email: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export default function ManageParticipantsRole({
|
||||
export default function ModifyParticipant({
|
||||
collection,
|
||||
open,
|
||||
onClose,
|
|
@ -1,55 +0,0 @@
|
|||
import { Box, Typography } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { t } from 'i18next';
|
||||
import { Collection } from 'types/collection';
|
||||
import { MenuItemGroup } from 'components/Menu/MenuItemGroup';
|
||||
import { EnteMenuItem } from 'components/Menu/EnteMenuItem';
|
||||
import Avatar from 'components/pages/gallery/Avatar';
|
||||
import { User } from 'types/user';
|
||||
import { LS_KEYS, getData } from 'utils/storage/localStorage';
|
||||
|
||||
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
|
||||
interface Iprops {
|
||||
collection: Collection;
|
||||
}
|
||||
|
||||
export function OwnerParticipant({ collection }: Iprops) {
|
||||
if (!collection.sharees?.length) {
|
||||
return <></>;
|
||||
}
|
||||
const user: User = getData(LS_KEYS.USER);
|
||||
|
||||
const ownerEmail =
|
||||
user.id === collection.owner?.id ? t('YOU') : collection.owner?.email;
|
||||
|
||||
return (
|
||||
<Box mb={3}>
|
||||
<Typography color="text.muted" variant="small" padding={1}>
|
||||
<AdminPanelSettingsIcon
|
||||
style={{ fontSize: 20, marginRight: 8 }}
|
||||
/>
|
||||
{t('OWNER')}
|
||||
</Typography>
|
||||
|
||||
<MenuItemGroup>
|
||||
<>
|
||||
<EnteMenuItem
|
||||
//
|
||||
fontWeight="normal"
|
||||
onClick={() => console.log('clicked', ownerEmail)}
|
||||
label={ownerEmail}
|
||||
startIcon={
|
||||
<Avatar
|
||||
email={
|
||||
user.id === collection.owner?.id
|
||||
? user.email
|
||||
: collection.owner?.email
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
</MenuItemGroup>
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
import { Box, Stack, Typography } from '@mui/material';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { t } from 'i18next';
|
||||
import { Collection } from 'types/collection';
|
||||
import { MenuItemGroup } from 'components/Menu/MenuItemGroup';
|
||||
import { EnteMenuItem } from 'components/Menu/EnteMenuItem';
|
||||
import MenuItemDivider from 'components/Menu/MenuItemDivider';
|
||||
import Avatar from 'components/pages/gallery/Avatar';
|
||||
import AddViewer from './AddViewer';
|
||||
import PhotoIcon from '@mui/icons-material/Photo';
|
||||
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
|
||||
import ManageParticipantsRole from './ManageParticipantsRole';
|
||||
|
||||
interface Iprops {
|
||||
collection: Collection;
|
||||
onRootClose: () => void;
|
||||
collectionUnshare: (email: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export function ViewerParticipants({
|
||||
collection,
|
||||
onRootClose,
|
||||
collectionUnshare,
|
||||
}: Iprops) {
|
||||
if (!collection.sharees?.length) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const [Viewers, setViewers] = useState([]);
|
||||
const [participantRoleView, setParticipantRoleView] = useState(false);
|
||||
const [selectedEmail, setSelectedEmail] = useState('');
|
||||
|
||||
const openParticipantRoleView = (email) => {
|
||||
setParticipantRoleView(true);
|
||||
setSelectedEmail(email);
|
||||
};
|
||||
const closeParticipantRoleView = () => {
|
||||
setParticipantRoleView(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const viewersEmails =
|
||||
collection.sharees
|
||||
?.filter((sharee) => sharee.role === 'VIEWER')
|
||||
.map((sharee) => sharee.email) || [];
|
||||
setViewers(viewersEmails);
|
||||
}, [collection.sharees]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<Box mb={3}>
|
||||
<Typography color="text.muted" variant="small" padding={1}>
|
||||
<PhotoIcon style={{ fontSize: 20, marginRight: 8 }} />
|
||||
{t('VIEWERS')}
|
||||
</Typography>
|
||||
<MenuItemGroup>
|
||||
<>
|
||||
{Viewers.map((item, index) => (
|
||||
<>
|
||||
<EnteMenuItem
|
||||
fontWeight="normal"
|
||||
key={item}
|
||||
onClick={() =>
|
||||
openParticipantRoleView(item)
|
||||
}
|
||||
label={item}
|
||||
startIcon={<Avatar email={item} />}
|
||||
endIcon={<ChevronRightIcon />}
|
||||
/>
|
||||
{index !== Viewers.length - 1 && (
|
||||
<MenuItemDivider />
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
<AddViewer
|
||||
open
|
||||
onClose={() => {}}
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
/>
|
||||
</>
|
||||
</MenuItemGroup>
|
||||
</Box>
|
||||
</Stack>
|
||||
<ManageParticipantsRole
|
||||
collectionUnshare={collectionUnshare}
|
||||
open={participantRoleView}
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
onClose={closeParticipantRoleView}
|
||||
selectedEmail={selectedEmail} // Pass the selected email to ManageParticipantsRole
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Collection } from 'types/collection';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { COLLECTION_ROLE, Collection } from 'types/collection';
|
||||
|
||||
import ManageParticipants from './ManageParticipants';
|
||||
import { Stack } from '@mui/material';
|
||||
import MenuSectionTitle from 'components/Menu/MenuSectionTitle';
|
||||
import { t } from 'i18next';
|
||||
|
@ -10,8 +9,10 @@ import { MenuItemGroup } from 'components/Menu/MenuItemGroup';
|
|||
import { EnteMenuItem } from 'components/Menu/EnteMenuItem';
|
||||
import MenuItemDivider from 'components/Menu/MenuItemDivider';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import AddViewer from './AddViewer';
|
||||
import AddCollab from './AddCollab';
|
||||
import AddParticipant from './AddParticipant';
|
||||
import ManageParticipants from './ManageParticipants';
|
||||
import AvatarGroup from 'components/pages/gallery/AvatarGroup';
|
||||
import ChevronRight from '@mui/icons-material/ChevronRight';
|
||||
|
||||
export default function EmailShare({
|
||||
collection,
|
||||
|
@ -20,28 +21,55 @@ export default function EmailShare({
|
|||
collection: Collection;
|
||||
onRootClose: () => void;
|
||||
}) {
|
||||
const [addViewerView, setAddViewerView] = useState(false);
|
||||
const [addCollabView, setAddCollabView] = useState(false);
|
||||
const [addParticipantView, setAddParticipantView] = useState(false);
|
||||
const [manageParticipantView, setManageParticipantView] = useState(false);
|
||||
|
||||
const closeAddViewer = () => setAddViewerView(false);
|
||||
const openAddViewer = () => setAddViewerView(true);
|
||||
const closeAddParticipant = () => setAddParticipantView(false);
|
||||
const openAddParticipant = () => setAddParticipantView(true);
|
||||
|
||||
const closeAddCollab = () => setAddCollabView(false);
|
||||
const openAddCollab = () => setAddCollabView(true);
|
||||
const closeManageParticipant = () => setManageParticipantView(false);
|
||||
const openManageParticipant = () => setManageParticipantView(true);
|
||||
|
||||
const participantType = useRef<
|
||||
COLLECTION_ROLE.COLLABORATOR | COLLECTION_ROLE.VIEWER
|
||||
>();
|
||||
|
||||
const openAddCollab = () => {
|
||||
participantType.current = COLLECTION_ROLE.COLLABORATOR;
|
||||
openAddParticipant();
|
||||
};
|
||||
|
||||
const openAddViewer = () => {
|
||||
participantType.current = COLLECTION_ROLE.VIEWER;
|
||||
openAddParticipant();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<MenuSectionTitle
|
||||
title={t('SHARE_WITH_PEOPLE')}
|
||||
title={t('shared_with_people', {
|
||||
count: collection.sharees?.length ?? 0,
|
||||
})}
|
||||
icon={<Workspaces />}
|
||||
/>
|
||||
<MenuItemGroup>
|
||||
{collection.sharees.length > 0 ? (
|
||||
<ManageParticipants
|
||||
collection={collection}
|
||||
onRootClose={onRootClose}
|
||||
/>
|
||||
<>
|
||||
<EnteMenuItem
|
||||
startIcon={
|
||||
<AvatarGroup sharees={collection.sharees} />
|
||||
}
|
||||
onClick={openManageParticipant}
|
||||
label={
|
||||
collection.sharees.length === 1
|
||||
? t(collection.sharees[0]?.email)
|
||||
: null
|
||||
}
|
||||
endIcon={<ChevronRight />}
|
||||
/>
|
||||
<MenuItemDivider hasIcon />
|
||||
</>
|
||||
) : null}
|
||||
<EnteMenuItem
|
||||
startIcon={<AddIcon />}
|
||||
|
@ -56,15 +84,17 @@ export default function EmailShare({
|
|||
/>
|
||||
</MenuItemGroup>
|
||||
</Stack>
|
||||
<AddViewer
|
||||
open={addViewerView}
|
||||
onClose={closeAddViewer}
|
||||
<AddParticipant
|
||||
open={addParticipantView}
|
||||
onClose={closeAddParticipant}
|
||||
onRootClose={onRootClose}
|
||||
collection={collection}
|
||||
type={participantType.current}
|
||||
/>
|
||||
<AddCollab
|
||||
open={addCollabView}
|
||||
onClose={closeAddCollab}
|
||||
<ManageParticipants
|
||||
peopleCount={collection.sharees.length}
|
||||
open={manageParticipantView}
|
||||
onClose={closeManageParticipant}
|
||||
onRootClose={onRootClose}
|
||||
collection={collection}
|
||||
/>
|
||||
|
|
|
@ -44,7 +44,13 @@ function CollectionShare({ collectionSummary, ...props }: Props) {
|
|||
<Stack spacing={'4px'} py={'12px'}>
|
||||
<Titlebar
|
||||
onClose={props.onClose}
|
||||
title={t('SHARE_COLLECTION')}
|
||||
title={
|
||||
type ===
|
||||
CollectionSummaryType.incomingShareCollaborator ||
|
||||
type === CollectionSummaryType.incomingShareViewer
|
||||
? t('SHARING_DETAILS')
|
||||
: t('SHARE_COLLECTION')
|
||||
}
|
||||
onRootClose={handleRootClose}
|
||||
caption={props.collection.name}
|
||||
/>
|
||||
|
|
|
@ -58,7 +58,6 @@ const MapBox: React.FC<MapBoxProps> = ({
|
|||
} else {
|
||||
if (mapContainer && mapContainer.hasChildNodes()) {
|
||||
if (mapContainer.firstChild) {
|
||||
console.log('removing child');
|
||||
mapContainer.removeChild(mapContainer.firstChild);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,9 @@ const Avatar: React.FC<AvatarProps> = ({ file, email }) => {
|
|||
|
||||
useLayoutEffect(() => {
|
||||
try {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
if (file.ownerID !== user.id) {
|
||||
// getting email from in-memory id-email map
|
||||
const email = userIDToEmailMap.get(file.ownerID);
|
||||
|
@ -66,21 +69,21 @@ const Avatar: React.FC<AvatarProps> = ({ file, email }) => {
|
|||
useLayoutEffect(() => {
|
||||
try {
|
||||
if (!email) {
|
||||
logError(Error(), 'email not found in userIDToEmailMap');
|
||||
return;
|
||||
}
|
||||
const id = Array.from(userIDToEmailMap.keys()).find(
|
||||
(key) => userIDToEmailMap.get(key) === email
|
||||
);
|
||||
if (!id && user.email !== email) {
|
||||
logError(Error(), `ID not found for email: ${email}`);
|
||||
return;
|
||||
} else if (!id && user.email === email) {
|
||||
if (user.email === email) {
|
||||
setUserLetter(email[0].toUpperCase());
|
||||
setColorCode(PUBLIC_COLLECTED_FILES_AVATAR_COLOR_CODE);
|
||||
return;
|
||||
}
|
||||
|
||||
const id = Array.from(userIDToEmailMap.keys()).find(
|
||||
(key) => userIDToEmailMap.get(key) === email
|
||||
);
|
||||
if (!id) {
|
||||
logError(Error(), `ID not found for email: ${email}`);
|
||||
return;
|
||||
}
|
||||
const colorIndex = id % theme.colors.avatarColors.length;
|
||||
const colorCode = theme.colors.avatarColors[colorIndex];
|
||||
setUserLetter(email[0].toUpperCase());
|
||||
|
|
48
apps/photos/src/components/pages/gallery/AvatarGroup.tsx
Normal file
48
apps/photos/src/components/pages/gallery/AvatarGroup.tsx
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { styled } from '@mui/material';
|
||||
import NumberAvatar from '@mui/material/Avatar';
|
||||
import Avatar from './Avatar';
|
||||
import { Collection } from 'types/collection';
|
||||
|
||||
const AvatarContainer = styled('div')({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginLeft: -5,
|
||||
});
|
||||
|
||||
const AvatarContainerOuter = styled('div')({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginLeft: 8,
|
||||
});
|
||||
const AvatarCounter = styled(NumberAvatar)({
|
||||
height: 20,
|
||||
width: 20,
|
||||
fontSize: 10,
|
||||
color: '#fff',
|
||||
});
|
||||
|
||||
const SHAREE_AVATAR_LIMIT = 6;
|
||||
|
||||
const AvatarGroup = ({ sharees }: { sharees: Collection['sharees'] }) => {
|
||||
const hasShareesOverLimit = sharees?.length > SHAREE_AVATAR_LIMIT;
|
||||
const countOfShareesOverLimit = sharees?.length - SHAREE_AVATAR_LIMIT;
|
||||
|
||||
return (
|
||||
<AvatarContainerOuter>
|
||||
{sharees?.slice(0, 6).map((sharee) => (
|
||||
<AvatarContainer key={sharee.email}>
|
||||
<Avatar key={sharee.email} email={sharee.email} />
|
||||
</AvatarContainer>
|
||||
))}
|
||||
{hasShareesOverLimit && (
|
||||
<AvatarContainer key="extra-count">
|
||||
<AvatarCounter>+{countOfShareesOverLimit}</AvatarCounter>
|
||||
</AvatarContainer>
|
||||
)}
|
||||
</AvatarContainerOuter>
|
||||
);
|
||||
};
|
||||
|
||||
export default AvatarGroup;
|
Loading…
Reference in a new issue