Skip to content

Commit

Permalink
[3533] Add a dedicated component to handle diagram subscription
Browse files Browse the repository at this point in the history
Bug: #3533
Signed-off-by: Michaël Charfadi <[email protected]>
  • Loading branch information
mcharfadi committed Oct 14, 2024
1 parent 2040b3c commit 18964c6
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 104 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ This will allow specifier to create images that fir perfectly in the project tem
- https://github.com/eclipse-sirius/sirius-web/issues/3826[#3826] [form] Make EMF default form support attributes with a date
- https://github.com/eclipse-sirius/sirius-web/issues/2163[#2163] [form] Make EMF default form support non changeable features
- https://github.com/eclipse-sirius/sirius-web/issues/4086[#4086] [form] Wrap widget returned by property section in a div with a specific classname

- https://github.com/eclipse-sirius/sirius-web/issues/3533[#3553] [diagram] Add a dedicated component to handle diagram subscription

== v2024.9.0

Expand Down Expand Up @@ -402,6 +402,7 @@ We cannot rely on the error message recaived from the backend since Cross Origin
- https://github.com/eclipse-sirius/sirius-web/issues/3701[#3701] [domain] Add edge and node tools to domain diagram including creation tools as well as delete and direct edit tools.
- https://github.com/eclipse-sirius/sirius-web/issues/3859[#3859] [view] Allow name duplication in ecore by grouping builders in dedicated packages


== v2024.5.0

=== Shapes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,12 @@
* Obeo - initial API and implementation
*******************************************************************************/

import { gql, OnDataOptions, useQuery, useSubscription } from '@apollo/client';
import { gql, useQuery } from '@apollo/client';
import { RepresentationComponentProps, useMultiToast } from '@eclipse-sirius/sirius-components-core';
import { ReactFlowProvider } from '@xyflow/react';
import { memo, useEffect, useState } from 'react';
import { DiagramContext } from '../contexts/DiagramContext';
import { DiagramDescriptionContext } from '../contexts/DiagramDescriptionContext';
import { DialogContextProvider } from '../dialog/DialogContext';
import { diagramEventSubscription } from '../graphql/subscription/diagramEventSubscription';
import {
GQLDiagramEventPayload,
GQLDiagramRefreshedEventPayload,
} from '../graphql/subscription/diagramEventSubscription.types';
import { ConnectorContextProvider } from '../renderer/connector/ConnectorContext';
import { DiagramRenderer } from '../renderer/DiagramRenderer';
import { DiagramDirectEditContextProvider } from '../renderer/direct-edit/DiagramDirectEditContext';
import { DropNodeContextProvider } from '../renderer/dropNode/DropNodeContext';
import { MarkerDefinitions } from '../renderer/edge/MarkerDefinitions';
Expand All @@ -37,12 +29,8 @@ import {
GQLDiagramDescription,
GQLDiagramDescriptionData,
GQLDiagramDescriptionVariables,
GQLDiagramEventData,
GQLDiagramEventVariables,
} from './DiagramRepresentation.types';
import { StoreContextProvider } from './StoreContext';

const subscription = gql(diagramEventSubscription);
import { DiagramSubscriptionProvider } from './DiagramSubscriptionProvider';

