From 6f6d10ae25684e1abb024b62927a9e42584e98fe Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Wed, 10 Mar 2021 15:33:18 +0800 Subject: [PATCH 1/3] flatten project tree in all up view --- .../components/ProjectTree/ProjectTree.tsx | 154 +++++++++--------- .../language-understanding/table-view.tsx | 10 +- .../recoilModel/selectors/dialogImports.ts | 3 +- .../src/recoilModel/selectors/project.ts | 32 +++- 4 files changed, 105 insertions(+), 94 deletions(-) diff --git a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx index 58b3c9d5b9..452ca994a7 100644 --- a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx +++ b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx @@ -185,12 +185,7 @@ export const ProjectTree: React.FC = ({ const addMainDialogRef = useCallback((mainDialog) => onboardingAddCoachMarkRef({ mainDialog }), []); const rootProjectId = useRecoilValue(rootBotProjectIdSelector); - const selectorOptions = { - showLgImports: options.showLgImports ?? false, - showLuImports: options.showLuImports ?? false, - }; - const projectCollection: TreeDataPerProject[] = useRecoilValue(projectTreeSelectorFamily(selectorOptions)); - + const projectCollection: TreeDataPerProject[] = useRecoilValue(projectTreeSelectorFamily); const jsonSchemaFilesByProjectId = useRecoilValue(jsonSchemaFilesByProjectIdSelector); // TODO Refactor to make sure tree is not generated until a new trigger/dialog is added. #5462 @@ -504,28 +499,20 @@ export const ProjectTree: React.FC = ({ : renderTriggerList(dialog.triggers, dialog, projectId, dialogLink, 1); }; - const renderLgImport = ( - item: LanguageFileImport, - dialog: DialogInfo, - projectId: string, - dialogLink: TreeLink - ): React.ReactNode => { + const renderLgImport = (item: LanguageFileImport, projectId: string): React.ReactNode => { const link: TreeLink = { projectId: rootProjectId, skillId: projectId === rootProjectId ? undefined : projectId, - dialogId: dialog.id, lgFileId: item.id, displayName: item.displayName ?? item.id, diagnostics: [], isRoot: false, - parentLink: dialogLink, isRemote: false, }; return ( = ({ ); }; - const renderLgImports = (dialog: DialogInfo, projectId: string, dialogLink: TreeLink) => { + const renderLgImports = (dialog: DialogInfo, projectId: string) => { return lgImportsByProjectByDialog[projectId][dialog.id] .filter((lgImport) => filterMatch(dialog.displayName) || filterMatch(lgImport.displayName)) .map((lgImport) => { - return renderLgImport(lgImport, dialog, projectId, dialogLink); + return renderLgImport(lgImport, projectId); }); }; - const renderLuImport = ( - item: LanguageFileImport, - dialog: DialogInfo, - projectId: string, - dialogLink: TreeLink - ): React.ReactNode => { + const renderLuImport = (item: LanguageFileImport, projectId: string): React.ReactNode => { const link: TreeLink = { projectId: rootProjectId, skillId: projectId === rootProjectId ? undefined : projectId, - dialogId: dialog.id, luFileId: item.id, displayName: item.displayName ?? item.id, diagnostics: [], isRoot: false, - parentLink: dialogLink, isRemote: false, }; return ( = ({ ); }; - const renderLuImports = (dialog: DialogInfo, projectId: string, dialogLink: TreeLink) => { + const renderLuImports = (dialog: DialogInfo, projectId: string) => { return luImportsByProjectByDialog[projectId][dialog.id] .filter((luImport) => filterMatch(dialog.displayName) || filterMatch(luImport.displayName)) .map((luImport) => { - return renderLuImport(luImport, dialog, projectId, dialogLink); + return renderLuImport(luImport, projectId); }); }; const createDetailsTree = (bot: TreeDataPerProject, startDepth: number) => { - const { projectId } = bot; + const { projectId, lgImportsList, luImportsList } = bot; const dialogs = bot.sortedDialogs; const filteredDialogs = @@ -605,56 +584,73 @@ export const ProjectTree: React.FC = ({ ); const commonLink = options.showCommonLinks ? [renderCommonDialogHeader(projectId, 1)] : []; - if (options.showTriggers || options.showLgImports || options.showLuImports) { - return [ - ...commonLink, - ...filteredDialogs.map((dialog: DialogInfo) => { - const { summaryElement, dialogLink } = renderDialogHeader(projectId, dialog, 0, bot.isPvaSchema); - const key = 'dialog-' + dialog.id; - let lgImports, luImports; - if (options.showLgImports) { - lgImports = renderLgImports(dialog, projectId, dialogLink); - } - - if (options.showLuImports) { - luImports = renderLuImports(dialog, projectId, dialogLink); - } - - const showExpanded = - options.showTriggers || - (options.showLgImports && lgImports.length > 0) || - (options.showLuImports && luImports.length > 0); - if (showExpanded) { - return ( - setPageElement(key, newState)} - > -
- {options.showTriggers && renderDialogTriggers(dialog, projectId, startDepth + 1, dialogLink)} - {options.showLgImports && lgImports} - {options.showLuImports && luImports} -
-
- ); - } else { - return renderDialogHeader(projectId, dialog, 1, bot.isPvaSchema).summaryElement; - } - }), - ]; - } else { - return [ - ...commonLink, - ...filteredDialogs.map( - (dialog: DialogInfo) => renderDialogHeader(projectId, dialog, 1, bot.isPvaSchema).summaryElement - ), - ]; - } + const importedLgLinks = options.showLgImports ? lgImportsList.map((file) => renderLgImport(file, projectId)) : []; + const importedLuLinks = options.showLuImports ? luImportsList.map((file) => renderLuImport(file, projectId)) : []; + + return [ + ...commonLink, + ...importedLgLinks, + ...importedLuLinks, + ...filteredDialogs.map((dialog: DialogInfo) => { + const { summaryElement, dialogLink } = renderDialogHeader(projectId, dialog, 0, bot.isPvaSchema); + const key = 'dialog-' + dialog.id; + + let lgImports, luImports; + if (options.showLgImports) { + lgImports = renderLgImports(dialog, projectId); + } + + if (options.showLuImports) { + luImports = renderLuImports(dialog, projectId); + } + + if (options.showTriggers) { + return ( + setPageElement(key, newState)} + > +
{renderDialogTriggers(dialog, projectId, startDepth + 1, dialogLink)}
+
+ ); + } else if (options.showLgImports && lgImports.length > 0 && dialog.isFormDialog) { + return ( + setPageElement(key, newState)} + > +
{lgImports}
+
+ ); + } else if (options.showLuImports && luImports.length > 0 && dialog.isFormDialog) { + return ( + setPageElement(key, newState)} + > +
{luImports}
+
+ ); + } else { + return renderDialogHeader(projectId, dialog, 1, bot.isPvaSchema).summaryElement; + } + }), + ]; }; const createBotSubtree = (bot: TreeDataPerProject) => { diff --git a/Composer/packages/client/src/pages/language-understanding/table-view.tsx b/Composer/packages/client/src/pages/language-understanding/table-view.tsx index 1317f1e7f4..13569b24c3 100644 --- a/Composer/packages/client/src/pages/language-understanding/table-view.tsx +++ b/Composer/packages/client/src/pages/language-understanding/table-view.tsx @@ -107,9 +107,7 @@ const TableView: React.FC = (props) => { return result.concat(items); }, []); - if (!activeDialog) { - setIntents(allIntents); - } else if (luFileId && file) { + if (luFileId && file) { const luIntents: Intent[] = []; get(file, 'intents', []).forEach(({ Name: name, Body: phrases }) => { const state = getIntentState(file); @@ -118,14 +116,16 @@ const TableView: React.FC = (props) => { phrases, fileId: file.id, dialogId: activeDialog?.id || '', - used: activeDialog?.referredLuIntents.some((lu) => lu.name === name), // used by it's dialog or not + used: !!activeDialog?.referredLuIntents.some((lu) => lu.name === name), // used by it's dialog or not state, }); }); setIntents(luIntents); - } else { + } else if (activeDialog) { const dialogIntents = allIntents.filter((t) => t.dialogId === activeDialog.id); setIntents(dialogIntents); + } else { + setIntents(allIntents); } }, [luFiles, activeDialog, actualProjectId, luFileId]); diff --git a/Composer/packages/client/src/recoilModel/selectors/dialogImports.ts b/Composer/packages/client/src/recoilModel/selectors/dialogImports.ts index d47366fd55..f0cc3b176e 100644 --- a/Composer/packages/client/src/recoilModel/selectors/dialogImports.ts +++ b/Composer/packages/client/src/recoilModel/selectors/dialogImports.ts @@ -37,8 +37,9 @@ export const getLanguageFileImports = ( } const currentImports = file.imports.map((item) => { const importedFile = getFile(getBaseName(item.id)); + const displayName = item.id.substring(0, item.id.indexOf('.')); return { - displayName: item.description, + displayName, importPath: item.path, id: importedFile ? importedFile.id : '', }; diff --git a/Composer/packages/client/src/recoilModel/selectors/project.ts b/Composer/packages/client/src/recoilModel/selectors/project.ts index b44fbc019d..23e4b91646 100644 --- a/Composer/packages/client/src/recoilModel/selectors/project.ts +++ b/Composer/packages/client/src/recoilModel/selectors/project.ts @@ -3,6 +3,7 @@ import { BotIndexer } from '@bfc/indexers'; import { BotAssets, checkForPVASchema, DialogInfo, FormDialogSchema, JsonSchemaFile } from '@bfc/shared'; +import { uniqBy } from 'lodash'; import isEmpty from 'lodash/isEmpty'; import { selector, selectorFamily } from 'recoil'; @@ -48,6 +49,8 @@ export type TreeDataPerProject = { sortedDialogs: DialogInfo[]; lgImports: Record; luImports: Record; + lgImportsList: LanguageFileImport[]; // all imported file exclude form diloag + luImportsList: LanguageFileImport[]; name: string; isPvaSchema: boolean; formDialogSchemas: FormDialogSchema[]; @@ -271,12 +274,9 @@ export const projectDialogsMapSelector = selector<{ [key: string]: DialogInfo[] }, }); -export const projectTreeSelectorFamily = selectorFamily< - TreeDataPerProject[], - { showLgImports: boolean; showLuImports: boolean } ->({ +export const projectTreeSelectorFamily = selector({ key: 'projectTreeSelectorFamily', - get: (options) => ({ get }) => { + get: ({ get }) => { const projectIds = get(botProjectIdsState); return projectIds.map((projectId: string) => { const { isRemote, isRootBot } = get(projectMetaDataState(projectId)); @@ -293,17 +293,26 @@ export const projectTreeSelectorFamily = selectorFamily< const botError = get(botErrorState(projectId)); const name = get(botDisplayNameState(projectId)); + const dialogIds = get(dialogIdsState(projectId)); const lgImports: Record = {}; const luImports: Record = {}; + // flatten imported file list + let lgImportsList: LanguageFileImport[] = []; + let luImportsList: LanguageFileImport[] = []; + dialogs.forEach((d) => { - if (options.showLgImports) { - lgImports[d.id] = get(lgImportsSelectorFamily({ projectId, dialogId: d.id })) ?? []; + const currentLgImports = get(lgImportsSelectorFamily({ projectId, dialogId: d.id })) ?? []; + lgImports[d.id] = currentLgImports; + if (!d.isFormDialog) { + lgImportsList.push(...currentLgImports); } - if (options.showLuImports) { - luImports[d.id] = get(luImportsSelectorFamily({ projectId, dialogId: d.id })) ?? []; + const currentLuImports = get(luImportsSelectorFamily({ projectId, dialogId: d.id })) ?? []; + luImports[d.id] = currentLuImports; + if (!d.isFormDialog) { + luImportsList.push(...currentLuImports); } }); @@ -311,6 +320,9 @@ export const projectTreeSelectorFamily = selectorFamily< const isPvaSchema = schemas && checkForPVASchema(schemas.sdk); const formDialogSchemas = get(formDialogSchemasSelectorFamily(projectId)); + lgImportsList = uniqBy(lgImportsList, 'id').filter((item) => !dialogIds.includes(item.displayName) && item.id); + luImportsList = uniqBy(luImportsList, 'id').filter((item) => !dialogIds.includes(item.displayName) && item.id); + return { projectId, isRemote, @@ -318,6 +330,8 @@ export const projectTreeSelectorFamily = selectorFamily< sortedDialogs, luImports, lgImports, + lgImportsList, + luImportsList, name, isPvaSchema, formDialogSchemas, From eb4d1e7ab17f12f95858382534dd33aa605b3e15 Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Wed, 10 Mar 2021 15:55:02 +0800 Subject: [PATCH 2/3] item active --- .../packages/client/src/components/ProjectTree/ProjectTree.tsx | 2 ++ Composer/packages/client/src/recoilModel/selectors/project.ts | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx index 452ca994a7..c74485b4d3 100644 --- a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx +++ b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx @@ -504,6 +504,7 @@ export const ProjectTree: React.FC = ({ projectId: rootProjectId, skillId: projectId === rootProjectId ? undefined : projectId, lgFileId: item.id, + dialogId: 'all', displayName: item.displayName ?? item.id, diagnostics: [], isRoot: false, @@ -541,6 +542,7 @@ export const ProjectTree: React.FC = ({ skillId: projectId === rootProjectId ? undefined : projectId, luFileId: item.id, displayName: item.displayName ?? item.id, + dialogId: 'all', diagnostics: [], isRoot: false, isRemote: false, diff --git a/Composer/packages/client/src/recoilModel/selectors/project.ts b/Composer/packages/client/src/recoilModel/selectors/project.ts index 23e4b91646..e049c5d721 100644 --- a/Composer/packages/client/src/recoilModel/selectors/project.ts +++ b/Composer/packages/client/src/recoilModel/selectors/project.ts @@ -3,8 +3,8 @@ import { BotIndexer } from '@bfc/indexers'; import { BotAssets, checkForPVASchema, DialogInfo, FormDialogSchema, JsonSchemaFile } from '@bfc/shared'; -import { uniqBy } from 'lodash'; import isEmpty from 'lodash/isEmpty'; +import uniqBy from 'lodash/uniqBy'; import { selector, selectorFamily } from 'recoil'; import { LanguageFileImport } from '../../../../types/src'; From bc56506a8c56ea64c26a154df4097c7313b9f85e Mon Sep 17 00:00:00 2001 From: zhixzhan Date: Wed, 10 Mar 2021 16:38:36 +0800 Subject: [PATCH 3/3] update tests --- .../recoilModel/selectors/__test__/dialogImports.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Composer/packages/client/src/recoilModel/selectors/__test__/dialogImports.test.tsx b/Composer/packages/client/src/recoilModel/selectors/__test__/dialogImports.test.tsx index 1be280b137..8aa7053f86 100644 --- a/Composer/packages/client/src/recoilModel/selectors/__test__/dialogImports.test.tsx +++ b/Composer/packages/client/src/recoilModel/selectors/__test__/dialogImports.test.tsx @@ -51,22 +51,22 @@ describe('dialogImports selectors', () => { const fileImports = getLanguageFileImports('name1', getFile); expect(fileImports).toEqual([ { - displayName: 'display-name2.lg', + displayName: 'name2', id: 'name2', importPath: '../files/name2.lg', }, { - displayName: 'display-name3.lg', + displayName: 'name3', id: 'name3', importPath: '../files/name3.lg', }, { - displayName: 'display-name4.lg', + displayName: 'name4', id: 'name4', importPath: '../files/name4.lg', }, { - displayName: 'display-name5-entity.lg', + displayName: 'name5-entity', id: 'name5-entity', importPath: '../files/name5-entity.lg', },