Skip to content

Commit 57c22e2

Browse files
committed
add support for revoke and visibilty change
Signed-off-by: aabidsofi19 <mailtoaabid01@gmail.com>
1 parent 9e5e564 commit 57c22e2

2 files changed

Lines changed: 156 additions & 193 deletions

File tree

src/custom/ShareModal/ShareModal.tsx

Lines changed: 114 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SelectChangeEvent } from '@mui/material';
1+
import { CircularProgress, SelectChangeEvent } from '@mui/material';
22
import React, { useEffect, useState } from 'react';
33
import {
44
Avatar,
@@ -50,10 +50,80 @@ interface User {
5050
interface AccessListProps {
5151
accessList: User[];
5252
ownerData: User;
53-
handleDelete: (email: string) => void;
53+
handleDelete: (email: string) => Promise<{ error: string }>;
5454
hostURL?: string | null;
5555
}
5656

57+
interface AccessListActorProps {
58+
actor: User;
59+
isOwner: boolean;
60+
hostURL?: string | null;
61+
handleDelete: (email: string) => Promise<{ error: string }>;
62+
}
63+
64+
const AccessListActor: React.FC<AccessListActorProps> = ({ actor: actorData, hostURL, isOwner, handleDelete }) => {
65+
const theme = useTheme()
66+
const [isRevoking, setIsRevoking] = useState(false)
67+
const revokeAcess = async () => {
68+
setIsRevoking(true)
69+
try {
70+
const res = await handleDelete(actorData.email)
71+
}
72+
finally {
73+
setIsRevoking(false)
74+
}
75+
}
76+
77+
const openInNewTab = (url: string) => {
78+
window.open(url, '_blank', 'noreferrer');
79+
};
80+
81+
return (
82+
<ListItem key={actorData.id} style={{ paddingLeft: '0' }}>
83+
<ListItemAvatar>
84+
<Avatar
85+
alt={actorData.first_name}
86+
src={actorData.avatar_url}
87+
imgProps={{ referrerPolicy: 'no-referrer' }}
88+
onClick={() => {
89+
hostURL && openInNewTab(`${hostURL}/user/${actorData.id}`);
90+
}}
91+
/>
92+
</ListItemAvatar>
93+
<ListItemText
94+
primary={`${actorData.first_name || ''} ${actorData.last_name || ''}`}
95+
secondary={actorData.email}
96+
secondaryTypographyProps={{
97+
sx: {
98+
color: theme.palette.background.neutral?.pressed
99+
}
100+
}}
101+
/>
102+
<ListItemSecondaryAction>
103+
{isOwner && (
104+
<div>Owner</div>
105+
)}
106+
107+
{!isOwner && !isRevoking && (
108+
<CustomTooltip title="Remove Access" placement="top" arrow>
109+
<IconButton
110+
edge="end"
111+
aria-label="delete"
112+
onClick={revokeAcess}
113+
>
114+
<DeleteIcon fill={theme.palette.background.neutral?.default} />
115+
</IconButton>
116+
</CustomTooltip>
117+
)}
118+
119+
{isRevoking && <CircularProgress size={24} sx={{ color: theme.palette.icon.default }} />}
120+
121+
</ListItemSecondaryAction>
122+
</ListItem>)
123+
}
124+
125+
126+
57127
/**
58128
* Custom component to show users list with delete icon and owner tag
59129
*/
@@ -63,11 +133,7 @@ const AccessList: React.FC<AccessListProps> = ({
63133
handleDelete,
64134
hostURL
65135
}: AccessListProps) => {
66-
const openInNewTab = (url: string) => {
67-
window.open(url, '_blank', 'noreferrer');
68-
};
69136

70-
const theme = useTheme();
71137

72138
return (
73139
<>
@@ -78,44 +144,8 @@ const AccessList: React.FC<AccessListProps> = ({
78144
)}
79145
<ListWrapper>
80146
<List dense>
81-
{accessList.map((actorData) => (
82-
<ListItem key={actorData.id} style={{ paddingLeft: '0' }}>
83-
<ListItemAvatar>
84-
<Avatar
85-
alt={actorData.first_name}
86-
src={actorData.avatar_url}
87-
imgProps={{ referrerPolicy: 'no-referrer' }}
88-
onClick={() => {
89-
hostURL && openInNewTab(`${hostURL}/user/${actorData.id}`);
90-
}}
91-
/>
92-
</ListItemAvatar>
93-
<ListItemText
94-
primary={`${actorData.first_name || ''} ${actorData.last_name || ''}`}
95-
secondary={actorData.email}
96-
secondaryTypographyProps={{
97-
sx: {
98-
color: theme.palette.background.neutral?.pressed
99-
}
100-
}}
101-
/>
102-
<ListItemSecondaryAction>
103-
{ownerData.id === actorData.id ? (
104-
<div>Owner</div>
105-
) : (
106-
<CustomTooltip title="Remove Access" placement="top" arrow>
107-
<IconButton
108-
edge="end"
109-
aria-label="delete"
110-
onClick={() => handleDelete(actorData.email)}
111-
>
112-
<DeleteIcon fill={theme.palette.background.neutral?.default} />
113-
</IconButton>
114-
</CustomTooltip>
115-
)}
116-
</ListItemSecondaryAction>
117-
</ListItem>
118-
))}
147+
{accessList.map((actorData) => (<AccessListActor hostURL={hostURL} key={actorData.id}
148+
actor={actorData} handleDelete={handleDelete} isOwner={ownerData.id == actorData.id} />))}
119149
</List>
120150
</ListWrapper>
121151
</>
@@ -139,8 +169,6 @@ interface ShareModalProps {
139169
ownerData: User;
140170
/** Function to fetch the list of users who have access to the resource */
141171
fetchAccessActors: () => Promise<User[]>;
142-
/** Function to handle the sharing of the resource with specified users and options */
143-
handleShare: (shareUserData: User[], selectedOption: string | undefined) => void;
144172
/** Optional URL of the host application. Defaults to `null` if not provided */
145173
hostURL?: string | null;
146174
/**
@@ -157,11 +185,12 @@ interface ShareModalProps {
157185
*/
158186
fetchSuggestions: (value: string) => Promise<User[]>;
159187
handleCopy: () => void;
160-
handleUpdateVisibility: (value: string) => Promise<{error:string}> ,
161-
isUpdatingVisibility : boolean ,
162-
handleShareWithNewUsers: (newUsers: User[]) => Promise<{error:string}>,
163-
// isSharing : boolean
164-
canShareWithNewUsers: boolean
188+
handleUpdateVisibility: (value: string) => Promise<{ error: string }>,
189+
isUpdatingVisibility: boolean,
190+
handleShareWithNewUsers: (newUsers: User[]) => Promise<{ error: string }>,
191+
canShareWithNewUsers: boolean,
192+
handleRevokeAccess: (revokedUsser: User[]) => Promise<{ error: string }>
193+
canRevokeAccess: boolean,
165194
}
166195

167196
/**
@@ -174,54 +203,45 @@ const ShareModal: React.FC<ShareModalProps> = ({
174203
dataName,
175204
ownerData,
176205
fetchAccessActors,
177-
handleShare,
178206
hostURL = null,
179207
handleCopy,
180208
handleUpdateVisibility,
181209
isUpdatingVisibility,
182210
canShareWithNewUsers,
211+
handleRevokeAccess,
183212
handleShareWithNewUsers,
184213
isVisibilitySelectorDisabled = false,
185214
fetchSuggestions
186215
}: ShareModalProps): JSX.Element => {
187-
console.log("new share modal new")
188-
const theme = useTheme();
216+
const theme = useTheme();
189217
const [openMenu, setMenu] = useState<boolean>(false);
190-
const [selectedOption, setOption] = useState<string | undefined>(selectedResource?.visibility);
191218
const [shareUserData, setShareUserData] = useState<User[]>([]);
219+
const [resourceVisibility,setVisibility] = useState(selectedResource.visibility)
192220

193-
const handleDelete = (email: string) => {
194-
setShareUserData((prevData) => prevData.filter((user) => user.email !== email));
221+
const handleDelete = async (email: string) => {
222+
const revoked = shareUserData.find(user => user.email == email)
223+
if (!revoked) {
224+
console.error("cant revoke user without acesss")
225+
return {error:""}
226+
}
227+
return handleRevokeAccess([revoked])
195228
};
196229

197-
const handleOptionClick = (event: SelectChangeEvent<unknown>) => {
230+
const handleOptionClick = async (event: SelectChangeEvent<unknown>) => {
198231
const value = event.target.value as string;
199-
setOption(value);
200-
handleUpdateVisibility(value)
201-
};
202-
203-
const handleMenuClose = () => setMenu(false);
204-
205-
const isShareDisabled = () => {
206-
// Ensure at least one user other than the owner is selected
207-
const otherUsersSelected = shareUserData.some((user) => user.id !== ownerData.id);
208-
209-
const existingAccessIds = shareUserData.map((user) => user.id);
210-
const ownerDataId = ownerData?.id;
232+
if (value == resourceVisibility){
233+
console.error("visibility is already ",value)
234+
return
235+
}
211236

212-
if (ownerDataId) {
213-
existingAccessIds.push(ownerDataId);
237+
const res = await handleUpdateVisibility(value)
238+
if (!res?.error){
239+
setVisibility(value)
214240
}
241+
};
215242

216-
const hasMismatchedUsers = !shareUserData.every((user) => existingAccessIds.includes(user.id));
243+
const handleMenuClose = () => setMenu(false);
217244

218-
return (
219-
!otherUsersSelected || // Disable if no other users are selected
220-
(shareUserData.length === existingAccessIds.length &&
221-
!hasMismatchedUsers &&
222-
selectedOption === selectedResource?.visibility)
223-
);
224-
};
225245

226246
useEffect(() => {
227247
const fetchActors = async () => {
@@ -231,11 +251,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
231251
fetchActors();
232252
}, [fetchAccessActors]);
233253

234-
useEffect(() => {
235-
if (selectedResource) {
236-
setOption(selectedResource?.visibility);
237-
}
238-
}, [selectedResource]);
254+
239255

240256
return (
241257
<div style={{ marginBottom: '1rem' }}>
@@ -270,7 +286,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
270286
hostURL={hostURL}
271287
/>
272288

273-
{selectedResource?.visibility !== 'published' && (
289+
{resourceVisibility !== 'published' && (
274290
<>
275291
<CustomListItemText>
276292
<Typography variant="h6">General Access</Typography>
@@ -279,14 +295,20 @@ const ShareModal: React.FC<ShareModalProps> = ({
279295
<FormControlWrapper size="small">
280296
<div style={{ display: 'flex', justifyContent: 'start', alignItems: 'center' }}>
281297
<VisibilityIconWrapper>
282-
{selectedOption === SHARE_MODE.PUBLIC ? (
298+
299+
{isUpdatingVisibility && <CircularProgress size={24} />}
300+
301+
302+
{!isUpdatingVisibility && resourceVisibility === SHARE_MODE.PUBLIC && (
283303
<PublicIcon
284304
width={24}
285305
height={24}
286306
fill={theme.palette.icon.default}
287307
stroke={theme.palette.mode === 'dark' ? WHITE : BLACK}
288308
/>
289-
) : (
309+
)}
310+
311+
{!isUpdatingVisibility && resourceVisibility == SHARE_MODE.PRIVATE && (
290312
<LockIcon
291313
width={24}
292314
height={24}
@@ -298,19 +320,19 @@ const ShareModal: React.FC<ShareModalProps> = ({
298320
<div style={{ display: 'flex', flexDirection: 'column' }}>
299321
<CustomSelect
300322
variant="outlined"
301-
defaultValue={selectedOption}
323+
value={resourceVisibility}
302324
labelId="share-menu-select"
303325
id="share-menu"
304326
open={openMenu}
305327
onClose={handleMenuClose}
306328
onOpen={() => setMenu(true)}
307329
onChange={handleOptionClick}
308-
disabled={isVisibilitySelectorDisabled}
330+
disabled={isVisibilitySelectorDisabled || isUpdatingVisibility}
309331
>
310332
{Object.values(SHARE_MODE).map((option) => (
311333
<MenuItem
312334
key={option}
313-
selected={option === selectedOption}
335+
selected={option === resourceVisibility}
314336
value={option}
315337
>
316338
{option.charAt(0).toUpperCase() + option.slice(1)}
@@ -324,7 +346,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
324346
component="span"
325347
variant="body2"
326348
>
327-
{selectedOption === SHARE_MODE.PRIVATE ? options.PRIVATE : options.PUBLIC}
349+
{resourceVisibility === SHARE_MODE.PRIVATE ? options.PRIVATE : options.PUBLIC}
328350
</Typography>
329351
</div>
330352
</div>

0 commit comments

Comments
 (0)