Skip to content

Commit

Permalink
chore: project tree flatten imports (microsoft#6354)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhixzhan authored Mar 10, 2021
1 parent c1b21b0 commit c504931
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 98 deletions.
156 changes: 77 additions & 79 deletions Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,7 @@ export const ProjectTree: React.FC<Props> = ({
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
Expand Down Expand Up @@ -504,28 +499,21 @@ export const ProjectTree: React.FC<Props> = ({
: 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,
dialogId: 'all',
displayName: item.displayName ?? item.id,
diagnostics: [],
isRoot: false,
parentLink: dialogLink,
isRemote: false,
};

return (
<TreeItem
key={`lg_${item.id}`}
dialogName={dialog.displayName}
extraSpace={INDENT_PER_LEVEL}
icon={icons.DIALOG}
isActive={doesLinkMatch(link, selectedLink)}
Expand All @@ -540,36 +528,29 @@ export const ProjectTree: React.FC<Props> = ({
);
};

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,
dialogId: 'all',
diagnostics: [],
isRoot: false,
parentLink: dialogLink,
isRemote: false,
};

return (
<TreeItem
key={`lu_${item.id}`}
dialogName={dialog.displayName}
extraSpace={INDENT_PER_LEVEL}
icon={icons.DIALOG}
isActive={doesLinkMatch(link, selectedLink)}
Expand All @@ -584,16 +565,16 @@ export const ProjectTree: React.FC<Props> = ({
);
};

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 =
Expand All @@ -605,56 +586,73 @@ export const ProjectTree: React.FC<Props> = ({
);
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 (
<ExpandableNode
key={key}
defaultState={getPageElement(key)}
depth={startDepth}
detailsRef={dialog.isRoot ? addMainDialogRef : undefined}
isActive={doesLinkMatch(dialogLink, selectedLink)}
summary={summaryElement}
onToggle={(newState) => setPageElement(key, newState)}
>
<div>
{options.showTriggers && renderDialogTriggers(dialog, projectId, startDepth + 1, dialogLink)}
{options.showLgImports && lgImports}
{options.showLuImports && luImports}
</div>
</ExpandableNode>
);
} 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 (
<ExpandableNode
key={key}
defaultState={getPageElement(key)}
depth={startDepth}
detailsRef={dialog.isRoot ? addMainDialogRef : undefined}
isActive={doesLinkMatch(dialogLink, selectedLink)}
summary={summaryElement}
onToggle={(newState) => setPageElement(key, newState)}
>
<div>{renderDialogTriggers(dialog, projectId, startDepth + 1, dialogLink)}</div>
</ExpandableNode>
);
} else if (options.showLgImports && lgImports.length > 0 && dialog.isFormDialog) {
return (
<ExpandableNode
key={key}
defaultState={getPageElement(key)}
depth={startDepth}
detailsRef={dialog.isRoot ? addMainDialogRef : undefined}
isActive={doesLinkMatch(dialogLink, selectedLink)}
summary={summaryElement}
onToggle={(newState) => setPageElement(key, newState)}
>
<div>{lgImports}</div>
</ExpandableNode>
);
} else if (options.showLuImports && luImports.length > 0 && dialog.isFormDialog) {
return (
<ExpandableNode
key={key}
defaultState={getPageElement(key)}
depth={startDepth}
detailsRef={dialog.isRoot ? addMainDialogRef : undefined}
isActive={doesLinkMatch(dialogLink, selectedLink)}
summary={summaryElement}
onToggle={(newState) => setPageElement(key, newState)}
>
<div>{luImports}</div>
</ExpandableNode>
);
} else {
return renderDialogHeader(projectId, dialog, 1, bot.isPvaSchema).summaryElement;
}
}),
];
};

const createBotSubtree = (bot: TreeDataPerProject) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ const TableView: React.FC<TableViewProps> = (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);
Expand All @@ -118,14 +116,16 @@ const TableView: React.FC<TableViewProps> = (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]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ export const getLanguageFileImports = <T extends LgFile | LuFile | QnAFile>(
}
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 : '',
};
Expand Down
32 changes: 23 additions & 9 deletions Composer/packages/client/src/recoilModel/selectors/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { BotIndexer } from '@bfc/indexers';
import { BotAssets, checkForPVASchema, DialogInfo, FormDialogSchema, JsonSchemaFile } from '@bfc/shared';
import isEmpty from 'lodash/isEmpty';
import uniqBy from 'lodash/uniqBy';
import { selector, selectorFamily } from 'recoil';

import { LanguageFileImport } from '../../../../types/src';
Expand Down Expand Up @@ -48,6 +49,8 @@ export type TreeDataPerProject = {
sortedDialogs: DialogInfo[];
lgImports: Record<string, LanguageFileImport[]>;
luImports: Record<string, LanguageFileImport[]>;
lgImportsList: LanguageFileImport[]; // all imported file exclude form diloag
luImportsList: LanguageFileImport[];
name: string;
isPvaSchema: boolean;
formDialogSchemas: FormDialogSchema[];
Expand Down Expand Up @@ -271,12 +274,9 @@ export const projectDialogsMapSelector = selector<{ [key: string]: DialogInfo[]
},
});

export const projectTreeSelectorFamily = selectorFamily<
TreeDataPerProject[],
{ showLgImports: boolean; showLuImports: boolean }
>({
export const projectTreeSelectorFamily = selector<TreeDataPerProject[]>({
key: 'projectTreeSelectorFamily',
get: (options) => ({ get }) => {
get: ({ get }) => {
const projectIds = get(botProjectIdsState);
return projectIds.map((projectId: string) => {
const { isRemote, isRootBot } = get(projectMetaDataState(projectId));
Expand All @@ -293,31 +293,45 @@ export const projectTreeSelectorFamily = selectorFamily<

const botError = get(botErrorState(projectId));
const name = get(botDisplayNameState(projectId));
const dialogIds = get(dialogIdsState(projectId));

const lgImports: Record<string, LanguageFileImport[]> = {};
const luImports: Record<string, LanguageFileImport[]> = {};

// 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);
}
});

const schemas = get(schemasState(projectId));
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,
isRootBot,
sortedDialogs,
luImports,
lgImports,
lgImportsList,
luImportsList,
name,
isPvaSchema,
formDialogSchemas,
Expand Down

0 comments on commit c504931

Please sign in to comment.