diff --git a/Composer/packages/client/src/ShellApi.ts b/Composer/packages/client/src/ShellApi.ts index a9bb3c989f..d9a4fb1c5b 100644 --- a/Composer/packages/client/src/ShellApi.ts +++ b/Composer/packages/client/src/ShellApi.ts @@ -1,4 +1,4 @@ -import React, { useEffect, useContext, useRef, useMemo } from 'react'; +import React, { useEffect, useContext, useRef, useMemo, useState } from 'react'; import { debounce, isEqual, get } from 'lodash'; import { parseLgTemplate, checkLgContent, updateTemplateInContent } from '../src/store/action/lg'; @@ -65,6 +65,10 @@ const shellNavigator = (shellPage: string, opts: { id?: string } = {}) => { }; export const ShellApi: React.FC = () => { + // HACK: `onSelect` should actually change some states + // TODO: (leilei, ze) fix it when refactoring shell state management. + const [, forceUpdate] = useState(); + const { state, actions } = useContext(StoreContext); const { dialogs, schemas, lgFiles, luFiles, designPageLocation, focusPath, breadcrumb } = state; const updateDialog = actions.updateDialog; @@ -101,6 +105,7 @@ export const ShellApi: React.FC = () => { apiClient.registerApi('navTo', navTo); apiClient.registerApi('onFocusEvent', focusEvent); apiClient.registerApi('onFocusSteps', focusSteps); + apiClient.registerApi('onSelect', onSelect); apiClient.registerApi('shellNavigate', ({ shellPage, opts }) => shellNavigator(shellPage, opts)); apiClient.registerApi('isExpression', ({ expression }) => isExpression(expression)); apiClient.registerApi('createDialog', () => { @@ -308,5 +313,9 @@ export const ShellApi: React.FC = () => { actions.focusTo(dataPath); } + function onSelect(ids) { + forceUpdate(ids); + } + return null; }; diff --git a/Composer/packages/client/src/extension-container/ExtensionContainer.tsx b/Composer/packages/client/src/extension-container/ExtensionContainer.tsx index 84306e5d61..038e1973cb 100644 --- a/Composer/packages/client/src/extension-container/ExtensionContainer.tsx +++ b/Composer/packages/client/src/extension-container/ExtensionContainer.tsx @@ -56,6 +56,10 @@ const shellApi = { return apiClient.apiCall('onFocusSteps', { subPaths }); }, + onSelect: (ids: string[]) => { + return apiClient.apiCall('onSelect', { ids }); + }, + shellNavigate: (shellPage, opts = {}) => { return apiClient.apiCall('shellNavigate', { shellPage, opts }); }, diff --git a/Composer/packages/client/src/messenger/FrameAPI.ts b/Composer/packages/client/src/messenger/FrameAPI.ts index ce5a1303dc..568871516f 100644 --- a/Composer/packages/client/src/messenger/FrameAPI.ts +++ b/Composer/packages/client/src/messenger/FrameAPI.ts @@ -36,7 +36,7 @@ export const VisualEditorAPI = (() => { return { hasElementFocused: () => visualEditorFrameAPI.invoke('hasElementFocused'), - hasElementSelected: () => visualEditorFrameAPI.invoke('hasElementSelected'), + hasElementSelected: () => visualEditorFrameAPI.invoke('hasElementSelected').catch(() => false), copySelection: () => visualEditorFrameAPI.invoke('copySelection'), cutSelection: () => visualEditorFrameAPI.invoke('cutSelection'), deleteSelection: () => visualEditorFrameAPI.invoke('deleteSelection'), diff --git a/Composer/packages/client/src/pages/design/index.js b/Composer/packages/client/src/pages/design/index.js index 4ce215d8cf..40b3365b5f 100644 --- a/Composer/packages/client/src/pages/design/index.js +++ b/Composer/packages/client/src/pages/design/index.js @@ -184,13 +184,15 @@ function DesignPage(props) { } }; - VisualEditorAPI.hasElementSelected() - .then(selected => { - setNodeOperationAvailability(selected); - }) - .catch(() => { - setNodeOperationAvailability(false); - }); + useEffect(() => { + // HACK: wait until visual editor finish rerender. + // TODO: (ze) expose visual editor store to Shell and (leilei) intercept store events. + setTimeout(() => { + VisualEditorAPI.hasElementSelected().then(selected => { + setNodeOperationAvailability(selected); + }); + }, 100); + }); const toolbarItems = [ { diff --git a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx index 6d85a78745..ff2e7e2210 100644 --- a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx +++ b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx @@ -33,6 +33,7 @@ export const ObiEditor: FC = ({ onFocusSteps, onOpen, onChange, + onSelect, }): JSX.Element | null => { let divRef; @@ -159,11 +160,10 @@ export const ObiEditor: FC = ({ } else { setKeyBoardStatus('normal'); } - }, [focusedId, selectionContext]); - useEffect(() => { - onChange(data); - }, [selectionContext]); + // Notify container at every selection change. + onSelect(selectionContext.selectedIds); + }, [focusedId, selectionContext]); useEffect( (): void => { @@ -309,6 +309,7 @@ ObiEditor.defaultProps = { onFocusEvent: () => {}, onOpen: () => {}, onChange: () => {}, + onSelect: () => {}, }; interface ObiEditorProps { @@ -321,4 +322,5 @@ interface ObiEditorProps { onFocusEvent: (eventId: string) => any; onOpen: (calleeDialog: string, callerId: string) => any; onChange: (newDialog: any) => any; + onSelect: (selection: any) => any; } diff --git a/Composer/packages/extensions/visual-designer/src/index.tsx b/Composer/packages/extensions/visual-designer/src/index.tsx index 52f393b59f..99b63661b7 100644 --- a/Composer/packages/extensions/visual-designer/src/index.tsx +++ b/Composer/packages/extensions/visual-designer/src/index.tsx @@ -30,7 +30,7 @@ const VisualDesigner: React.FC = ({ } const data = dataCache.current; - const { navTo, onFocusEvent, onFocusSteps, saveData, getLgTemplates, removeLgTemplate } = shellApi; + const { navTo, onFocusEvent, onFocusSteps, onSelect, saveData, getLgTemplates, removeLgTemplate } = shellApi; const focusedId = Array.isArray(focusedSteps) && focusedSteps[0] ? focusedSteps[0] : ''; @@ -63,6 +63,7 @@ const VisualDesigner: React.FC = ({ onFocusEvent={onFocusEvent} onOpen={(x, rest) => navTo(x, rest)} onChange={x => saveData(x)} + onSelect={onSelect} /> @@ -87,6 +88,7 @@ VisualDesigner.defaultProps = { navTo: () => {}, onFocusEvent: (eventId: string) => {}, onFocusSteps: (stepIds: string[]) => {}, + onSelect: (ids: string[]) => {}, saveData: () => {}, }, };