export const getDiagramDescription = gql`
query getDiagramDescription($editingContextId: ID!, $representationId: ID!) {
Expand Down Expand Up @@ -75,38 +63,14 @@ export const getDiagramDescription = gql`
}
`;

const isDiagramRefreshedEventPayload = (payload: GQLDiagramEventPayload): payload is GQLDiagramRefreshedEventPayload =>
payload.__typename === 'DiagramRefreshedEventPayload';

export const DiagramRepresentation = memo(
({ editingContextId, representationId, readOnly }: RepresentationComponentProps) => {
const [state, setState] = useState<DiagramRepresentationState>({
id: crypto.randomUUID(),
diagramRefreshedEventPayload: null,
payload: null,
complete: false,
message: null,
});
const { addErrorMessage } = useMultiToast();

const variables: GQLDiagramEventVariables = {
input: {
id: state.id,
editingContextId,
diagramId: representationId,
},
};

const onData = ({ data }: OnDataOptions<GQLDiagramEventData>) => {
if (data.data) {
const { diagramEvent } = data.data;
if (isDiagramRefreshedEventPayload(diagramEvent)) {
setState((prevState) => ({ ...prevState, diagramRefreshedEventPayload: diagramEvent }));
}
setState((prevState) => ({ ...prevState, payload: diagramEvent }));
}
};

const {
loading: diagramDescriptionLoading,
data: diagramDescriptionData,
Expand All @@ -116,7 +80,6 @@ export const DiagramRepresentation = memo(
editingContextId,
representationId,
},
skip: state.diagramRefreshedEventPayload === null,
});

useEffect(() => {
Expand All @@ -132,74 +95,40 @@ export const DiagramRepresentation = memo(
}
}, [diagramDescriptionLoading, diagramDescriptionData, diagramDescriptionError]);

const onComplete = () => {
setState((prevState) => ({ ...prevState, diagramRefreshedEventPayload: null, complete: true }));
};

const { error } = useSubscription<GQLDiagramEventData>(subscription, {
variables,
fetchPolicy: 'no-cache',
onData,
onComplete,
});

const diagramDescription: GQLDiagramDescription | undefined =
diagramDescriptionData?.viewer.editingContext.representation.description;

if (state.message) {
return <div>{state.message}</div>;
}
if (error) {
return <div>{error.message}</div>;
}
if (state.complete) {
return <div>The representation is not available anymore</div>;
}
if (!state.diagramRefreshedEventPayload || !diagramDescription) {

if (!diagramDescription) {
return <div></div>;
}

return (
<ReactFlowProvider>
<DiagramContext.Provider
value={{
editingContextId,
diagramId: representationId,
refreshEventPayloadId: state.diagramRefreshedEventPayload.id,
payload: state.payload,
readOnly,
}}>
<DiagramDescriptionContext.Provider value={{ diagramDescription }}>
<StoreContextProvider>
<DiagramDirectEditContextProvider>
<DiagramPaletteContextProvider>
<DiagramElementPaletteContextProvider>
<ConnectorContextProvider>
<DropNodeContextProvider>
<NodeContextProvider>
<div
style={{ display: 'inline-block', position: 'relative' }}
data-representation-kind="diagram"
data-representation-label={state.diagramRefreshedEventPayload.diagram.metadata.label}>
<MarkerDefinitions />
<FullscreenContextProvider>
<DialogContextProvider>
<DiagramRenderer
key={state.diagramRefreshedEventPayload.diagram.id}
diagramRefreshedEventPayload={state.diagramRefreshedEventPayload}
/>
</DialogContextProvider>
</FullscreenContextProvider>
</div>
</NodeContextProvider>
</DropNodeContextProvider>
</ConnectorContextProvider>
</DiagramElementPaletteContextProvider>
</DiagramPaletteContextProvider>
</DiagramDirectEditContextProvider>
</StoreContextProvider>
</DiagramDescriptionContext.Provider>
</DiagramContext.Provider>
<DiagramDirectEditContextProvider>
<DiagramPaletteContextProvider>
<DiagramElementPaletteContextProvider>
<ConnectorContextProvider>
<DropNodeContextProvider>
<NodeContextProvider>
<MarkerDefinitions />
<FullscreenContextProvider>
<DiagramDescriptionContext.Provider value={{ diagramDescription }}>
<DiagramSubscriptionProvider
diagramId={representationId}
editingContextId={editingContextId}
readOnly={readOnly}></DiagramSubscriptionProvider>
</DiagramDescriptionContext.Provider>
</FullscreenContextProvider>
</NodeContextProvider>
</DropNodeContextProvider>
</ConnectorContextProvider>
</DiagramElementPaletteContextProvider>
</DiagramPaletteContextProvider>
</DiagramDirectEditContextProvider>
</ReactFlowProvider>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,10 @@
*******************************************************************************/

import { GQLNodeDescription } from '../graphql/query/nodeDescriptionFragment.types';
import {
GQLDiagramEventPayload,
GQLDiagramRefreshedEventPayload,
} from '../graphql/subscription/diagramEventSubscription.types';
import { GQLDiagramEventPayload } from '../graphql/subscription/diagramEventSubscription.types';

export interface DiagramRepresentationState {
id: string;
diagramRefreshedEventPayload: GQLDiagramRefreshedEventPayload | null;
payload: GQLDiagramEventPayload | null;
complete: boolean;
message: string | null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/

import LinearProgress from '@mui/material/LinearProgress';
import Typography from '@mui/material/Typography';
import { memo, useEffect, useState } from 'react';
import { DiagramContext } from '../contexts/DiagramContext';
import {
GQLDiagramEventPayload,
GQLDiagramRefreshedEventPayload,
} from '../graphql/subscription/diagramEventSubscription.types';
import { DiagramRenderer } from '../renderer/DiagramRenderer';
import { DiagramSubscriptionProviderProps, DiagramSubscriptionState } from './DiagramSubscriptionProvider.types';
import { StoreContextProvider } from './StoreContext';
import { useDiagramSubscription } from './useDiagramSubscription';

const isDiagramRefreshedEventPayload = (
payload: GQLDiagramEventPayload | null
): payload is GQLDiagramRefreshedEventPayload => !!payload && payload.__typename === 'DiagramRefreshedEventPayload';

export const DiagramSubscriptionProvider = memo(
({ diagramId, editingContextId, readOnly }: DiagramSubscriptionProviderProps) => {
const [state, setState] = useState<DiagramSubscriptionState>({
id: crypto.randomUUID(),
diagramRefreshedEventPayload: null,
complete: false,
message: '',
});

const { loading, complete, payload } = useDiagramSubscription(editingContextId, diagramId);

useEffect(() => {
if (isDiagramRefreshedEventPayload(payload)) {
setState((prevState) => ({ ...prevState, diagramRefreshedEventPayload: payload }));
}
}, [payload]);

if (loading || !state.diagramRefreshedEventPayload) {
return <LinearProgress />;
}

if (complete) {
return (
<div>
<Typography variant="subtitle2">The representation is not available anymore</Typography>
</div>
);
}

return (
<StoreContextProvider>
<DiagramContext.Provider
value={{
editingContextId,
diagramId: diagramId,
refreshEventPayloadId: state.diagramRefreshedEventPayload.id,
payload: payload,
readOnly,
}}>
<DiagramRenderer diagramRefreshedEventPayload={state.diagramRefreshedEventPayload} />
</DiagramContext.Provider>
</StoreContextProvider>
);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
import { GQLDiagramRefreshedEventPayload } from '../graphql/subscription/diagramEventSubscription.types';

export type DiagramSubscriptionContextValue = {
diagramRefreshedEventPayload: GQLDiagramRefreshedEventPayload | null;
refreshEventPayloadId: string;
};

export type DiagramSubscriptionState = {
id: string;
diagramRefreshedEventPayload: GQLDiagramRefreshedEventPayload | null;
complete: boolean;
message: string;
};

export interface DiagramSubscriptionProviderProps {
editingContextId: string;
diagramId: string;
readOnly: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (c) 2024 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/

import { gql, useSubscription } from '@apollo/client';
import { useMultiToast } from '@eclipse-sirius/sirius-components-core';
import { useEffect, useState } from 'react';
import { diagramEventSubscription } from '../graphql/subscription/diagramEventSubscription';
import {
GQLDiagramEventSubscription,
GQLDiagramEventVariables,
UseDiagramSubscriptionState,
UseDiagramSubscriptionValue,
} from './useDiagramSubscription.types';

export const useDiagramSubscription = (editingContextId: string, diagramId: string): UseDiagramSubscriptionValue => {
const [state, setState] = useState<UseDiagramSubscriptionState>({
id: crypto.randomUUID(),
complete: false,
payload: null,
});

const variables: GQLDiagramEventVariables = {
input: {
id: state.id,
editingContextId,
diagramId,
},
};

const onComplete = () => {
setState((prevState) => ({ ...prevState, diagramRefreshedEventPayload: null, complete: true }));
};

const { error, loading, data } = useSubscription<GQLDiagramEventSubscription, GQLDiagramEventVariables>(
gql(diagramEventSubscription),
{
variables,
fetchPolicy: 'no-cache',
onComplete,
}
);

const { addErrorMessage } = useMultiToast();
useEffect(() => {
if (error) {
addErrorMessage('An unexpected error has occurred, please refresh the page');
}
}, [error]);

return {
loading,
payload: data?.diagramEvent ?? null,
complete: state.complete,
};
};
Loading

0 comments on commit 18964c6

Please sign in to comment.