diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index a9c490132d..1c0e4d1209 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -2160,6 +2160,7 @@ The `label` value is displayed before the widget to be consistent with other pro - https://github.com/eclipse-sirius/sirius-components/issues/1341[#1341] [diagram] Expose source and target node variables during EdgeTool execution - [core] The input and payloads manipulated by the `EditingContextEventProcessor` can now be logged using ` logging.level.org.eclipse.sirius.components.collaborative.editingcontext.EditingContextEventProcessor=trace` +- https://github.com/eclipse-sirius/sirius-web/issues/4278[#4278] Always use onData callback with useSubscription === New features diff --git a/packages/core/frontend/sirius-components-core/src/workbench/Workbench.tsx b/packages/core/frontend/sirius-components-core/src/workbench/Workbench.tsx index 62657a2f9a..79ea721cab 100644 --- a/packages/core/frontend/sirius-components-core/src/workbench/Workbench.tsx +++ b/packages/core/frontend/sirius-components-core/src/workbench/Workbench.tsx @@ -10,9 +10,10 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMachine } from '@xstate/react'; import { useEffect } from 'react'; +import { flushSync } from 'react-dom'; import { makeStyles } from 'tss-react/mui'; import { StateMachine } from 'xstate'; import { useComponent } from '../extension/useComponent'; @@ -97,7 +98,27 @@ export const Workbench = ({ const { data: representationFactories } = useData(representationFactoryExtensionPoint); - const { error } = useSubscription(editingContextEventSubscription, { + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + const handleDataEvent: HandleSubscriptionResultEvent = { + type: 'HANDLE_SUBSCRIPTION_RESULT', + result: data, + }; + dispatch(handleDataEvent); + }); + }; + + const onError = ({ message }: ApolloError) => { + const showToastEvent: ShowToastEvent = { type: 'SHOW_TOAST', message }; + dispatch(showToastEvent); + }; + + const onComplete = () => { + const completeEvent: HandleCompleteEvent = { type: 'HANDLE_COMPLETE' }; + dispatch(completeEvent); + }; + + useSubscription(editingContextEventSubscription, { variables: { input: { id, @@ -105,27 +126,11 @@ export const Workbench = ({ }, }, fetchPolicy: 'no-cache', - onData: ({ data }) => { - const handleDataEvent: HandleSubscriptionResultEvent = { - type: 'HANDLE_SUBSCRIPTION_RESULT', - result: data, - }; - dispatch(handleDataEvent); - }, - onComplete: () => { - const completeEvent: HandleCompleteEvent = { type: 'HANDLE_COMPLETE' }; - dispatch(completeEvent); - }, + onData, + onComplete, + onError, }); - useEffect(() => { - if (error) { - const { message } = error; - const showToastEvent: ShowToastEvent = { type: 'SHOW_TOAST', message }; - dispatch(showToastEvent); - } - }, [error, dispatch]); - useEffect(() => { const updateSelectedRepresentation: UpdateSelectedRepresentationEvent = { type: 'UPDATE_SELECTED_REPRESENTATION', diff --git a/packages/deck/frontend/sirius-components-deck/src/representation/DeckRepresentation.tsx b/packages/deck/frontend/sirius-components-deck/src/representation/DeckRepresentation.tsx index 2cde74d495..88f3bf8f12 100644 --- a/packages/deck/frontend/sirius-components-deck/src/representation/DeckRepresentation.tsx +++ b/packages/deck/frontend/sirius-components-deck/src/representation/DeckRepresentation.tsx @@ -11,7 +11,7 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { useSubscription } from '@apollo/client'; +import { ApolloError, OnDataOptions, useSubscription } from '@apollo/client'; import { RepresentationComponentProps, UseSelectionValue, @@ -20,8 +20,8 @@ import { } from '@eclipse-sirius/sirius-components-core'; import Typography from '@mui/material/Typography'; import { Theme, useTheme } from '@mui/material/styles'; -import { makeStyles } from 'tss-react/mui'; import { useEffect, useState } from 'react'; +import { makeStyles } from 'tss-react/mui'; import { Deck } from '../Deck'; import { Card, CardMetadata, Lane } from '../Deck.types'; import { @@ -42,6 +42,7 @@ import { GQLLane, } from './deckSubscription.types'; +import { flushSync } from 'react-dom'; import { useDeckMutations } from './useDeckMutations'; const useDeckRepresentationStyles = makeStyles()(() => ({ @@ -68,18 +69,9 @@ export const DeckRepresentation = ({ editingContextId, representationId }: Repre complete: false, }); - const { error } = useSubscription(deckEventSubscription, { - variables: { - input: { - id, - editingContextId, - deckId: representationId, - }, - }, - fetchPolicy: 'no-cache', - - onData: ({ data }) => { - if (data?.data) { + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + if (data.data) { const { deckEvent } = data.data; if (isDeckRefreshedEventPayload(deckEvent)) { setState((prevState) => { @@ -89,23 +81,32 @@ export const DeckRepresentation = ({ editingContextId, representationId }: Repre addMessages(deckEvent.messages); } } + }); + }; + + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const onComplete = () => setState((prevState) => ({ ...prevState, complete: true, deck: undefined })); + + useSubscription(deckEventSubscription, { + variables: { + input: { + id, + editingContextId, + deckId: representationId, + }, }, - onComplete: () => { - setState((prevState) => { - return { ...prevState, complete: true, deck: undefined }; - }); - }, + fetchPolicy: 'no-cache', + onData, + onComplete, + onError, }); const { deleteCard, editDeckCard, createCard, dropDeckCard, editDeckLane, dropDeckLane, changeLaneCollapsedState } = useDeckMutations(editingContextId, representationId); - useEffect(() => { - if (error) { - addErrorMessage(error.message); - } - }, [error]); - useEffect(() => { if (deck && selection.entries) { const selectionIds: string[] = selection.entries.map((entry) => entry.id); diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.tsx b/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.tsx index f0811de6cb..a42a7d83d4 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.tsx @@ -15,6 +15,7 @@ import { gql, OnDataOptions, useQuery, useSubscription } from '@apollo/client'; import { RepresentationComponentProps, useMultiToast } from '@eclipse-sirius/sirius-components-core'; import { ReactFlowProvider } from '@xyflow/react'; import { memo, useEffect, useState } from 'react'; +import { flushSync } from 'react-dom'; import { DiagramContext } from '../contexts/DiagramContext'; import { DiagramDescriptionContext } from '../contexts/DiagramDescriptionContext'; import { DialogContextProvider } from '../dialog/DialogContext'; @@ -97,16 +98,6 @@ export const DiagramRepresentation = memo( }, }; - const onData = ({ data }: OnDataOptions) => { - 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, @@ -132,6 +123,18 @@ export const DiagramRepresentation = memo( } }, [diagramDescriptionLoading, diagramDescriptionData, diagramDescriptionError]); + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + if (data.data) { + const { diagramEvent } = data.data; + if (isDiagramRefreshedEventPayload(diagramEvent)) { + setState((prevState) => ({ ...prevState, diagramRefreshedEventPayload: diagramEvent })); + } + setState((prevState) => ({ ...prevState, payload: diagramEvent })); + } + }); + }; + const onComplete = () => { setState((prevState) => ({ ...prevState, diagramRefreshedEventPayload: null, complete: true })); }; diff --git a/packages/formdescriptioneditors/frontend/sirius-components-formdescriptioneditors/src/FormDescriptionEditorRepresentation.tsx b/packages/formdescriptioneditors/frontend/sirius-components-formdescriptioneditors/src/FormDescriptionEditorRepresentation.tsx index c24a5e3344..96fd38da34 100644 --- a/packages/formdescriptioneditors/frontend/sirius-components-formdescriptioneditors/src/FormDescriptionEditorRepresentation.tsx +++ b/packages/formdescriptioneditors/frontend/sirius-components-formdescriptioneditors/src/FormDescriptionEditorRepresentation.tsx @@ -10,7 +10,7 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useSubscription } from '@apollo/client'; +import { gql, OnDataOptions, useSubscription } from '@apollo/client'; import { RepresentationComponentProps, Toast, useData } from '@eclipse-sirius/sirius-components-core'; import { widgetContributionExtensionPoint } from '@eclipse-sirius/sirius-components-forms'; import ViewAgendaIcon from '@mui/icons-material/ViewAgenda'; @@ -18,6 +18,7 @@ import WebIcon from '@mui/icons-material/Web'; import Typography from '@mui/material/Typography'; import { useMachine } from '@xstate/react'; import React, { useEffect } from 'react'; +import { flushSync } from 'react-dom'; import { makeStyles } from 'tss-react/mui'; import { StateMachine } from 'xstate'; import { formDescriptionEditorEventSubscription } from './FormDescriptionEditorEventFragment'; @@ -30,13 +31,13 @@ import { WidgetDescriptor } from './FormDescriptionEditorRepresentation.types'; import { FormDescriptionEditorRepresentationContext, FormDescriptionEditorRepresentationEvent, + formDescriptionEditorRepresentationMachine, FormDescriptionEditorRepresentationStateSchema, HandleSubscriptionResultEvent, HideToastEvent, InitializeRepresentationEvent, SchemaValue, ShowToastEvent, - formDescriptionEditorRepresentationMachine, } from './FormDescriptionEditorRepresentationMachine'; import { PageList } from './PageList'; import { coreWidgets } from './coreWidgets'; @@ -168,22 +169,29 @@ export const FormDescriptionEditorRepresentation = ({ formDescriptionEditorId: representationId, }; const variables: GQLFormDescriptionEditorEventVariables = { input }; + + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + const handleDataEvent: HandleSubscriptionResultEvent = { + type: 'HANDLE_SUBSCRIPTION_RESULT', + result: data, + }; + dispatch(handleDataEvent); + }); + }; + + const onComplete = () => { + dispatch({ type: 'HANDLE_COMPLETE' }); + }; + const { error } = useSubscription( gql(formDescriptionEditorEventSubscription), { variables, fetchPolicy: 'no-cache', skip: formDescriptionEditorRepresentation !== 'ready', - onData: ({ data }) => { - const handleDataEvent: HandleSubscriptionResultEvent = { - type: 'HANDLE_SUBSCRIPTION_RESULT', - result: data, - }; - dispatch(handleDataEvent); - }, - onComplete: () => { - dispatch({ type: 'HANDLE_COMPLETE' }); - }, + onData, + onComplete, } ); diff --git a/packages/forms/frontend/sirius-components-forms/src/representations/useFormSubscription.tsx b/packages/forms/frontend/sirius-components-forms/src/representations/useFormSubscription.tsx index 57b59e6003..b8432900fc 100644 --- a/packages/forms/frontend/sirius-components-forms/src/representations/useFormSubscription.tsx +++ b/packages/forms/frontend/sirius-components-forms/src/representations/useFormSubscription.tsx @@ -11,9 +11,10 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { formRefreshedEventPayloadFragment } from '../form/FormEventFragments'; import { GQLFormEventInput, @@ -44,6 +45,7 @@ export const useFormSubscription = (editingContextId: string, formId: string): U const [state, setState] = useState({ id: crypto.randomUUID(), complete: false, + payload: null, }); const input: GQLFormEventInput = { @@ -56,37 +58,34 @@ export const useFormSubscription = (editingContextId: string, formId: string): U const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + const onData = ({ data }: OnDataOptions) => { - const { data: gqlDetailsEventSubscription } = data; - if (gqlDetailsEventSubscription) { - const { formEvent: payload } = gqlDetailsEventSubscription; - setState((prevState) => ({ ...prevState, payload, complete: false })); - if (isFormRefreshedEventPayload(payload)) { - setState((prevState) => ({ ...prevState, complete: false })); + flushSync(() => { + if (data.data) { + const { formEvent } = data.data; + setState((prevState) => ({ ...prevState, payload: data.data.formEvent, complete: false })); + if (isFormRefreshedEventPayload(formEvent)) { + setState((prevState) => ({ ...prevState, complete: false })); + } } - } + }); }; - const { data, error, loading } = useSubscription( - gql(formEventSubscription), - { - variables, - fetchPolicy: 'no-cache', - onData, - onComplete, - } - ); - - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); + const { loading } = useSubscription(gql(formEventSubscription), { + variables, + fetchPolicy: 'no-cache', + onData, + onComplete, + onError, + }); return { loading, - payload: !!data?.formEvent ? data.formEvent : null, + payload: state.payload, complete: state.complete, }; }; diff --git a/packages/forms/frontend/sirius-components-forms/src/representations/useFormSubscription.types.ts b/packages/forms/frontend/sirius-components-forms/src/representations/useFormSubscription.types.ts index 476fac4da0..33078eacfe 100644 --- a/packages/forms/frontend/sirius-components-forms/src/representations/useFormSubscription.types.ts +++ b/packages/forms/frontend/sirius-components-forms/src/representations/useFormSubscription.types.ts @@ -22,6 +22,7 @@ export interface UseFormSubscriptionValue { export interface UseFormSubscriptionState { id: string; complete: boolean; + payload: GQLFormEventPayload | null; } export interface GQLFormEventInput { diff --git a/packages/forms/frontend/sirius-components-widget-reference/src/components/useModelBrowserSubscription.tsx b/packages/forms/frontend/sirius-components-widget-reference/src/components/useModelBrowserSubscription.tsx index 33b9bc99eb..a472a9e6be 100644 --- a/packages/forms/frontend/sirius-components-widget-reference/src/components/useModelBrowserSubscription.tsx +++ b/packages/forms/frontend/sirius-components-widget-reference/src/components/useModelBrowserSubscription.tsx @@ -11,10 +11,11 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; import { getTreeEventSubscription } from '@eclipse-sirius/sirius-components-trees'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLModelBrowserEventData, GQLModelBrowserEventInput, @@ -48,36 +49,36 @@ export const useModelBrowserSubscription = ( const variables: GQLModelBrowserEventVariables = { input }; + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + const onData = ({ data }: OnDataOptions) => { - const { data: gqlTreeData } = data; - if (gqlTreeData) { - const { modelBrowserEvent: payload } = gqlTreeData; - if (isTreeRefreshedEventPayload(payload)) { - const { tree } = payload; - setState((prevState) => ({ ...prevState, tree })); + flushSync(() => { + if (data.data) { + const { modelBrowserEvent } = data.data; + if (isTreeRefreshedEventPayload(modelBrowserEvent)) { + const { tree } = modelBrowserEvent; + setState((prevState) => ({ ...prevState, tree })); + } } - } + }); }; const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const { error, loading } = useSubscription( + const { loading } = useSubscription( gql(getTreeEventSubscription(maxDepth, 'modelBrowserEvent', 'ModelBrowserEventInput')), { variables, fetchPolicy: 'no-cache', onData, onComplete, + onError, } ); - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); - return { loading, tree: state.tree, diff --git a/packages/gantt/frontend/sirius-components-gantt/src/representation/GanttRepresentation.tsx b/packages/gantt/frontend/sirius-components-gantt/src/representation/GanttRepresentation.tsx index eb4aefdd58..c75dbf5762 100644 --- a/packages/gantt/frontend/sirius-components-gantt/src/representation/GanttRepresentation.tsx +++ b/packages/gantt/frontend/sirius-components-gantt/src/representation/GanttRepresentation.tsx @@ -11,10 +11,11 @@ * Obeo - initial API and implementation *******************************************************************************/ import { Task, TaskOrEmpty } from '@ObeoNetwork/gantt-task-react'; -import { useSubscription } from '@apollo/client'; +import { ApolloError, OnDataOptions, useSubscription } from '@apollo/client'; import { RepresentationComponentProps, useMultiToast, useSelection } from '@eclipse-sirius/sirius-components-core'; import Typography from '@mui/material/Typography'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { makeStyles } from 'tss-react/mui'; import { useGanttMutations } from '../graphql/mutation/useGanttMutations'; import { @@ -64,9 +65,7 @@ const isErrorPayload = (payload: GQLGanttEventPayload): payload is GQLErrorPaylo */ export const GanttRepresentation = ({ editingContextId, representationId }: RepresentationComponentProps) => { const { classes } = useGanttRepresentationStyles(); - const { addErrorMessage, addMessages } = useMultiToast(); - const { setSelection } = useSelection(); const [{ id, gantt, complete }, setState] = useState({ @@ -75,18 +74,14 @@ export const GanttRepresentation = ({ editingContextId, representationId }: Repr complete: false, }); - const { error } = useSubscription(ganttEventSubscription, { - variables: { - input: { - id, - editingContextId, - ganttId: representationId, - }, - }, - fetchPolicy: 'no-cache', - onData: ({ data: subscriptionData }) => { - if (subscriptionData?.data) { - const { ganttEvent } = subscriptionData.data; + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + if (data.data) { + const { ganttEvent } = data.data; if (isGanttRefreshedEventPayload(ganttEvent)) { setState((previousState) => { return { ...previousState, gantt: ganttEvent.gantt }; @@ -95,7 +90,20 @@ export const GanttRepresentation = ({ editingContextId, representationId }: Repr addMessages(ganttEvent.messages); } } + }); + }; + + useSubscription(ganttEventSubscription, { + variables: { + input: { + id, + editingContextId, + ganttId: representationId, + }, }, + fetchPolicy: 'no-cache', + onData, + onError, onComplete: () => { setState((previousState) => { return { ...previousState, complete: true, gantt: null }; @@ -103,12 +111,6 @@ export const GanttRepresentation = ({ editingContextId, representationId }: Repr }, }); - useEffect(() => { - if (error) { - addErrorMessage(error.message); - } - }, [error]); - //--------------------------------- // Mutations const { diff --git a/packages/portals/frontend/sirius-components-portals/src/hooks/usePortal.ts b/packages/portals/frontend/sirius-components-portals/src/hooks/usePortal.ts index 9dbc3f943e..5a54f47a53 100644 --- a/packages/portals/frontend/sirius-components-portals/src/hooks/usePortal.ts +++ b/packages/portals/frontend/sirius-components-portals/src/hooks/usePortal.ts @@ -10,8 +10,10 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useSubscription } from '@apollo/client'; -import { useEffect, useState } from 'react'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLPortalEventPayload, GQLPortalEventSubscription, @@ -70,27 +72,31 @@ export const usePortal = (editingContextId: string, representationId: string): U }, }; - const { error } = useSubscription(portalEventSubscription, { - variables, - fetchPolicy: 'no-cache', - onData: ({ data }) => { + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { if (data.data) { const { portalEvent } = data.data; if (isPortalRefreshedEventPayload(portalEvent)) { setState((prevState) => ({ ...prevState, portal: portalEvent.portal })); } } - }, - onComplete: () => { - setState((prevState) => ({ ...prevState, portal: null, complete: true })); - }, - }); + }); + }; - useEffect(() => { - if (error) { - setState((prevState) => ({ ...prevState, message: error.message })); - } - }, [error]); + const onComplete = () => setState((prevState) => ({ ...prevState, portal: null, complete: true })); + + useSubscription(portalEventSubscription, { + variables, + fetchPolicy: 'no-cache', + onData, + onComplete, + onError, + }); return { portal: state.portal, complete: state.complete, message: state.message }; }; diff --git a/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.tsx b/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.tsx index 502825d2f7..45b8fe3fca 100644 --- a/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.tsx +++ b/packages/selection/frontend/sirius-components-selection/src/useSelectionDialogTreeSubscription.tsx @@ -11,10 +11,11 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; import { getTreeEventSubscription } from '@eclipse-sirius/sirius-components-trees'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLSelectionDialogTreeEventData, GQLSelectionDialogTreeEventInput, @@ -49,35 +50,35 @@ export const useSelectionDialogTreeSubscription = ( const variables: GQLSelectionDialogTreeEventVariables = { input }; const onData = ({ data }: OnDataOptions) => { - const { data: gqlTreeData } = data; - if (gqlTreeData) { - const { selectionDialogTreeEvent: payload } = gqlTreeData; - if (isTreeRefreshedEventPayload(payload)) { - const { tree } = payload; - setState((prevState) => ({ ...prevState, tree })); + flushSync(() => { + if (data.data) { + const { selectionDialogTreeEvent } = data.data; + if (isTreeRefreshedEventPayload(selectionDialogTreeEvent)) { + const { tree } = selectionDialogTreeEvent; + setState((prevState) => ({ ...prevState, tree })); + } } - } + }); }; const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const { error, loading } = useSubscription( + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const { loading } = useSubscription( gql(getTreeEventSubscription(maxDepth, 'selectionDialogTreeEvent', 'SelectionDialogTreeEventInput')), { variables, fetchPolicy: 'no-cache', onData, onComplete, + onError, } ); - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); - return { loading, tree: state.tree, diff --git a/packages/sirius-web/frontend/sirius-web-application/src/diagrams/useDiagramFilterSubscription.tsx b/packages/sirius-web/frontend/sirius-web-application/src/diagrams/useDiagramFilterSubscription.tsx index 5e4f696e1e..b5eb0c2c41 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/diagrams/useDiagramFilterSubscription.tsx +++ b/packages/sirius-web/frontend/sirius-web-application/src/diagrams/useDiagramFilterSubscription.tsx @@ -11,10 +11,11 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; import { formRefreshedEventPayloadFragment } from '@eclipse-sirius/sirius-components-forms'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLDiagramFilterEventInput, GQLDiagramFilterEventSubscription, @@ -43,6 +44,7 @@ export const useDiagramFilterSubscription = ( const [state, setState] = useState({ id: crypto.randomUUID(), complete: false, + payload: null, }); const input: GQLDiagramFilterEventInput = { @@ -55,10 +57,18 @@ export const useDiagramFilterSubscription = ( const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const onData = ({}: OnDataOptions) => - setState((prevState) => ({ ...prevState, complete: false })); + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + setState((prevState) => ({ ...prevState, payload: data.data.diagramFilterEvent, complete: false })); + }); + }; + + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; - const { data, error, loading } = useSubscription( + const { loading } = useSubscription( gql(getDiagramFilterEventSubscription), { variables, @@ -66,19 +76,13 @@ export const useDiagramFilterSubscription = ( skip, onData, onComplete, + onError, } ); - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); - return { loading, - payload: data?.diagramFilterEvent ?? null, + payload: state.payload, complete: state.complete, }; }; diff --git a/packages/sirius-web/frontend/sirius-web-application/src/diagrams/useDiagramFilterSubscription.types.ts b/packages/sirius-web/frontend/sirius-web-application/src/diagrams/useDiagramFilterSubscription.types.ts index 214a63e98b..40107ce445 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/diagrams/useDiagramFilterSubscription.types.ts +++ b/packages/sirius-web/frontend/sirius-web-application/src/diagrams/useDiagramFilterSubscription.types.ts @@ -22,6 +22,7 @@ export interface UseDiagramFilterSubscriptionValue { export interface UseDiagramFilterSubscriptionState { id: string; complete: boolean; + payload: GQLDiagramFilterEventPayload | null; } export interface GQLDiagramFilterEventInput { diff --git a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/EditProjectNavbar/EditProjectNavbar.tsx b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/EditProjectNavbar/EditProjectNavbar.tsx index 22186b04bc..666652d55b 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/EditProjectNavbar/EditProjectNavbar.tsx +++ b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/EditProjectNavbar/EditProjectNavbar.tsx @@ -10,7 +10,7 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { ComponentExtension, Toast, useComponent, useComponents } from '@eclipse-sirius/sirius-components-core'; import DeleteIcon from '@mui/icons-material/Delete'; import EditIcon from '@mui/icons-material/Edit'; @@ -24,7 +24,8 @@ import MenuItem from '@mui/material/MenuItem'; import Typography from '@mui/material/Typography'; import { emphasize } from '@mui/material/styles'; import { useMachine } from '@xstate/react'; -import React, { useEffect } from 'react'; +import React from 'react'; +import { flushSync } from 'react-dom'; import { Navigate, Link as RouterLink } from 'react-router-dom'; import { makeStyles } from 'tss-react/mui'; import { StateMachine } from 'xstate'; @@ -41,6 +42,7 @@ import { editProjectNavbarSubtitleExtensionPoint } from './EditProjectNavbarExte import { EditProjectNavbarContext, EditProjectNavbarEvent, + editProjectNavbarMachine, EditProjectNavbarStateSchema, HandleCloseContextMenuEvent, HandleCloseModalEvent, @@ -52,7 +54,6 @@ import { HideToastEvent, SchemaValue, ShowToastEvent, - editProjectNavbarMachine, } from './EditProjectNavbarMachine'; import { editProjectNavbarMenuContainerExtensionPoint, @@ -110,7 +111,22 @@ export const EditProjectNavbar = ({ readOnly }: EditProjectNavbarProps) => { const { toast, navbar } = value as SchemaValue; const { id, to, modalDisplayed, projectMenuAnchor, projectName, message } = context; - const { error } = useSubscription(projectEventSubscription, { + const onError = ({ message }: ApolloError) => { + const showToastEvent: ShowToastEvent = { type: 'SHOW_TOAST', message }; + dispatch(showToastEvent); + }; + + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + const handleDataEvent: HandleSubscriptionResultEvent = { + type: 'HANDLE_SUBSCRIPTION_RESULT', + result: data, + }; + dispatch(handleDataEvent); + }); + }; + + useSubscription(projectEventSubscription, { variables: { input: { id, @@ -119,27 +135,14 @@ export const EditProjectNavbar = ({ readOnly }: EditProjectNavbarProps) => { }, fetchPolicy: 'no-cache', skip: navbar === 'complete', - onData: ({ data }) => { - const handleDataEvent: HandleSubscriptionResultEvent = { - type: 'HANDLE_SUBSCRIPTION_RESULT', - result: data, - }; - dispatch(handleDataEvent); - }, + onData, onComplete: () => { const completeEvent: HandleCompleteEvent = { type: 'HANDLE_COMPLETE' }; dispatch(completeEvent); }, + onError, }); - useEffect(() => { - if (error) { - const { message } = error; - const showToastEvent: ShowToastEvent = { type: 'SHOW_TOAST', message }; - dispatch(showToastEvent); - } - }, [error]); - const onMoreClick = (event: React.MouseEvent) => { if (navbar === 'empty') { const showContextMenu: HandleShowContextMenuEvent = { diff --git a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useDetailsViewSubscription.tsx b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useDetailsViewSubscription.tsx index 1d61650429..e5d75f34f4 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useDetailsViewSubscription.tsx +++ b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useDetailsViewSubscription.tsx @@ -11,10 +11,11 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; import { formRefreshedEventPayloadFragment } from '@eclipse-sirius/sirius-components-forms'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLDetailsEventInput, GQLDetailsEventSubscription, @@ -54,12 +55,20 @@ export const useDetailsViewSubscription = ( const variables: GQLDetailsEventVariables = { input }; - const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + setState((prevState) => ({ ...prevState, payload: data.data.detailsEvent, complete: false })); + }); + }; - const onData = ({}: OnDataOptions) => - setState((prevState) => ({ ...prevState, complete: false })); + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; - const { data, error, loading } = useSubscription( + const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); + + const { loading } = useSubscription( gql(getDetailsViewEventSubscription), { variables, @@ -67,19 +76,13 @@ export const useDetailsViewSubscription = ( skip, onData, onComplete, + onError, } ); - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); - return { loading, - payload: data?.detailsEvent ?? null, + payload: state.payload, complete: state.complete, }; }; diff --git a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRelatedElementsViewSubscription.tsx b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRelatedElementsViewSubscription.tsx index 26ac00cd61..734c31fc60 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRelatedElementsViewSubscription.tsx +++ b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRelatedElementsViewSubscription.tsx @@ -11,10 +11,11 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; import { formRefreshedEventPayloadFragment } from '@eclipse-sirius/sirius-components-forms'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLRelatedElementsEventInput, GQLRelatedElementsEventSubscription, @@ -43,6 +44,7 @@ export const useRelatedElementsViewSubscription = ( const [state, setState] = useState({ id: crypto.randomUUID(), complete: false, + payload: null, }); const input: GQLRelatedElementsEventInput = { @@ -55,30 +57,31 @@ export const useRelatedElementsViewSubscription = ( const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const onData = ({}: OnDataOptions) => - setState((prevState) => ({ ...prevState, complete: false })); - - const { data, error, loading } = useSubscription< - GQLRelatedElementsEventSubscription, - GQLRelatedElementsEventVariables - >(gql(getRelatedElementsViewEventSubscription), { - variables, - fetchPolicy: 'no-cache', - skip, - onData, - onComplete, - }); + const onData = ({ data }: OnDataOptions) => + flushSync(() => { + setState((prevState) => ({ ...prevState, payload: data.data.relatedElementsEvent, complete: false })); + }); const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const { loading } = useSubscription( + gql(getRelatedElementsViewEventSubscription), + { + variables, + fetchPolicy: 'no-cache', + skip, + onData, + onComplete, + onError, } - }, [error]); + ); return { loading, - payload: data?.relatedElementsEvent ?? null, + payload: state.payload, complete: state.complete, }; }; diff --git a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRelatedElementsViewSubscription.types.ts b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRelatedElementsViewSubscription.types.ts index 3247c83a28..1a65fee82a 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRelatedElementsViewSubscription.types.ts +++ b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRelatedElementsViewSubscription.types.ts @@ -22,6 +22,7 @@ export interface UseRelatedElementsViewSubscriptionValue { export interface UseRelatedElementsViewSubscriptionState { id: string; complete: boolean; + payload: GQLRelatedElementsEventPayload | null; } export interface GQLRelatedElementsEventInput { diff --git a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRepresentationsViewSubscription.tsx b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRepresentationsViewSubscription.tsx index d2359a9b63..93173ab07a 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRepresentationsViewSubscription.tsx +++ b/packages/sirius-web/frontend/sirius-web-application/src/views/edit-project/workbench-views/useRepresentationsViewSubscription.tsx @@ -11,10 +11,11 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; import { formRefreshedEventPayloadFragment } from '@eclipse-sirius/sirius-components-forms'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLRepresentationsEventInput, GQLRepresentationsEventSubscription, @@ -56,30 +57,31 @@ export const useRepresentationsViewSubscription = ( const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const onData = ({}: OnDataOptions) => - setState((prevState) => ({ ...prevState, complete: false })); - - const { data, error, loading } = useSubscription< - GQLRepresentationsEventSubscription, - GQLRepresentationsEventVariables - >(gql(getRepresentationsViewEventSubscription), { - variables, - fetchPolicy: 'no-cache', - skip, - onData, - onComplete, - }); + const onData = ({ data }: OnDataOptions) => + flushSync(() => { + setState((prevState) => ({ ...prevState, payload: data.data.representationsEvent, complete: false })); + }); const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const { loading } = useSubscription( + gql(getRepresentationsViewEventSubscription), + { + variables, + fetchPolicy: 'no-cache', + skip, + onData, + onComplete, + onError, } - }, [error]); + ); return { loading, - payload: data?.representationsEvent ?? null, + payload: state.payload, complete: state.complete, }; }; diff --git a/packages/sirius-web/frontend/sirius-web-application/src/views/explorer/useExplorerSubscription.tsx b/packages/sirius-web/frontend/sirius-web-application/src/views/explorer/useExplorerSubscription.tsx index c606d9a9a7..c3a5d7b07c 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/views/explorer/useExplorerSubscription.tsx +++ b/packages/sirius-web/frontend/sirius-web-application/src/views/explorer/useExplorerSubscription.tsx @@ -11,10 +11,11 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; import { getTreeEventSubscription } from '@eclipse-sirius/sirius-components-trees'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLExplorerEventData, GQLExplorerEventInput, @@ -33,6 +34,7 @@ export const useExplorerSubscription = ( const [state, setState] = useState({ id: crypto.randomUUID(), complete: false, + payload: null, }); const input: GQLExplorerEventInput = { @@ -45,28 +47,34 @@ export const useExplorerSubscription = ( const variables: GQLExplorerEventVariables = { input }; + const onData = ({ data }: OnDataOptions) => { + flushSync(() => { + setState((prevState) => ({ ...prevState, payload: data.data.explorerEvent, complete: false })); + }); + }; + const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const { data, error, loading } = useSubscription( + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const { loading } = useSubscription( gql(getTreeEventSubscription(maxDepth, 'explorerEvent', 'ExplorerEventInput')), { variables, fetchPolicy: 'no-cache', + onData, onComplete, + onError, skip: treeDescriptionId === null, } ); - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); - return { loading, - payload: !!data?.explorerEvent ? data.explorerEvent : null, + payload: state.payload, complete: state.complete, }; }; diff --git a/packages/sirius-web/frontend/sirius-web-application/src/views/explorer/useExplorerSubscription.types.ts b/packages/sirius-web/frontend/sirius-web-application/src/views/explorer/useExplorerSubscription.types.ts index 4c78b50fd7..49cae7a024 100644 --- a/packages/sirius-web/frontend/sirius-web-application/src/views/explorer/useExplorerSubscription.types.ts +++ b/packages/sirius-web/frontend/sirius-web-application/src/views/explorer/useExplorerSubscription.types.ts @@ -21,6 +21,7 @@ export interface UseExplorerSubscriptionValue { export interface UseExplorerSubscriptionState { id: string; complete: boolean; + payload: GQLTreeEventPayload | null; } export interface GQLExplorerEventInput { diff --git a/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts b/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts index f3bf37a889..c9d122d7b3 100644 --- a/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts +++ b/packages/tables/frontend/sirius-components-tables/src/representation/useTableSubscription.ts @@ -10,9 +10,10 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLTableColumnFilterPayload, GQLTableEventData, @@ -136,58 +137,56 @@ export const useTableSubscription = (editingContextId: string, representationId: const variables: GQLTableEventVariables = { input }; const onData = ({ data }: OnDataOptions) => { - const { data: gqlTableData } = data; - if (gqlTableData) { - const { tableEvent: payload } = gqlTableData; - if (isTableRefreshedEventPayload(payload)) { - const { table } = payload; - setState((prevState) => ({ ...prevState, table })); - } else if (isTableGlobalFilterValuePayload(payload)) { - const { globalFilterValue } = payload; - setState((prevState) => { - if (prevState.table) { - return { - ...prevState, - table: { ...prevState.table, globalFilter: globalFilterValue }, - }; - } else { - return prevState; - } - }); - } else if (isTableColumnFilterPayload(payload)) { - const { columnFilters } = payload; - setState((prevState) => { - if (prevState.table) { - return { - ...prevState, - table: { ...prevState.table, columnFilters: columnFilters }, - }; - } else { - return prevState; - } - }); + flushSync(() => { + const { data: gqlTableData } = data; + if (gqlTableData) { + const { tableEvent: payload } = gqlTableData; + if (isTableRefreshedEventPayload(payload)) { + const { table } = payload; + setState((prevState) => ({ ...prevState, table })); + } else if (isTableGlobalFilterValuePayload(payload)) { + const { globalFilterValue } = payload; + setState((prevState) => { + if (prevState.table) { + return { + ...prevState, + table: { ...prevState.table, globalFilter: globalFilterValue }, + }; + } else { + return prevState; + } + }); + } else if (isTableColumnFilterPayload(payload)) { + const { columnFilters } = payload; + setState((prevState) => { + if (prevState.table) { + return { + ...prevState, + table: { ...prevState.table, columnFilters: columnFilters }, + }; + } else { + return prevState; + } + }); + } } - } + }); }; const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const { error, loading } = useSubscription( - gql(getTableEventSubscription), - { - variables, - fetchPolicy: 'no-cache', - onData, - onComplete, - } - ); - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const { loading } = useSubscription(gql(getTableEventSubscription), { + variables, + fetchPolicy: 'no-cache', + onData, + onComplete, + onError, + }); return { loading, diff --git a/packages/trees/frontend/sirius-components-trees/src/trees/useTreeSubscription.tsx b/packages/trees/frontend/sirius-components-trees/src/trees/useTreeSubscription.tsx index 1a9cbe6e75..e545746632 100644 --- a/packages/trees/frontend/sirius-components-trees/src/trees/useTreeSubscription.tsx +++ b/packages/trees/frontend/sirius-components-trees/src/trees/useTreeSubscription.tsx @@ -11,11 +11,12 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { getTreeEventSubscription } from '../views/getTreeEventSubscription'; import { GQLTreeEventData, @@ -51,35 +52,35 @@ export const useTreeSubscription = ( const variables: GQLTreeEventVariables = { input }; const onData = ({ data }: OnDataOptions) => { - const { data: gqlTreeData } = data; - if (gqlTreeData) { - const { treeEvent: payload } = gqlTreeData; - if (isTreeRefreshedEventPayload(payload)) { - const { tree } = payload; - setState((prevState) => ({ ...prevState, tree })); + flushSync(() => { + if (data.data) { + const { treeEvent: payload } = data.data; + if (isTreeRefreshedEventPayload(payload)) { + const { tree } = payload; + setState((prevState) => ({ ...prevState, tree })); + } } - } + }); }; const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const { error, loading } = useSubscription( + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; + + const { loading } = useSubscription( gql(getTreeEventSubscription(maxDepth, 'treeEvent', 'TreeEventInput')), { variables, fetchPolicy: 'no-cache', onData, onComplete, + onError, } ); - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); - return { loading, tree: state.tree, diff --git a/packages/validation/frontend/sirius-components-validation/src/useValidationViewSubscription.tsx b/packages/validation/frontend/sirius-components-validation/src/useValidationViewSubscription.tsx index e3004cb26d..f0a43dbbff 100644 --- a/packages/validation/frontend/sirius-components-validation/src/useValidationViewSubscription.tsx +++ b/packages/validation/frontend/sirius-components-validation/src/useValidationViewSubscription.tsx @@ -11,9 +11,10 @@ * Obeo - initial API and implementation *******************************************************************************/ -import { gql, OnDataOptions, useSubscription } from '@apollo/client'; +import { ApolloError, gql, OnDataOptions, useSubscription } from '@apollo/client'; import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { flushSync } from 'react-dom'; import { GQLValidationEventInput, GQLValidationEventSubscription, @@ -48,6 +49,7 @@ export const useValidationViewSubscription = ( const [state, setState] = useState({ id: crypto.randomUUID(), complete: false, + payload: null, }); const input: GQLValidationEventInput = { @@ -60,10 +62,22 @@ export const useValidationViewSubscription = ( const onComplete = () => setState((prevState) => ({ ...prevState, complete: true })); - const onData = ({}: OnDataOptions) => - setState((prevState) => ({ ...prevState, complete: false })); + const onData = ({ data }: OnDataOptions) => + flushSync(() => { + if (data.data) { + const { validationEvent } = data.data; + if (validationEvent) { + setState((prevState) => ({ ...prevState, payload: validationEvent, complete: false })); + } + } + }); + + const { addErrorMessage } = useMultiToast(); + const onError = ({ message }: ApolloError) => { + addErrorMessage(message); + }; - const { data, error, loading } = useSubscription( + const { loading } = useSubscription( gql(getValidationViewEventSubscription), { variables, @@ -71,19 +85,13 @@ export const useValidationViewSubscription = ( skip, onData, onComplete, + onError, } ); - const { addErrorMessage } = useMultiToast(); - useEffect(() => { - if (error) { - addErrorMessage('An unexpected error has occurred, please refresh the page'); - } - }, [error]); - return { loading, - payload: data?.validationEvent ?? null, + payload: state.payload, complete: state.complete, }; }; diff --git a/packages/validation/frontend/sirius-components-validation/src/useValidationViewSubscription.types.ts b/packages/validation/frontend/sirius-components-validation/src/useValidationViewSubscription.types.ts index b98d3bfa5a..be0cef5060 100644 --- a/packages/validation/frontend/sirius-components-validation/src/useValidationViewSubscription.types.ts +++ b/packages/validation/frontend/sirius-components-validation/src/useValidationViewSubscription.types.ts @@ -19,6 +19,7 @@ export interface UseValidationViewSubscriptionValue { export interface UseValidationViewSubscriptionState { id: string; complete: boolean; + payload: GQLValidationEventPayload | null; } export interface GQLValidationEventVariables {