diff --git a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx index 7ce902a27f..4c4fb03d46 100644 --- a/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx +++ b/src/components/graph/menus/network-modifications/network-modification-node-editor.tsx @@ -193,52 +193,60 @@ const NetworkModificationNodeEditor = () => { broadcast.onmessage = (event) => { console.info('message received from broadcast channel: ', event.data); isInitiatingCopyTab.current = false; - if (JSON.stringify(emptyCopiedModificationsSelection) === JSON.stringify(event.data)) { - cleanClipboard(); + if (JSON.stringify(emptyCopiedModificationsSelection) === JSON.stringify(event.data.modificationsToCopy)) { + cleanCurrentTabClipboard(event.data.message); } else { - setCopiedModifications(event.data.modificationsUuids); + setCopiedModifications(event.data.modificationsToCopy.modificationsUuids); setCopyInfos({ - copyType: event.data.copyInfos.copyType, - originStudyUuid: event.data.copyInfos.originStudyUuid, - originNodeUuid: event.data.copyInfos.originNodeUuid, + copyType: event.data.modificationsToCopy.copyInfos.copyType, + originStudyUuid: event.data.modificationsToCopy.copyInfos.originStudyUuid, + originNodeUuid: event.data.modificationsToCopy.copyInfos.originNodeUuid, }); - snackInfo({ messageId: 'CopiedModificationUpdateMessageFromAnotherStudy' }); + snackInfo({ messageId: event.data.message }); } }; return broadcast; }); - useEffect(() => { - //If the tab is closed we want to invalidate the copy on all tabs because we won't able to track the node modification - window.addEventListener('beforeunload', (event) => { - if (true === isInitiatingCopyTab.current) { - broadcastChannel.postMessage(emptyCopiedModificationsSelection); - snackInfo({ - messageId: 'CopiedModificationInvalidationMessageAfterTabClosure', - }); - } - }); - }, [broadcastChannel, snackInfo]); - - const cleanClipboard = useCallback( - (showSnackInfo: boolean = true) => { + const cleanCurrentTabClipboard = useCallback( + (snackInfoMessage?: string) => { setCopyInfos(null); setCopiedModifications((oldCopiedModifications) => { - if (oldCopiedModifications.length && showSnackInfo) { + if (oldCopiedModifications.length && snackInfoMessage) { snackInfo({ - messageId: 'CopiedModificationInvalidationMessage', + messageId: snackInfoMessage, }); } return []; }); + }, + [snackInfo] + ); + const cleanOtherTabsClipboard = useCallback( + (snackInfoMessage?: string) => { if (true === isInitiatingCopyTab.current) { - broadcastChannel.postMessage(emptyCopiedModificationsSelection); + broadcastChannel.postMessage({ + modificationsToCopy: emptyCopiedModificationsSelection, + message: snackInfoMessage, + }); isInitiatingCopyTab.current = false; } }, - [snackInfo, broadcastChannel] + [broadcastChannel] ); + const cleanClipboard = useCallback(() => { + cleanCurrentTabClipboard('copiedModificationsInvalidationMsg'); + cleanOtherTabsClipboard('copiedModificationsInvalidationMsgFromOtherStudy'); + }, [cleanCurrentTabClipboard, cleanOtherTabsClipboard]); + + useEffect(() => { + //If the tab is closed we want to invalidate the copy on all tabs because we won't able to track the node modification + window.addEventListener('beforeunload', (event) => { + cleanOtherTabsClipboard('copiedModificationsInvalidationMsgFromStudyClosure'); + }); + }, [cleanOtherTabsClipboard]); + // TODO this is not complete. // We should clean Clipboard on notifications when another user edit // a modification on a public study which is in the clipboard. @@ -1078,7 +1086,9 @@ const NetworkModificationNodeEditor = () => { originStudyUuid: studyUuid ?? undefined, originNodeUuid: currentNode?.id, }); - }, [currentNode?.id, selectedModificationsIds, studyUuid]); + isInitiatingCopyTab.current = true; + cleanOtherTabsClipboard('copiedModificationsInvalidationMsg'); + }, [cleanOtherTabsClipboard, currentNode?.id, selectedModificationsIds, studyUuid]); const doCopyModifications = useCallback(() => { setCopiedModifications(selectedModificationsIds()); @@ -1088,12 +1098,15 @@ const NetworkModificationNodeEditor = () => { originNodeUuid: currentNode?.id, }); broadcastChannel.postMessage({ - modificationsUuids: selectedModificationsIds(), - copyInfos: { - copyType: NetworkModificationCopyType.COPY, - originStudyUuid: studyUuid, - originNodeUuid: currentNode?.id, + modificationsToCopy: { + modificationsUuids: selectedModificationsIds(), + copyInfos: { + copyType: NetworkModificationCopyType.COPY, + originStudyUuid: studyUuid, + originNodeUuid: currentNode?.id, + }, }, + message: 'copiedModificationsUpdateMsg', }); isInitiatingCopyTab.current = true; }, [broadcastChannel, currentNode?.id, selectedModificationsIds, studyUuid]); @@ -1105,7 +1118,7 @@ const NetworkModificationNodeEditor = () => { if (copyInfos.copyType === NetworkModificationCopyType.MOVE) { copyOrMoveModifications(studyUuid, currentNode.id, copiedModifications, copyInfos) .then(() => { - cleanClipboard(false); + cleanCurrentTabClipboard(); }) .catch((errmsg) => { snackError({ @@ -1121,7 +1134,7 @@ const NetworkModificationNodeEditor = () => { }); }); } - }, [copyInfos, studyUuid, currentNode?.id, copiedModifications, cleanClipboard, snackError]); + }, [copyInfos, studyUuid, currentNode?.id, copiedModifications, cleanCurrentTabClipboard, snackError]); const removeNullFields = useCallback((data: NetworkModificationData) => { let dataTemp = data; diff --git a/src/components/network-modification-tree-pane.jsx b/src/components/network-modification-tree-pane.jsx index fcc55e84c5..eb2539c036 100644 --- a/src/components/network-modification-tree-pane.jsx +++ b/src/components/network-modification-tree-pane.jsx @@ -74,19 +74,32 @@ export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid [dispatch] ); + const dispatchNoNodeSelectionForCopy = useCallback( + (snackInfoMessage = null) => { + if (nodeSelectionForCopyRef.current.nodeId && snackInfoMessage) { + snackInfo({ + messageId: snackInfoMessage, + }); + } + dispatch(setNodeSelectionForCopy(noNodeSelectionForCopy)); + }, + [dispatch, snackInfo] + ); + const [broadcastChannel] = useState(() => { const broadcast = new BroadcastChannel('nodeCopyChannel'); broadcast.onmessage = (event) => { - console.info('message received from broadcast channel'); - console.info(event.data); + console.info('message received from broadcast channel: ', event.data); isInitiatingCopyTab.current = false; - if (JSON.stringify(noNodeSelectionForCopy) === JSON.stringify(event.data)) { - dispatch(setNodeSelectionForCopy(noNodeSelectionForCopy)); - snackInfo({ - messageId: 'CopiedNodeInvalidationMessage', - }); + if (JSON.stringify(noNodeSelectionForCopy) === JSON.stringify(event.data.nodeToCopy)) { + dispatchNoNodeSelectionForCopy(event.data.message); } else { - dispatchNodeSelectionForCopy(event.data.sourceStudyUuid, event.data.nodeId, event.data.copyType); + dispatchNodeSelectionForCopy( + event.data.nodeToCopy.sourceStudyUuid, + event.data.nodeToCopy.nodeId, + event.data.nodeToCopy.copyType + ); + snackInfo({ messageId: event.data.message }); } }; return broadcast; @@ -100,7 +113,10 @@ export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid //If the tab is closed we want to invalidate the copy on all tabs because we won't able to track the node modification window.addEventListener('beforeunload', (event) => { if (true === isInitiatingCopyTab.current) { - broadcastChannel.postMessage(noNodeSelectionForCopy); + broadcastChannel.postMessage({ + nodeToCopy: noNodeSelectionForCopy, + message: 'copiedNodeInvalidationMsgFromStudyClosure', + }); } }); //broadcastChannel doesn't change @@ -152,19 +168,19 @@ export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid ); const resetNodeClipboard = useCallback(() => { - dispatch(setNodeSelectionForCopy(noNodeSelectionForCopy)); - snackInfo({ - messageId: 'CopiedNodeInvalidationMessage', - }); + dispatchNoNodeSelectionForCopy('copiedNodeInvalidationMsg'); //only the tab that initiated the copy should update through the websocket, all the other tabs will get the info through broadcast if (true === isInitiatingCopyTab.current) { - broadcastChannel.postMessage(noNodeSelectionForCopy); + broadcastChannel.postMessage({ + nodeToCopy: noNodeSelectionForCopy, + message: 'copiedNodeInvalidationMsgFromOtherStudy', + }); //we need to reset isInitiatingCopyTab here otherwise it won't in the current tab thus next unrelated pasting actions will reset other tabs clipboard isInitiatingCopyTab.current = false; } - }, [broadcastChannel, dispatch, snackInfo]); + }, [broadcastChannel, dispatchNoNodeSelectionForCopy]); const reorderSubtree = useCallback( (parentNodeId, orderedChildrenNodeIds) => { @@ -371,16 +387,30 @@ export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid isInitiatingCopyTab.current = true; dispatchNodeSelectionForCopy(studyUuid, nodeId, CopyType.NODE_COPY); broadcastChannel.postMessage({ - sourceStudyUuid: studyUuid, - nodeId: nodeId, - copyType: CopyType.NODE_COPY, + nodeToCopy: { + sourceStudyUuid: studyUuid, + nodeId: nodeId, + copyType: CopyType.NODE_COPY, + }, + message: 'copiedNodeUpdateMsg', }); }; const handleCutNode = (nodeId) => { - nodeId - ? dispatchNodeSelectionForCopy(studyUuid, nodeId, CopyType.NODE_CUT) - : dispatch(setNodeSelectionForCopy(noNodeSelectionForCopy)); + if (nodeId) { + dispatchNodeSelectionForCopy(studyUuid, nodeId, CopyType.NODE_CUT); + broadcastChannel.postMessage({ + nodeToCopy: noNodeSelectionForCopy, + message: 'copiedNodeInvalidationMsgFromOtherStudy', + }); + } else { + dispatchNoNodeSelectionForCopy(); + broadcastChannel.postMessage({ + nodeToCopy: noNodeSelectionForCopy, + message: null, + }); + } + isInitiatingCopyTab.current = false; }; const handlePasteNode = useCallback( @@ -395,7 +425,7 @@ export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid } ); //Do not wait for the response, after the first CUT / PASTE operation, we can't paste anymore - dispatch(setNodeSelectionForCopy(noNodeSelectionForCopy)); + dispatchNoNodeSelectionForCopy(); } else if (CopyType.NODE_COPY === nodeSelectionForCopyRef.current.copyType) { copyTreeNode( nodeSelectionForCopyRef.current.sourceStudyUuid, @@ -412,7 +442,7 @@ export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid //In copy/paste, we can still paste the same node later } }, - [studyUuid, dispatch, snackError] + [studyUuid, dispatchNoNodeSelectionForCopy, snackError] ); const handleRemoveNode = useCallback( @@ -524,16 +554,30 @@ export const NetworkModificationTreePane = ({ studyUuid, currentRootNetworkUuid isInitiatingCopyTab.current = true; dispatchNodeSelectionForCopy(studyUuid, nodeId, CopyType.SUBTREE_COPY); broadcastChannel.postMessage({ - sourceStudyUuid: studyUuid, - nodeId: nodeId, - copyType: CopyType.SUBTREE_COPY, + nodeToCopy: { + sourceStudyUuid: studyUuid, + nodeId: nodeId, + copyType: CopyType.SUBTREE_COPY, + }, + message: 'copiedNodeInvalidationMsgFromOtherStudy', }); }; const handleCutSubtree = (nodeId) => { - nodeId - ? dispatchNodeSelectionForCopy(studyUuid, nodeId, CopyType.SUBTREE_CUT) - : dispatch(setNodeSelectionForCopy(noNodeSelectionForCopy)); + if (nodeId) { + dispatchNodeSelectionForCopy(studyUuid, nodeId, CopyType.SUBTREE_CUT); + broadcastChannel.postMessage({ + nodeToCopy: noNodeSelectionForCopy, + message: 'copiedNodeInvalidationMsgFromOtherStudy', + }); + } else { + dispatchNoNodeSelectionForCopy(); + broadcastChannel.postMessage({ + nodeToCopy: noNodeSelectionForCopy, + message: null, + }); + } + isInitiatingCopyTab.current = false; }; const handlePasteSubtree = useCallback( diff --git a/src/translations/en.json b/src/translations/en.json index 6af3db65b6..e118632cb5 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -27,11 +27,16 @@ "moveStudyNotification": "The study has been moved from {oldStudyPath} to {studyPath}", - "CopiedNodeInvalidationMessage": "As a result of node modification, the contents of the clipboard have been deleted", - "CopiedModificationInvalidationMessage": "Following a modification of the hypothesis, the content of the clipboard has been deleted", - "CopiedModificationInvalidationMessageFromAnotherStudy": "Following a change in the modifications in another study, the content of the clipboard has been deleted", - "CopiedModificationUpdateMessageFromAnotherStudy": "As a result of copying modifications from another study, the content of the clipboard has been updated", - "CopiedModificationInvalidationMessageAfterTabClosure": "As a result of closing the tab from another study, the content of the clipboard has been deleted", + "copiedNodeInvalidationMsg": "Clipboard content cleared due to changes in nodes", + "copiedNodeInvalidationMsgFromOtherStudy": "Clipboard content cleared due to changes in nodes in another study", + "copiedNodeInvalidationMsgFromStudyClosure": "Clipboard content cleared due to a study being closed", + "copiedNodeUpdateMsg": "Clipboard content updated after copying a node in another study", + + "copiedModificationsInvalidationMsg": "Clipboard content cleared due to changes in modifications", + "copiedModificationsInvalidationMsgFromOtherStudy": "Clipboard content cleared due to changes in modifications in another study", + "copiedModificationsInvalidationMsgFromStudyClosure": "Clipboard content cleared due to a study being closed", + "copiedModificationsUpdateMsg": "Clipboard content updated after copying modifications in another study", + "exportNetwork": "Export the network", "export": "Export", "download.fileName": "File name", diff --git a/src/translations/fr.json b/src/translations/fr.json index cf7b88cd5e..8db6f1447e 100644 --- a/src/translations/fr.json +++ b/src/translations/fr.json @@ -27,11 +27,16 @@ "moveStudyNotification": "L'étude a été déplacée de {oldStudyPath} à {studyPath}", - "CopiedNodeInvalidationMessage": "Suite à modification de nœud, le contenu du presse papier a été supprimé", - "CopiedModificationInvalidationMessage": "Suite à modification d'hypothèse, le contenu du presse papier a été supprimé", - "CopiedModificationInvalidationMessageFromAnotherStudy": "Suite à un changement des modifications dans une autre étude, le contenu du presse papier a été supprimé", - "CopiedModificationUpdateMessageFromAnotherStudy": "Suite à la copie de modifications dans une autre étude, le presse papier a été modifié", - "CopiedModificationInvalidationMessageAfterTabClosure": "Suite à la fermeture de l'onglet d'une autre étude, le presse papier a été supprimé", + "copiedNodeInvalidationMsg": "Contenu du presse-papier supprimé, suite à un changement dans les noeuds", + "copiedNodeInvalidationMsgFromOtherStudy": "Contenu du presse-papier supprimé, suite à un changement dans les noeuds dans une autre étude", + "copiedNodeInvalidationMsgFromStudyClosure": "Contenu du presse-papier supprimé, suite à la fermeture d'une étude", + "copiedNodeUpdateMsg": "Contenu du presse-papier mis à jour, suite à la copie d'un noeud dans une autre étude", + + "copiedModificationsInvalidationMsg": "Contenu du presse-papier supprimé, suite à un changement dans les modifications", + "copiedModificationsInvalidationMsgFromOtherStudy": "Contenu du presse-papier supprimé, suite à un changement dans les modifications dans une autre étude", + "copiedModificationsInvalidationMsgFromStudyClosure": "Contenu du presse-papier supprimé, suite à la fermeture d'une étude", + "copiedModificationsUpdateMsg": "Contenu du presse-papier mis à jour, suite à la copie de modifications dans une autre étude", + "exportNetwork": "Exporter le réseau", "export": "Exporter", "download.fileName": "Nom de fichier",