Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release: #5601

Merged
merged 5 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
912 changes: 594 additions & 318 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@alkemio/client-web",
"version": "0.53.5",
"version": "0.53.6",
"description": "Alkemio client, enabling users to interact with Challenges hosted on the Alkemio platform.",
"author": "Alkemio Foundation",
"repository": {
Expand Down Expand Up @@ -34,7 +34,7 @@
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
"@mui/base": "^5.0.0-beta.23",
"@mui/icons-material": "^5.11.16",
"@mui/icons-material": "^5.15.10",
"@mui/lab": "^5.0.0-alpha.56",
"@mui/material": "5.13.x",
"@mui/styles": "^5.2.0",
Expand Down
15 changes: 0 additions & 15 deletions src/core/apollo/generated/apollo-hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11140,21 +11140,6 @@ export const CommunityUserPrivilegesWithParentCommunityDocument = gql`
}
}
}
adminUsers: usersInRole(role: ADMIN) {
id
profile {
id
displayName
avatar: visual(type: AVATAR) {
...VisualUri
}
location {
id
country
city
}
}
}
}
}
}
Expand Down
13 changes: 0 additions & 13 deletions src/core/apollo/generated/graphql-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17597,19 +17597,6 @@ export type CommunityUserPrivilegesWithParentCommunityQuery = {
};
}>
| undefined;
adminUsers?:
| Array<{
__typename?: 'User';
id: string;
profile: {
__typename?: 'Profile';
id: string;
displayName: string;
avatar?: { __typename?: 'Visual'; id: string; uri: string; name: string } | undefined;
location?: { __typename?: 'Location'; id: string; country: string; city: string } | undefined;
};
}>
| undefined;
}
| undefined;
};
Expand Down
3 changes: 2 additions & 1 deletion src/core/i18n/en/translation.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2281,7 +2281,8 @@
"whiteboardDisconnected": {
"title": "Whiteboard connection lost",
"message": "Apologies for the inconvenience! The whiteboard isn't available for editing right now, possibly due to a network issue or loss of access. Please try closing and reopening the whiteboard or refreshing the page. If the issue persists, reach out to your Space administrator for assistance.",
"lastSaved": "The content of this whiteboard was last saved {{lastSaved}}."
"lastSaved": "The content of this whiteboard was last saved {{lastSaved}}.",
"offline": "You are currently offline. The whiteboard will try to reconnect when you are back online."
},
"readonlyReason": {
"contentUpdatePolicy": "You don't have the rights to edit this whiteboard. Ask the owner, <ownerlink>{{ownerName}}</ownerlink>, to change the settings if you want to contribute.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { DialogContent } from '../../../../core/ui/dialog/deprecated';
import CollaborativeExcalidrawWrapper from '../../../common/whiteboard/excalidraw/CollaborativeExcalidrawWrapper';
import { ExportedDataState } from '@alkemio/excalidraw/types/data/types';
import DialogHeader from '../../../../core/ui/dialog/DialogHeader';
import { Box, Button, DialogActions } from '@mui/material';
import { Box } from '@mui/material';
import { gutters } from '../../../../core/ui/grid/utils';
import whiteboardSchema from '../validation/whiteboardSchema';
import FormikInputField from '../../../../core/ui/forms/FormikInputField/FormikInputField';
Expand All @@ -25,12 +25,9 @@ import {
generateWhiteboardPreviewImages,
WhiteboardPreviewImage,
} from '../WhiteboardPreviewImages/WhiteboardPreviewImages';
import { Text } from '../../../../core/ui/typography';
import { formatTimeElapsed } from '../../../shared/utils/formatTimeElapsed';
import { useWhiteboardRtLastUpdatedDateQuery } from '../../../../core/apollo/generated/apollo-hooks';
import { CollabAPI } from '../../../common/whiteboard/excalidraw/collab/Collab';
import { CollabAPI } from '../../../common/whiteboard/excalidraw/collab/useCollab';
import useWhiteboardFilesManager from '../../../common/whiteboard/excalidraw/useWhiteboardFilesManager';
import WrapperMarkdown from '../../../../core/ui/markdown/WrapperMarkdown';
import WhiteboardDialogFooter from './WhiteboardDialogFooter';
import { useLocation } from 'react-router-dom';
import { ExcalidrawElement, ExcalidrawImageElement } from '@alkemio/excalidraw/types/element/types';
Expand Down Expand Up @@ -129,9 +126,7 @@ const WhiteboardRtDialog = <Whiteboard extends WhiteboardRtWithContent>({

const [excalidrawAPI, setExcalidrawAPI] = useState<ExcalidrawImperativeAPI | null>(null);
const collabApiRef = useRef<CollabAPI>(null);
const [collaborationEnabled, setCollaborationEnabled] = useState(true);
const [collaborationStoppedNoticeOpen, setCollaborationStoppedNoticeOpen] = useState(false);
const editModeEnabled = options.canEdit && collaborationEnabled;
const editModeEnabled = options.canEdit;

const styles = useStyles();

Expand Down Expand Up @@ -226,7 +221,7 @@ const WhiteboardRtDialog = <Whiteboard extends WhiteboardRtWithContent>({
};

const onClose = async () => {
if (editModeEnabled && collaborationEnabled && whiteboard) {
if (editModeEnabled && collabApiRef.current?.isCollaborating() && whiteboard) {
const whiteboardState = await getWhiteboardState();
const { whiteboard: updatedWhiteboard, previewImages } = await prepareWhiteboardForUpdate(
whiteboard,
Expand Down Expand Up @@ -307,11 +302,9 @@ const WhiteboardRtDialog = <Whiteboard extends WhiteboardRtWithContent>({
<DialogContent classes={{ root: styles.dialogContent }}>
{!state?.loadingWhiteboardValue && whiteboard && (
<CollaborativeExcalidrawWrapper
entities={{ whiteboard, filesManager }}
entities={{ whiteboard, filesManager, lastSavedDate }}
collabApiRef={collabApiRef}
options={{
collaborationEnabled,
viewModeEnabled: !editModeEnabled,
UIOptions: {
canvasActions: {
export: {
Expand All @@ -336,12 +329,6 @@ const WhiteboardRtDialog = <Whiteboard extends WhiteboardRtWithContent>({
});
},
}}
events={{
onCollaborationEnabledChange: enabled => {
setCollaborationEnabled(enabled);
setCollaborationStoppedNoticeOpen(!enabled);
},
}}
/>
)}
{state?.loadingWhiteboardValue && <Loading text="Loading whiteboard..." />}
Expand All @@ -358,22 +345,6 @@ const WhiteboardRtDialog = <Whiteboard extends WhiteboardRtWithContent>({
)}
</Formik>
</Dialog>
<Dialog open={collaborationStoppedNoticeOpen} onClose={() => setCollaborationStoppedNoticeOpen(false)}>
<DialogHeader title={t('pages.whiteboard.whiteboardDisconnected.title')} />
<DialogContent>
<WrapperMarkdown>{t('pages.whiteboard.whiteboardDisconnected.message')}</WrapperMarkdown>
{lastSavedDate && (
<Text>
{t('pages.whiteboard.whiteboardDisconnected.lastSaved', {
lastSaved: formatTimeElapsed(lastSavedDate, t),
})}
</Text>
)}
</DialogContent>
<DialogActions>
<Button onClick={() => setCollaborationStoppedNoticeOpen(false)}>{t('buttons.ok')}</Button>
</DialogActions>
</Dialog>
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,18 @@ import React, { Ref, useCallback, useEffect, useMemo, useRef, useState } from 'r
import { useCombinedRefs } from '../../../shared/utils/useCombinedRefs';
import EmptyWhiteboard from '../EmptyWhiteboard';
import { ExcalidrawElement } from '@alkemio/excalidraw/types/element/types';
import { CollabAPI } from './collab/Collab';
import { useUserContext } from '../../../community/user';
import { WhiteboardFilesManager } from './useWhiteboardFilesManager';
import useCollab from './collab/useCollab';
import useCollab, { CollabAPI } from './collab/useCollab';
import Dialog from '@mui/material/Dialog';
import DialogHeader from '../../../../core/ui/dialog/DialogHeader';
import { DialogContent } from '../../../../core/ui/dialog/deprecated';
import WrapperMarkdown from '../../../../core/ui/markdown/WrapperMarkdown';
import { Text } from '../../../../core/ui/typography';
import { formatTimeElapsed } from '../../../shared/utils/formatTimeElapsed';
import { Button, DialogActions } from '@mui/material';
import { useTranslation } from 'react-i18next';
import { LoadingButton } from '@mui/lab';

const useActorWhiteboardStyles = makeStyles(theme => ({
container: {
Expand All @@ -30,6 +38,7 @@ const useActorWhiteboardStyles = makeStyles(theme => ({
export interface WhiteboardWhiteboardEntities {
whiteboard: { id?: string; content: string } | undefined;
filesManager: WhiteboardFilesManager;
lastSavedDate: Date | undefined;
}

export interface WhiteboardWhiteboardActions {
Expand All @@ -38,32 +47,22 @@ export interface WhiteboardWhiteboardActions {
onSavedToDatabase?: () => void;
}

export interface WhiteboardWhiteboardEvents {
onCollaborationEnabledChange?: (collaborationEnabled: boolean) => void;
}
export interface WhiteboardWhiteboardEvents {}

export interface WhiteboardWhiteboardOptions extends ExcalidrawProps {
collaborationEnabled: boolean;
}
export interface WhiteboardWhiteboardOptions extends ExcalidrawProps {}

export interface WhiteboardWhiteboardProps {
entities: WhiteboardWhiteboardEntities;
options: WhiteboardWhiteboardOptions;
actions: WhiteboardWhiteboardActions;
events: WhiteboardWhiteboardEvents;
events?: WhiteboardWhiteboardEvents;
collabApiRef?: Ref<CollabAPI | null>;
}

const WINDOW_SCROLL_HANDLER_DEBOUNCE_INTERVAL = 100;

const CollaborativeExcalidrawWrapper = ({
entities,
actions,
options,
events,
collabApiRef,
}: WhiteboardWhiteboardProps) => {
const { whiteboard, filesManager } = entities;
const CollaborativeExcalidrawWrapper = ({ entities, actions, options, collabApiRef }: WhiteboardWhiteboardProps) => {
const { whiteboard, filesManager, lastSavedDate } = entities;

const combinedCollabApiRef = useCombinedRefs<CollabAPI | null>(null, collabApiRef);

Expand Down Expand Up @@ -116,11 +115,11 @@ const CollaborativeExcalidrawWrapper = ({
[]
);

const { UIOptions: externalUIOptions, collaborationEnabled, ...restOptions } = options;
const { UIOptions: externalUIOptions, ...restOptions } = options;

const mergedUIOptions = useMemo(() => merge(UIOptions, externalUIOptions), [UIOptions, externalUIOptions]);

const [collabApi, initializeCollab] = useCollab({
const [collabApi, initializeCollab, { connecting, collaborating }] = useCollab({
username,
onSavedToDatabase: actions.onSavedToDatabase,
filesManager,
Expand All @@ -138,11 +137,10 @@ const CollaborativeExcalidrawWrapper = ({
return { success: false, errors: ['ExcalidrawAPI not yet ready'] };
},
onCloseConnection: () => {
events.onCollaborationEnabledChange?.(false);
setCollaborationStoppedNoticeOpen(true);
},
onInitialize: collabApi => {
combinedCollabApiRef.current = collabApi;
events.onCollaborationEnabledChange?.(true);
},
});

Expand All @@ -153,14 +151,26 @@ const CollaborativeExcalidrawWrapper = ({

const [excalidrawApi, setExcalidrawApi] = useState<ExcalidrawImperativeAPI | null>(null);

const [collaborationStartTime, setCollaborationStartTime] = useState<number | null>(Date.now());

const restartCollaboration = () => {
setCollaborationStartTime(Date.now());
};

useEffect(() => {
if (!connecting && collaborating) {
setCollaborationStoppedNoticeOpen(false);
}
}, [connecting, collaborating]);

useEffect(() => {
if (excalidrawApi && whiteboard?.id) {
if (excalidrawApi && whiteboard?.id && collaborationStartTime !== null) {
return initializeCollab({
excalidrawApi,
roomId: whiteboard.id,
});
}
}, [excalidrawApi, whiteboard?.id]);
}, [excalidrawApi, whiteboard?.id, collaborationStartTime]);

const handleInitializeApi = useCallback(
(excalidrawApi: ExcalidrawImperativeAPI) => {
Expand All @@ -170,29 +180,68 @@ const CollaborativeExcalidrawWrapper = ({
[actions.onInitApi]
);

const [collaborationStoppedNoticeOpen, setCollaborationStoppedNoticeOpen] = useState(false);

const { t } = useTranslation();

const [isOnline, setIsOnline] = useState(navigator.onLine);

useEffect(() => {
const handleOnlineChange = () => setIsOnline(navigator.onLine);
window.addEventListener('online', handleOnlineChange);
window.addEventListener('offline', handleOnlineChange);
setIsOnline(navigator.onLine);
return () => {
window.removeEventListener('online', handleOnlineChange);
window.removeEventListener('offline', handleOnlineChange);
};
}, []);

return (
<div className={styles.container}>
{whiteboard && (
<Excalidraw
key={whiteboard.id} // initializing a fresh Excalidraw for each whiteboard
excalidrawAPI={handleInitializeApi}
initialData={data}
UIOptions={mergedUIOptions}
isCollaborating={collaborationEnabled}
viewModeEnabled={!collaborationEnabled}
gridModeEnabled
onChange={onChange}
onPointerUpdate={collabApi?.onPointerUpdate}
detectScroll={false}
autoFocus
generateIdForFile={addNewFile}
/*renderTopRightUI={_isMobile => {
return <LiveCollaborationStatus />;
}}*/
{...restOptions}
/>
)}
</div>
<>
<div className={styles.container}>
{whiteboard && (
<Excalidraw
key={whiteboard.id} // initializing a fresh Excalidraw for each whiteboard
excalidrawAPI={handleInitializeApi}
initialData={data}
UIOptions={mergedUIOptions}
isCollaborating={collaborating}
viewModeEnabled={!collaborating}
gridModeEnabled
onChange={onChange}
onPointerUpdate={collabApi?.onPointerUpdate}
detectScroll={false}
autoFocus
generateIdForFile={addNewFile}
/*renderTopRightUI={_isMobile => {
return <LiveCollaborationStatus />;
}}*/
{...restOptions}
/>
)}
</div>
<Dialog open={collaborationStoppedNoticeOpen} onClose={() => setCollaborationStoppedNoticeOpen(false)}>
<DialogHeader title={t('pages.whiteboard.whiteboardDisconnected.title')} />
<DialogContent>
{isOnline && <WrapperMarkdown>{t('pages.whiteboard.whiteboardDisconnected.message')}</WrapperMarkdown>}
{!isOnline && <WrapperMarkdown>{t('pages.whiteboard.whiteboardDisconnected.offline')}</WrapperMarkdown>}
{lastSavedDate && (
<Text>
{t('pages.whiteboard.whiteboardDisconnected.lastSaved', {
lastSaved: formatTimeElapsed(lastSavedDate, t),
})}
</Text>
)}
</DialogContent>
<DialogActions>
<LoadingButton onClick={restartCollaboration} disabled={!isOnline} loading={connecting}>
Reconnect
</LoadingButton>
<Button onClick={() => setCollaborationStoppedNoticeOpen(false)}>{t('buttons.ok')}</Button>
</DialogActions>
</Dialog>
</>
);
};

Expand Down
Loading
Loading