From 8a670a7d82d618af3702b4413c822261c9bd582e Mon Sep 17 00:00:00 2001 From: zeye Date: Thu, 14 Nov 2019 14:33:51 +0800 Subject: [PATCH 01/11] implement adaptive action walker --- .../src/deleteUtils/AdaptiveActionVisitor.ts | 6 ++++ .../src/deleteUtils/walkActionWithChildren.ts | 11 +++++++ .../src/deleteUtils/walkAdaptiveAction.ts | 33 +++++++++++++++++++ .../src/deleteUtils/walkAdaptiveActionList.ts | 11 +++++++ .../shared/src/deleteUtils/walkIfCondition.ts | 12 +++++++ .../src/deleteUtils/walkSwitchCondition.ts | 17 ++++++++++ 6 files changed, 90 insertions(+) create mode 100644 Composer/packages/lib/shared/src/deleteUtils/AdaptiveActionVisitor.ts create mode 100644 Composer/packages/lib/shared/src/deleteUtils/walkActionWithChildren.ts create mode 100644 Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveAction.ts create mode 100644 Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveActionList.ts create mode 100644 Composer/packages/lib/shared/src/deleteUtils/walkIfCondition.ts create mode 100644 Composer/packages/lib/shared/src/deleteUtils/walkSwitchCondition.ts diff --git a/Composer/packages/lib/shared/src/deleteUtils/AdaptiveActionVisitor.ts b/Composer/packages/lib/shared/src/deleteUtils/AdaptiveActionVisitor.ts new file mode 100644 index 0000000000..7390c56fb8 --- /dev/null +++ b/Composer/packages/lib/shared/src/deleteUtils/AdaptiveActionVisitor.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { MicrosoftIDialog } from '../types'; + +export type AdaptiveActionVisitor = (action: MicrosoftIDialog) => void; diff --git a/Composer/packages/lib/shared/src/deleteUtils/walkActionWithChildren.ts b/Composer/packages/lib/shared/src/deleteUtils/walkActionWithChildren.ts new file mode 100644 index 0000000000..a3d64fd90c --- /dev/null +++ b/Composer/packages/lib/shared/src/deleteUtils/walkActionWithChildren.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { AdaptiveActionVisitor } from './AdaptiveActionVisitor'; +import { walkAdaptiveActionList } from './walkAdaptiveActionList'; + +export const walkActionWithChildren = (input, visitor: AdaptiveActionVisitor) => { + visitor(input); + + walkAdaptiveActionList(input.actions, visitor); +}; diff --git a/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveAction.ts b/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveAction.ts new file mode 100644 index 0000000000..474eee51d6 --- /dev/null +++ b/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveAction.ts @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { MicrosoftIDialog, SDKTypes } from '../types'; + +import { walkIfCondition } from './walkIfCondition'; +import { walkSwitchCondition } from './walkSwitchCondition'; +import { walkActionWithChildren } from './walkActionWithChildren'; +import { AdaptiveActionVisitor } from './AdaptiveActionVisitor'; + +const WalkerMap: { [$type: string]: (input, visitor: AdaptiveActionVisitor) => void } = { + [SDKTypes.IfCondition]: walkIfCondition, + [SDKTypes.SwitchCondition]: walkSwitchCondition, + [SDKTypes.Foreach]: walkActionWithChildren, + [SDKTypes.ForeachPage]: walkActionWithChildren, + [SDKTypes.EditActions]: walkActionWithChildren, +}; + +export const walkAdaptiveAction = (input, visit: (action: MicrosoftIDialog) => void): void => { + if (typeof input === 'string') { + visit(input); + return; + } + + if (!input || !input.$type) { + return; + } + + if (WalkerMap[input.$type]) { + WalkerMap[input.$type](input, visit); + } + return; +}; diff --git a/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveActionList.ts b/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveActionList.ts new file mode 100644 index 0000000000..6359116f94 --- /dev/null +++ b/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveActionList.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { MicrosoftIDialog } from '../types'; + +import { walkAdaptiveAction } from './walkAdaptiveAction'; +export const walkAdaptiveActionList = (inputs: MicrosoftIDialog[], visit: (action: MicrosoftIDialog) => void): void => { + if (Array.isArray(inputs)) { + inputs.forEach(action => walkAdaptiveAction(action, visit)); + } +}; diff --git a/Composer/packages/lib/shared/src/deleteUtils/walkIfCondition.ts b/Composer/packages/lib/shared/src/deleteUtils/walkIfCondition.ts new file mode 100644 index 0000000000..cacf194040 --- /dev/null +++ b/Composer/packages/lib/shared/src/deleteUtils/walkIfCondition.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { AdaptiveActionVisitor } from './AdaptiveActionVisitor'; +import { walkAdaptiveActionList } from './walkAdaptiveActionList'; + +export const walkIfCondition = (input, visitor: AdaptiveActionVisitor) => { + visitor(input); + + walkAdaptiveActionList(input.actions, visitor); + walkAdaptiveActionList(input.elseActions, visitor); +}; diff --git a/Composer/packages/lib/shared/src/deleteUtils/walkSwitchCondition.ts b/Composer/packages/lib/shared/src/deleteUtils/walkSwitchCondition.ts new file mode 100644 index 0000000000..c31f86ee1a --- /dev/null +++ b/Composer/packages/lib/shared/src/deleteUtils/walkSwitchCondition.ts @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { AdaptiveActionVisitor } from './AdaptiveActionVisitor'; +import { walkAdaptiveActionList } from './walkAdaptiveActionList'; + +export const walkSwitchCondition = (input, visitor: AdaptiveActionVisitor) => { + visitor(input); + + walkAdaptiveActionList(input.default, visitor); + + if (Array.isArray(input.cases)) { + input.cases.forEach(currentCase => { + walkAdaptiveActionList(currentCase.actions, visitor); + }); + } +}; From b6227220783483ab027f9c1caf0812b14d430060 Mon Sep 17 00:00:00 2001 From: zeye Date: Thu, 14 Nov 2019 14:50:28 +0800 Subject: [PATCH 02/11] implement `deleteAction` --- .../lib/shared/src/deleteUtils/index.ts | 37 +++++++++++++++++++ .../packages/lib/shared/src/dialogFactory.ts | 5 +++ 2 files changed, 42 insertions(+) create mode 100644 Composer/packages/lib/shared/src/deleteUtils/index.ts diff --git a/Composer/packages/lib/shared/src/deleteUtils/index.ts b/Composer/packages/lib/shared/src/deleteUtils/index.ts new file mode 100644 index 0000000000..d062026c15 --- /dev/null +++ b/Composer/packages/lib/shared/src/deleteUtils/index.ts @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { MicrosoftIDialog, SDKTypes } from '../types'; + +import { walkAdaptiveAction } from './walkAdaptiveAction'; + +export const deleteAdaptiveAction = (data: MicrosoftIDialog, deleteLgTemplates: (lgTemplates: string[]) => any) => { + const activityTemplates: string[] = []; + const collectLgTemplates = (action: any) => { + if (typeof action === 'string') return; + if (!action || !action.$type) return; + + switch (action.$type) { + case SDKTypes.SendActivity: + activityTemplates.push(action.activity); + break; + case SDKTypes.AttachmentInput: + case SDKTypes.ChoiceInput: + case SDKTypes.ConfirmInput: + case SDKTypes.DateTimeInput: + case SDKTypes.NumberInput: + case SDKTypes.TextInput: + activityTemplates.push( + action.prompt, + action.unrecognizedPrompt, + action.invalidPrompt, + action.defaultValueResponse + ); + break; + } + }; + + walkAdaptiveAction(data, collectLgTemplates); + + deleteLgTemplates(activityTemplates.filter(activity => !!activity)); +}; diff --git a/Composer/packages/lib/shared/src/dialogFactory.ts b/Composer/packages/lib/shared/src/dialogFactory.ts index 6b6ec6aeda..2ad6fdc566 100644 --- a/Composer/packages/lib/shared/src/dialogFactory.ts +++ b/Composer/packages/lib/shared/src/dialogFactory.ts @@ -5,6 +5,7 @@ import nanoid from 'nanoid/generate'; import { appschema } from './appschema'; import { copyAdaptiveAction } from './copyUtils'; +import { deleteAdaptiveAction } from './deleteUtils'; interface DesignerAttributes { name: string; @@ -105,6 +106,10 @@ export const deepCopyAction = async (data, lgApi) => { return await copyAdaptiveAction(data, { lgApi, updateDesigner }); }; +export const deleteAction = (data, deleteLgTemplates: (templates: string[]) => any) => { + return deleteAdaptiveAction(data, deleteLgTemplates); +}; + export const seedNewDialog = ( $type: string, designerAttributes: Partial = {}, From 4558897def66ae54334d8c4546909a3ae894e640 Mon Sep 17 00:00:00 2001 From: zeye Date: Thu, 14 Nov 2019 15:03:34 +0800 Subject: [PATCH 03/11] add a batch api `deleteActions` --- .../lib/shared/src/deleteUtils/index.ts | 62 ++++++++++--------- .../packages/lib/shared/src/dialogFactory.ts | 9 ++- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/Composer/packages/lib/shared/src/deleteUtils/index.ts b/Composer/packages/lib/shared/src/deleteUtils/index.ts index d062026c15..0c94f765c0 100644 --- a/Composer/packages/lib/shared/src/deleteUtils/index.ts +++ b/Composer/packages/lib/shared/src/deleteUtils/index.ts @@ -4,34 +4,40 @@ import { MicrosoftIDialog, SDKTypes } from '../types'; import { walkAdaptiveAction } from './walkAdaptiveAction'; +import { walkAdaptiveActionList } from './walkAdaptiveActionList'; + +const collectLgTemplates = (action: any, outputTemplates: string[]) => { + if (typeof action === 'string') return; + if (!action || !action.$type) return; + + switch (action.$type) { + case SDKTypes.SendActivity: + outputTemplates.push(action.activity); + break; + case SDKTypes.AttachmentInput: + case SDKTypes.ChoiceInput: + case SDKTypes.ConfirmInput: + case SDKTypes.DateTimeInput: + case SDKTypes.NumberInput: + case SDKTypes.TextInput: + outputTemplates.push(action.prompt, action.unrecognizedPrompt, action.invalidPrompt, action.defaultValueResponse); + break; + } +}; export const deleteAdaptiveAction = (data: MicrosoftIDialog, deleteLgTemplates: (lgTemplates: string[]) => any) => { - const activityTemplates: string[] = []; - const collectLgTemplates = (action: any) => { - if (typeof action === 'string') return; - if (!action || !action.$type) return; - - switch (action.$type) { - case SDKTypes.SendActivity: - activityTemplates.push(action.activity); - break; - case SDKTypes.AttachmentInput: - case SDKTypes.ChoiceInput: - case SDKTypes.ConfirmInput: - case SDKTypes.DateTimeInput: - case SDKTypes.NumberInput: - case SDKTypes.TextInput: - activityTemplates.push( - action.prompt, - action.unrecognizedPrompt, - action.invalidPrompt, - action.defaultValueResponse - ); - break; - } - }; - - walkAdaptiveAction(data, collectLgTemplates); - - deleteLgTemplates(activityTemplates.filter(activity => !!activity)); + const lgTemplates: string[] = []; + walkAdaptiveAction(data, action => collectLgTemplates(action, lgTemplates)); + + deleteLgTemplates(lgTemplates.filter(activity => !!activity)); +}; + +export const deleteAdaptiveActionList = ( + data: MicrosoftIDialog[], + deleteLgTemplates: (lgTemplates: string[]) => any +) => { + const lgTemplates: string[] = []; + walkAdaptiveActionList(data, action => collectLgTemplates(action, lgTemplates)); + + deleteLgTemplates(lgTemplates.filter(activity => !!activity)); }; diff --git a/Composer/packages/lib/shared/src/dialogFactory.ts b/Composer/packages/lib/shared/src/dialogFactory.ts index 2ad6fdc566..bfdae3a22c 100644 --- a/Composer/packages/lib/shared/src/dialogFactory.ts +++ b/Composer/packages/lib/shared/src/dialogFactory.ts @@ -5,7 +5,8 @@ import nanoid from 'nanoid/generate'; import { appschema } from './appschema'; import { copyAdaptiveAction } from './copyUtils'; -import { deleteAdaptiveAction } from './deleteUtils'; +import { deleteAdaptiveAction, deleteAdaptiveActionList } from './deleteUtils'; +import { MicrosoftIDialog } from './types'; interface DesignerAttributes { name: string; @@ -106,10 +107,14 @@ export const deepCopyAction = async (data, lgApi) => { return await copyAdaptiveAction(data, { lgApi, updateDesigner }); }; -export const deleteAction = (data, deleteLgTemplates: (templates: string[]) => any) => { +export const deleteAction = (data: MicrosoftIDialog, deleteLgTemplates: (templates: string[]) => any) => { return deleteAdaptiveAction(data, deleteLgTemplates); }; +export const deleteActions = (inputs: MicrosoftIDialog[], deleteLgTemplates: (templates: string[]) => any) => { + return deleteAdaptiveActionList(inputs, deleteLgTemplates); +}; + export const seedNewDialog = ( $type: string, designerAttributes: Partial = {}, From cf43cb3f7993588ad61b6dd37e664c4db807eebb Mon Sep 17 00:00:00 2001 From: zeye Date: Thu, 14 Nov 2019 15:12:53 +0800 Subject: [PATCH 04/11] apply new destructor to ObiEditor --- .../visual-designer/src/editors/ObiEditor.tsx | 36 +++---------------- .../visual-designer/src/utils/jsonTracker.ts | 19 ++++------ 2 files changed, 11 insertions(+), 44 deletions(-) diff --git a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx index 806fbaf985..0fe3f71dfb 100644 --- a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx +++ b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx @@ -68,37 +68,7 @@ export const ObiEditor: FC = ({ break; case NodeEventTypes.Delete: handler = e => { - // TODO: move the shared logic into shared lib as a generic destruction process - const findLgTemplates = (value: any): string[] => { - const targetNames = ['prompt', 'unrecognizedPrompt', 'defaultValueResponse', 'invalidPrompt', 'activity']; - const targets: string[] = []; - - targetNames.forEach(name => { - if (has(value, name)) { - targets.push(get(value, name)); - } - }); - - const templates: string[] = []; - targets.forEach(target => { - // only match auto generated lg temapte name - const reg = /\[(bfd((?:activity)|(?:prompt)|(?:unrecognizedPrompt)|(?:defaultValueResponse)|(?:invalidPrompt))-\d{6})\]/g; - let matchResult; - while ((matchResult = reg.exec(target)) !== null) { - const templateName = matchResult[1]; - templates.push(templateName); - } - }); - - return templates; - }; - - const cleanLgTemplate = async (removedData: any): Promise => { - const templateNames: string[] = findLgTemplates(removedData); - const lgFileId = 'common'; - await removeLgTemplates(lgFileId, templateNames); - }; - onChange(deleteNode(data, e.id, cleanLgTemplate)); + onChange(deleteNode(data, e.id, (lgTemplates: string[]) => removeLgTemplates('common', lgTemplates))); onFocusSteps([]); }; break; @@ -140,7 +110,9 @@ export const ObiEditor: FC = ({ break; case NodeEventTypes.DeleteSelection: handler = e => { - const dialog = deleteNodes(data, e.actionIds); + const dialog = deleteNodes(data, e.actionIds, (lgTemplates: string[]) => + removeLgTemplates('common', lgTemplates) + ); onChange(dialog); onFocusSteps([]); }; diff --git a/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts b/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts index f8808f1c73..bf38d538c0 100644 --- a/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts +++ b/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { cloneDeep, get, set } from 'lodash'; -import { seedNewDialog, deepCopyAction } from '@bfc/shared'; +import { seedNewDialog, deepCopyAction, deleteAction, deleteActions } from '@bfc/shared'; import { getFriendlyName } from '../components/nodes/utils'; @@ -96,7 +96,7 @@ export function queryNode(inputDialog, path) { return target.currentData; } -export function deleteNode(inputDialog, path, callbackOnRemovedData?: (removedData: any) => any) { +export function deleteNode(inputDialog, path, deleteLgTemplates: (lgTempaltes: string[]) => any) { const dialog = cloneDeep(inputDialog); const target = locateNode(dialog, path); if (!target) return dialog; @@ -112,15 +112,12 @@ export function deleteNode(inputDialog, path, callbackOnRemovedData?: (removedDa delete parentData[currentKey]; } - // invoke callback handler - if (callbackOnRemovedData && typeof callbackOnRemovedData === 'function') { - callbackOnRemovedData(deletedData); - } + deleteAction(deletedData, deleteLgTemplates); return dialog; } -export function deleteNodes(inputDialog, nodeIds: string[], callbackOnRemovedData?: (removedData: any) => any) { +export function deleteNodes(inputDialog, nodeIds: string[], deleteLgTemplates: (lgTempaltes: string[]) => any) { const dialog = cloneDeep(inputDialog); const nodeLocations = nodeIds.map(id => locateNode(dialog, id)); @@ -146,10 +143,7 @@ export function deleteNodes(inputDialog, nodeIds: string[], callbackOnRemovedDat } }); - // invoke callback handler - if (callbackOnRemovedData && typeof callbackOnRemovedData === 'function') { - deletedNodes.forEach(x => callbackOnRemovedData(x)); - } + deleteActions(deletedNodes, deleteLgTemplates); return dialog; } @@ -178,7 +172,8 @@ export function copyNodes(inputDialog, nodeIds: string[]): any[] { export function cutNodes(inputDialog, nodeIds: string[]) { const nodesData = copyNodes(inputDialog, nodeIds); - const newDialog = deleteNodes(inputDialog, nodeIds); + // TODO: revisit how does cut/paste work with undo/redo #1212 + const newDialog = deleteNodes(inputDialog, nodeIds, () => {}); return { dialog: newDialog, cutData: nodesData }; } From e4d7a3a3add4747746448b020128907df81baad1 Mon Sep 17 00:00:00 2001 From: zeye Date: Thu, 14 Nov 2019 15:41:07 +0800 Subject: [PATCH 05/11] fix walkAdaptiveAction && add UT --- .../deleteUtils/walkAdaptiveAction.test.ts | 17 +++++++++++++++++ .../src/deleteUtils/walkAdaptiveAction.ts | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveAction.test.ts diff --git a/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveAction.test.ts b/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveAction.test.ts new file mode 100644 index 0000000000..484f33c4df --- /dev/null +++ b/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveAction.test.ts @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { walkAdaptiveAction } from '../../src/deleteUtils/walkAdaptiveAction'; + +describe('walkAdaptiveAction', () => { + it('can walk single action', () => { + const action = { + $type: 'Microsoft.SendActivity', + prompt: 'hello', + }; + const spy = jest.fn(); + walkAdaptiveAction(action, x => spy(x)); + + expect(spy).toBeCalledWith(action); + }); +}); diff --git a/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveAction.ts b/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveAction.ts index 474eee51d6..cd032f7fde 100644 --- a/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveAction.ts +++ b/Composer/packages/lib/shared/src/deleteUtils/walkAdaptiveAction.ts @@ -28,6 +28,8 @@ export const walkAdaptiveAction = (input, visit: (action: MicrosoftIDialog) => v if (WalkerMap[input.$type]) { WalkerMap[input.$type](input, visit); + } else { + visit(input); } return; }; From ccc94ab78a419fe3adacd81163d0019fdf797ae4 Mon Sep 17 00:00:00 2001 From: zeye Date: Thu, 14 Nov 2019 15:44:56 +0800 Subject: [PATCH 06/11] add test for walkAdaptiveActionList --- .../deleteUtils/walkAdaptiveAction.test.ts | 3 ++- .../walkAdaptiveActionList.test.ts | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveActionList.test.ts diff --git a/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveAction.test.ts b/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveAction.test.ts index 484f33c4df..f293d13356 100644 --- a/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveAction.test.ts +++ b/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveAction.test.ts @@ -7,11 +7,12 @@ describe('walkAdaptiveAction', () => { it('can walk single action', () => { const action = { $type: 'Microsoft.SendActivity', - prompt: 'hello', + activity: 'hello', }; const spy = jest.fn(); walkAdaptiveAction(action, x => spy(x)); + expect(spy).toBeCalledTimes(1); expect(spy).toBeCalledWith(action); }); }); diff --git a/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveActionList.test.ts b/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveActionList.test.ts new file mode 100644 index 0000000000..6852722d4d --- /dev/null +++ b/Composer/packages/lib/shared/__tests__/deleteUtils/walkAdaptiveActionList.test.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License + +import { walkAdaptiveActionList } from '../../src/deleteUtils/walkAdaptiveActionList'; + +describe('walkAdaptiveAction', () => { + it('can walk action list', () => { + const actions = [ + { + $type: 'Microsoft.SendActivity', + prompt: 'hello', + }, + { + $type: 'Microsoft.ChoiceInput', + prompt: 'hello', + }, + ]; + const spy = jest.fn(); + walkAdaptiveActionList(actions, x => spy(x)); + + expect(spy).toBeCalledTimes(2); + expect(spy).toHaveBeenNthCalledWith(1, actions[0]); + expect(spy).toHaveBeenNthCalledWith(2, actions[1]); + }); +}); From b06bb7460c6841673ad801bef610047a85ea30a9 Mon Sep 17 00:00:00 2001 From: zeye Date: Thu, 14 Nov 2019 15:58:07 +0800 Subject: [PATCH 07/11] normalize lg templates before removing --- .../visual-designer/src/editors/ObiEditor.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx index 0fe3f71dfb..7e1b1d9cd1 100644 --- a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx +++ b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx @@ -5,7 +5,6 @@ import { jsx } from '@emotion/core'; import { useContext, FC, useEffect, useState, useRef } from 'react'; import { MarqueeSelection, Selection } from 'office-ui-fabric-react/lib/MarqueeSelection'; -import { has, get } from 'lodash'; import { NodeEventTypes } from '../constants/NodeEventTypes'; import { KeyboardCommandTypes, KeyboardPrimaryTypes } from '../constants/KeyboardCommandTypes'; @@ -47,6 +46,19 @@ export const ObiEditor: FC = ({ NodeRendererContext ); const lgApi = { getLgTemplates, removeLgTemplates, updateLgTemplate }; + + const deleteLgTemplates = (lgTemplates: string[]) => { + const lgPattern = /\[(bfd\w+-\d+)\]/; + const normalizedLgTemplates = lgTemplates + .map(x => { + const matches = lgPattern.exec(x); + if (matches && matches.length === 2) return matches[1]; + return ''; + }) + .filter(x => !!x); + return removeLgTemplates('common', normalizedLgTemplates); + }; + const dispatchEvent = (eventName: NodeEventTypes, eventData: any): any => { let handler; switch (eventName) { @@ -68,7 +80,7 @@ export const ObiEditor: FC = ({ break; case NodeEventTypes.Delete: handler = e => { - onChange(deleteNode(data, e.id, (lgTemplates: string[]) => removeLgTemplates('common', lgTemplates))); + onChange(deleteNode(data, e.id, deleteLgTemplates)); onFocusSteps([]); }; break; @@ -110,9 +122,7 @@ export const ObiEditor: FC = ({ break; case NodeEventTypes.DeleteSelection: handler = e => { - const dialog = deleteNodes(data, e.actionIds, (lgTemplates: string[]) => - removeLgTemplates('common', lgTemplates) - ); + const dialog = deleteNodes(data, e.actionIds, deleteLgTemplates); onChange(dialog); onFocusSteps([]); }; From d20b7ebe035e1291a626e8ac9b8971eb50bd10be Mon Sep 17 00:00:00 2001 From: Chris Whitten Date: Fri, 15 Nov 2019 15:25:33 -0800 Subject: [PATCH 08/11] Update jsonTracker.ts --- .../packages/extensions/visual-designer/src/utils/jsonTracker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts b/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts index efe0424fcb..c47e46d0d2 100644 --- a/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts +++ b/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts @@ -5,7 +5,6 @@ import { seedNewDialog, deepCopyAction, deleteAction, deleteActions } from '@bfc import cloneDeep from 'lodash/cloneDeep'; import get from 'lodash/get'; import set from 'lodash/set'; -import { seedNewDialog, deepCopyAction } from '@bfc/shared'; import { getFriendlyName } from '../components/nodes/utils'; From 70224519c107931cb72c746808f7254c139d06cf Mon Sep 17 00:00:00 2001 From: zeye Date: Mon, 18 Nov 2019 16:55:57 +0800 Subject: [PATCH 09/11] move deleteAction bindings outside jsonTracker to not break origin tests --- .../visual-designer/src/editors/ObiEditor.tsx | 5 +++-- .../visual-designer/src/utils/jsonTracker.ts | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx index 7e1b1d9cd1..194b514d4b 100644 --- a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx +++ b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx @@ -5,6 +5,7 @@ import { jsx } from '@emotion/core'; import { useContext, FC, useEffect, useState, useRef } from 'react'; import { MarqueeSelection, Selection } from 'office-ui-fabric-react/lib/MarqueeSelection'; +import { deleteAction, deleteActions } from '@bfc/shared'; import { NodeEventTypes } from '../constants/NodeEventTypes'; import { KeyboardCommandTypes, KeyboardPrimaryTypes } from '../constants/KeyboardCommandTypes'; @@ -80,7 +81,7 @@ export const ObiEditor: FC = ({ break; case NodeEventTypes.Delete: handler = e => { - onChange(deleteNode(data, e.id, deleteLgTemplates)); + onChange(deleteNode(data, e.id, node => deleteAction(node, deleteLgTemplates))); onFocusSteps([]); }; break; @@ -122,7 +123,7 @@ export const ObiEditor: FC = ({ break; case NodeEventTypes.DeleteSelection: handler = e => { - const dialog = deleteNodes(data, e.actionIds, deleteLgTemplates); + const dialog = deleteNodes(data, e.actionIds, nodes => deleteActions(nodes, deleteLgTemplates)); onChange(dialog); onFocusSteps([]); }; diff --git a/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts b/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts index bf38d538c0..f8808f1c73 100644 --- a/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts +++ b/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import { cloneDeep, get, set } from 'lodash'; -import { seedNewDialog, deepCopyAction, deleteAction, deleteActions } from '@bfc/shared'; +import { seedNewDialog, deepCopyAction } from '@bfc/shared'; import { getFriendlyName } from '../components/nodes/utils'; @@ -96,7 +96,7 @@ export function queryNode(inputDialog, path) { return target.currentData; } -export function deleteNode(inputDialog, path, deleteLgTemplates: (lgTempaltes: string[]) => any) { +export function deleteNode(inputDialog, path, callbackOnRemovedData?: (removedData: any) => any) { const dialog = cloneDeep(inputDialog); const target = locateNode(dialog, path); if (!target) return dialog; @@ -112,12 +112,15 @@ export function deleteNode(inputDialog, path, deleteLgTemplates: (lgTempaltes: s delete parentData[currentKey]; } - deleteAction(deletedData, deleteLgTemplates); + // invoke callback handler + if (callbackOnRemovedData && typeof callbackOnRemovedData === 'function') { + callbackOnRemovedData(deletedData); + } return dialog; } -export function deleteNodes(inputDialog, nodeIds: string[], deleteLgTemplates: (lgTempaltes: string[]) => any) { +export function deleteNodes(inputDialog, nodeIds: string[], callbackOnRemovedData?: (removedData: any) => any) { const dialog = cloneDeep(inputDialog); const nodeLocations = nodeIds.map(id => locateNode(dialog, id)); @@ -143,7 +146,10 @@ export function deleteNodes(inputDialog, nodeIds: string[], deleteLgTemplates: ( } }); - deleteActions(deletedNodes, deleteLgTemplates); + // invoke callback handler + if (callbackOnRemovedData && typeof callbackOnRemovedData === 'function') { + deletedNodes.forEach(x => callbackOnRemovedData(x)); + } return dialog; } @@ -172,8 +178,7 @@ export function copyNodes(inputDialog, nodeIds: string[]): any[] { export function cutNodes(inputDialog, nodeIds: string[]) { const nodesData = copyNodes(inputDialog, nodeIds); - // TODO: revisit how does cut/paste work with undo/redo #1212 - const newDialog = deleteNodes(inputDialog, nodeIds, () => {}); + const newDialog = deleteNodes(inputDialog, nodeIds); return { dialog: newDialog, cutData: nodesData }; } From 52f224f2f18373e6cbbe8c08a1702a4ebf75ad9f Mon Sep 17 00:00:00 2001 From: zeye Date: Mon, 18 Nov 2019 17:02:44 +0800 Subject: [PATCH 10/11] avoid unnecessary change --- .../extensions/visual-designer/src/utils/jsonTracker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts b/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts index 837e44c706..bffe04aeea 100644 --- a/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts +++ b/Composer/packages/extensions/visual-designer/src/utils/jsonTracker.ts @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { seedNewDialog, deepCopyAction } from '@bfc/shared'; import cloneDeep from 'lodash/cloneDeep'; import get from 'lodash/get'; import set from 'lodash/set'; +import { seedNewDialog, deepCopyAction } from '@bfc/shared'; import { getFriendlyName } from '../components/nodes/utils'; From 71c1a1e59556456a1e683403bcff6412708d042c Mon Sep 17 00:00:00 2001 From: zeye Date: Tue, 19 Nov 2019 14:44:45 +0800 Subject: [PATCH 11/11] remove unused code --- .../extensions/visual-designer/src/editors/ObiEditor.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx index 6074435215..d5dad318a0 100644 --- a/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx +++ b/Composer/packages/extensions/visual-designer/src/editors/ObiEditor.tsx @@ -48,8 +48,6 @@ export const ObiEditor: FC = ({ NodeRendererContext ); - const lgApi = { getLgTemplates, removeLgTemplates, updateLgTemplate }; - const deleteLgTemplates = (lgTemplates: string[]) => { const lgPattern = /\[(bfd\w+-\d+)\]/; const normalizedLgTemplates = lgTemplates