Skip to content

Commit dc4395d

Browse files
authored
Merge branch 'master' into mui-migration-1
2 parents e8d2198 + fd815d6 commit dc4395d

10 files changed

Lines changed: 191 additions & 38 deletions

File tree

src/custom/Modal/index.tsx

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import React, { useRef, useState } from 'react';
33
import { Box, Dialog, IconButton, Paper, Typography } from '../../base';
44
import { ContainedButton, OutlinedButton, TextButton } from '../../base/Button/Button';
55
import { iconLarge, iconMedium } from '../../constants/iconsSizes';
6-
import { CloseIcon, InfoCircleIcon } from '../../icons';
6+
import { CloseIcon, FullScreenIcon, InfoCircleIcon } from '../../icons';
7+
import FullScreenExitIcon from '../../icons/Fullscreen/FullScreenExitIcon';
78
import { darkModalGradient, lightModalGradient } from '../../theme/colors/colors';
89
import { CustomTooltip } from '../CustomTooltip';
910

@@ -12,6 +13,7 @@ interface ModalProps extends DialogProps {
1213
title: string;
1314
headerIcon?: React.ReactNode;
1415
reactNode?: React.ReactNode;
16+
isFullScreenModeAllowed?: boolean;
1517
}
1618

1719
interface ModalFooterProps {
@@ -56,6 +58,20 @@ const StyledDialog = styled(Dialog)`
5658
}
5759
`;
5860

61+
const FullscreenButton = styled(FullScreenIcon)(({ theme }) => ({
62+
height: '2.25rem',
63+
width: '2.25rem',
64+
fill: theme.palette.common.white,
65+
cursor: 'pointer'
66+
}));
67+
68+
const FullscreenExitButton = styled(FullScreenExitIcon)(({ theme }) => ({
69+
height: '2.25rem',
70+
width: '2.25rem',
71+
fill: theme.palette.common.white,
72+
cursor: 'pointer'
73+
}));
74+
5975
export const ModalStyledHeader = styled('div')(({ theme }) => ({
6076
background: theme.palette.mode === 'light' ? lightModalGradient.header : darkModalGradient.header,
6177
color: '#eee',
@@ -103,7 +119,8 @@ export const useModal = ({ headerIcon }: { headerIcon: React.ReactNode }): UseMo
103119
export const ModalBody = styled(Paper)(({ theme }) => ({
104120
padding: '1rem',
105121
backgroundColor: theme.palette.background.surfaces,
106-
overflowY: 'auto'
122+
overflowY: 'auto',
123+
height: '100%'
107124
}));
108125

109126
const StyledFooter = styled('div', {
@@ -135,16 +152,22 @@ export const Modal: React.FC<ModalProps> = ({
135152
reactNode,
136153
children,
137154
maxWidth = 'xs',
155+
isFullScreenModeAllowed,
138156
...props
139157
}) => {
158+
const [fullScreen, setFullScreen] = useState(false);
159+
const toggleFullScreen = () => {
160+
setFullScreen((prev) => !prev);
161+
};
140162
return (
141163
<StyledDialog
142-
fullWidth={true}
143164
maxWidth={maxWidth}
144165
open={open}
145166
onClose={closeModal}
146167
aria-labelledby="alert-dialog-slide-title"
147168
aria-describedby="alert-dialog-slide-description"
169+
fullScreen={fullScreen}
170+
fullWidth={!fullScreen}
148171
{...props}
149172
>
150173
{title && (
@@ -153,9 +176,20 @@ export const Modal: React.FC<ModalProps> = ({
153176
<Typography component={'div'} variant="h6">
154177
{title}
155178
</Typography>
156-
<CloseBtn onClick={closeModal}>
157-
<CloseIcon {...iconLarge} fill="#fff"></CloseIcon>
158-
</CloseBtn>
179+
<div style={{ display: 'flex', alignItems: 'center', gap: '1rem' }}>
180+
{isFullScreenModeAllowed && (
181+
<CustomTooltip title={fullScreen ? 'Exit Fullscreen' : 'Enter Fullscreen'}>
182+
{fullScreen ? (
183+
<FullscreenExitButton onClick={toggleFullScreen} />
184+
) : (
185+
<FullscreenButton onClick={toggleFullScreen} />
186+
)}
187+
</CustomTooltip>
188+
)}
189+
<CloseBtn onClick={closeModal}>
190+
<CloseIcon {...iconLarge} fill="#fff"></CloseIcon>
191+
</CloseBtn>
192+
</div>
159193
</ModalStyledHeader>
160194
)}
161195

src/custom/ShareModal/ShareModal.tsx

Lines changed: 67 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,11 @@ interface SelectedResource {
151151
[key: string]: unknown;
152152
}
153153

154+
type SelectedResources = SelectedResource | SelectedResource[];
155+
154156
export type ResourceAccessArg = {
155157
resourceType: string;
156-
resourceId: string;
158+
resourceId: string | string[];
157159
resourceAccessMappingPayload: {
158160
grant_access: string[];
159161
revoke_access: string[];
@@ -167,8 +169,8 @@ import { VISIBILITY } from '../../constants/constants';
167169
interface ShareModalProps {
168170
/** Function to close the share modal */
169171
handleShareModalClose: () => void;
170-
/** The resource that is selected for sharing.*/
171-
selectedResource: SelectedResource;
172+
/** The resource(s) that is selected for sharing.*/
173+
selectedResource: SelectedResources;
172174
/** The name of the data being shared, like design or filter */
173175
dataName: string;
174176
/** Data of the user who owns the resource */
@@ -212,26 +214,29 @@ const ShareModal: React.FC<ShareModalProps> = ({
212214
useGetAllUsersQuery,
213215
shareableLink
214216
}: ShareModalProps): JSX.Element => {
217+
console.log('amit selectdResource', selectedResource);
215218
const theme = useTheme();
216219
const [openMenu, setMenu] = useState<boolean>(false);
217220
const [shareUserData, setShareUserData] = useState<User[]>([]);
218-
const [resourceVisibility, setVisibility] = useState(selectedResource.visibility);
221+
const [resourceVisibility, setVisibility] = useState(
222+
Array.isArray(selectedResource) ? selectedResource[0].visibility : selectedResource.visibility
223+
);
224+
console.log('amit resourceVisibility', resourceVisibility);
219225
const [isUpdatingVisibility, setUpdatingVisibility] = useState(false);
220226

221-
const userCanUpdateVisibility = canUpdateResourceVisibility(
222-
selectedResource,
223-
currentUser,
224-
ownerData
225-
);
226-
const userCanShareWithNewUsers = canShareResourceWithNewUsers(
227-
selectedResource,
228-
currentUser,
229-
ownerData
230-
);
227+
const userCanUpdateVisibility = Array.isArray(selectedResource)
228+
? selectedResource.every((resource) =>
229+
canUpdateResourceVisibility(resource, currentUser, ownerData)
230+
)
231+
: canUpdateResourceVisibility(selectedResource, currentUser, ownerData);
232+
233+
const userCanShareWithNewUsers = Array.isArray(selectedResource)
234+
? selectedResource.every((resource) =>
235+
canShareResourceWithNewUsers(resource, currentUser, ownerData)
236+
)
237+
: canShareResourceWithNewUsers(selectedResource, currentUser, ownerData);
231238

232239
const handleCopy = () => {
233-
// const shareableLink = getShareableResourceRoute(dataName,selectedResource.id,selectedResource.name)
234-
console.log('shareableLink', shareableLink);
235240
navigator.clipboard.writeText(shareableLink);
236241
notify({
237242
message: 'Link copied to clipboard',
@@ -242,16 +247,49 @@ const ShareModal: React.FC<ShareModalProps> = ({
242247
const resourceType = dataName === 'design' ? 'pattern' : dataName;
243248

244249
const handleShareWithNewUsers = async (newUsers: User[]) => {
245-
console.log('new users', newUsers);
246250
const grantAccessList = newUsers.map((user) => ({
247251
actor_id: user.user_id,
248252
actor_type: 'user'
249253
}));
250254
const emails = newUsers.map((u) => u.email);
251255

256+
if (Array.isArray(selectedResource)) {
257+
const responses = await Promise.all(
258+
selectedResource.map((resource) =>
259+
resourceAccessMutator({
260+
resourceType,
261+
resourceId: resource.id,
262+
resourceAccessMappingPayload: {
263+
grant_access: [...grantAccessList],
264+
revoke_access: [],
265+
notify_users: true
266+
}
267+
})
268+
)
269+
);
270+
271+
const hasError = responses.some((response) => response?.error);
272+
273+
if (!hasError) {
274+
notify({
275+
message: `${dataName}s shared with ${emails.join(', ')}`,
276+
event_type: 'success'
277+
});
278+
} else {
279+
notify({
280+
message: `An error occurred. Some ${dataName}s may not have been shared`,
281+
event_type: 'error'
282+
});
283+
}
284+
285+
return {
286+
error: hasError ? 'Some resources failed to share' : ''
287+
};
288+
}
289+
252290
const response = await resourceAccessMutator({
253291
resourceType,
254-
resourceId: selectedResource?.id,
292+
resourceId: !Array.isArray(selectedResource) ? selectedResource.id : selectedResource[0].id,
255293
resourceAccessMappingPayload: {
256294
grant_access: [...grantAccessList],
257295
revoke_access: [],
@@ -260,9 +298,8 @@ const ShareModal: React.FC<ShareModalProps> = ({
260298
});
261299

262300
if (!response?.error) {
263-
const msg = `${dataName} shared with ${emails.join(', ')} `;
264301
notify({
265-
message: msg,
302+
message: `${dataName} shared with ${emails.join(', ')}`,
266303
event_type: 'success'
267304
});
268305
}
@@ -288,7 +325,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
288325

289326
const response = await resourceAccessMutator({
290327
resourceType,
291-
resourceId: selectedResource?.id,
328+
resourceId: !Array.isArray(selectedResource) ? selectedResource.id : selectedResource[0].id,
292329
resourceAccessMappingPayload: {
293330
grant_access: [],
294331
revoke_access: [...revokeAccessList],
@@ -327,7 +364,9 @@ const ShareModal: React.FC<ShareModalProps> = ({
327364

328365
/* eslint-disable @typescript-eslint/no-explicit-any */
329366
const notifyVisibilityChange = (res: any, value: any) => {
330-
const UPDATE_VISIBILITY_MSG = `${startCase(dataName)} '${selectedResource.name}' is now ${value}`;
367+
const UPDATE_VISIBILITY_MSG = Array.isArray(selectedResource)
368+
? `${startCase(dataName)}s (${selectedResource.length}) are now ${value}`
369+
: `${startCase(dataName)} '${selectedResource.name}' is now ${value}`;
331370
const FAILED_TO_UPDATE_VISIBILITY_MSG = `Failed to update visibility. ${res?.error?.error || ''}`;
332371

333372
if (!res.error) {
@@ -378,7 +417,11 @@ const ShareModal: React.FC<ShareModalProps> = ({
378417
<Modal
379418
open={true}
380419
closeModal={handleShareModalClose}
381-
title={`Share ${dataName} "${selectedResource?.name}"`}
420+
title={
421+
Array.isArray(selectedResource)
422+
? `Share ${selectedResource.length} ${dataName}s`
423+
: `Share ${dataName} "${selectedResource?.name}"`
424+
}
382425
>
383426
<ModalBody>
384427
<UserShareSearch
@@ -395,7 +438,7 @@ const ShareModal: React.FC<ShareModalProps> = ({
395438
hostURL={hostURL}
396439
/>
397440

398-
{resourceVisibility !== 'published' && (
441+
{!Array.isArray(selectedResource) && resourceVisibility !== 'published' && (
399442
<>
400443
<CustomListItemText>
401444
<Typography variant="h6">General Access</Typography>

src/custom/UniversalFilter.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface UniversalFilterProps {
2929
}
3030

3131
export const FilterHeader = styled('div')(({ theme }) => ({
32-
background: theme.palette.mode === 'light' ? lightModalGradient.fotter : darkModalGradient.fotter,
32+
background: theme.palette.mode === 'light' ? lightModalGradient.header : darkModalGradient.header,
3333
padding: '0.75rem 1rem',
3434
margin: '-1rem -1rem 1rem -1rem',
3535
display: 'flex',
@@ -136,7 +136,11 @@ function UniversalFilter({
136136
anchorEl={anchorEl}
137137
placement="bottom-end"
138138
>
139-
<ClickAwayListener onClickAway={handleClose}>
139+
<ClickAwayListener
140+
onClickAway={handleClose}
141+
mouseEvent="onMouseDown"
142+
touchEvent="onTouchStart"
143+
>
140144
<Paper
141145
sx={{
142146
padding: '1rem',

src/hooks/useRoomActivity.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useRef, useState } from 'react';
1+
import { MutableRefObject, useEffect, useRef, useState } from 'react';
22
import {
33
ICE_SERVERS,
44
ICEServer,
@@ -152,6 +152,7 @@ const subscribeToRoomActivity = async (
152152
});
153153

154154
ws.addEventListener('message', (event: MessageEvent) => {
155+
console.log('[RoomActivity] new message', event);
155156
const data = JSON.parse(event.data) as UserMapChangeMessage;
156157
if (data.type === USER_MAP_CHANGE_MSG && data.user_map) {
157158
onUserMapChange(data.user_map);
@@ -174,7 +175,7 @@ export const useRoomActivity = ({
174175
provider_url,
175176
getUserProfile,
176177
getUserAccessToken
177-
}: UseRoomActivityParams): UserMapping => {
178+
}: UseRoomActivityParams): [UserMapping, MutableRefObject<WebSocket | null>] => {
178179
const [allRoomsUserMapping, setAllRoomsUserMapping] = useState<UserMapping>({});
179180
const wsRef = useRef<WebSocket | null>(null);
180181

@@ -191,10 +192,29 @@ export const useRoomActivity = ({
191192

192193
return () => {
193194
if (ws) {
195+
console.log('closing websocket', ws);
194196
ws.close();
195197
}
196198
};
197199
}, [provider_url, getUserProfile, getUserAccessToken]);
198200

199-
return allRoomsUserMapping;
201+
return [allRoomsUserMapping, wsRef];
202+
};
203+
204+
export const subscribeToRoom = (ws: WebSocket, room: string) => {
205+
ws.send(
206+
JSON.stringify({
207+
type: 'subscribe',
208+
topic: room
209+
})
210+
);
211+
};
212+
213+
export const unSubscribeRoom = (ws: WebSocket, room: string) => {
214+
ws.send(
215+
JSON.stringify({
216+
type: 'unsubscribe',
217+
topic: room
218+
})
219+
);
200220
};

src/icons/Export/ExportIcon.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { FC } from 'react';
2+
import { DEFAULT_HEIGHT, DEFAULT_WIDTH } from '../../constants/constants';
3+
import { IconProps } from '../types';
4+
5+
export const DownloadIcon: FC<IconProps> = ({
6+
width = DEFAULT_WIDTH,
7+
height = DEFAULT_HEIGHT,
8+
fill = '#455a64',
9+
style = {}
10+
}) => (
11+
<svg
12+
xmlns="http://www.w3.org/2000/svg"
13+
height={height}
14+
viewBox="0 0 24 24"
15+
width={width}
16+
fill={fill}
17+
style={style}
18+
>
19+
<path d="M19 9h-4V3H9v6H5l7 7zM5 18v2h14v-2z"></path>
20+
</svg>
21+
);
22+
23+
export default DownloadIcon;

src/icons/Export/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as ExportIcon } from './ExportIcon';

src/icons/Open/OpenFileIcon.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { FC } from 'react';
2+
import { DEFAULT_WIDTH } from '../../constants/constants';
3+
import { IconProps } from '../types';
4+
5+
export const OpenFileIcon: FC<IconProps> = ({
6+
width = DEFAULT_WIDTH,
7+
height = DEFAULT_WIDTH,
8+
fill = 'currentColor',
9+
...props
10+
}) => (
11+
<svg
12+
xmlns="http://www.w3.org/2000/svg"
13+
width={width}
14+
height={height}
15+
viewBox="0 0 256 256"
16+
fill={fill}
17+
{...props}
18+
>
19+
<path
20+
d="M245,110.64A16,16,0,0,0,232,104H216V88a16,16,0,0,0-16-16H130.67L102.94,51.2a16.14,16.14,0,0,0-9.6-3.2H40A16,16,0,0,0,24,64V208h0a8,8,0,0,0,8,8H211.1a8,8,0,0,0,7.59-5.47l28.49-85.47A16.05,16.05,0,0,0,245,110.64ZM93.34,64,123.2,86.4A8,8,0,0,0,128,88h72v16H69.77a16,16,0,0,0-15.18,10.94L40,158.7V64Z"
21+
fill={fill}
22+
></path>
23+
</svg>
24+
);
25+
26+
export default OpenFileIcon;

0 commit comments

Comments
 (0)