Skip to content

Commit

Permalink
fix: switching in project tree is lagging (#5524)
Browse files Browse the repository at this point in the history
* fix: project tree switch item lagging

* fix tests

Co-authored-by: Dong Lei <donglei@microsoft.com>
  • Loading branch information
lei9444 and boydc2014 committed Jan 14, 2021
1 parent dad8dcf commit 17d705a
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 165 deletions.
8 changes: 6 additions & 2 deletions Composer/packages/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import { useInitializeLogger } from './telemetry/useInitializeLogger';

initializeIcons(undefined, { disableWarnings: true });

const Logger = () => {
useInitializeLogger();
return null;
};

export const App: React.FC = () => {
const { appLocale } = useRecoilValue(userSettingsState);
const { fetchExtensions, fetchFeatureFlags } = useRecoilValue(dispatcherState);
Expand All @@ -28,10 +33,9 @@ export const App: React.FC = () => {
fetchFeatureFlags();
}, []);

useInitializeLogger();

return (
<Fragment key={appLocale}>
<Logger />
<Announcement />
<Header />
<MainContainer />
Expand Down
161 changes: 18 additions & 143 deletions Composer/packages/client/src/pages/design/DesignPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
/** @jsx jsx */
import { jsx } from '@emotion/core';
import React, { Suspense, useEffect, useMemo, useState, useCallback } from 'react';
import { Breadcrumb, IBreadcrumbItem } from 'office-ui-fabric-react/lib/Breadcrumb';
import formatMessage from 'format-message';
import { globalHistory, RouteComponentProps } from '@reach/router';
import get from 'lodash/get';
import { DialogInfo, PromptTab, getEditorAPI, registerEditorAPI, Diagnostic } from '@bfc/shared';
import { ActionButton } from 'office-ui-fabric-react/lib/Button';
import { JsonEditor } from '@bfc/code-editor';
import { EditorExtension, PluginConfig } from '@bfc/extension-client';
import { useRecoilValue, useRecoilState } from 'recoil';
Expand All @@ -24,7 +22,6 @@ import { dialogStyle } from '../../components/Modal/dialogStyle';
import { ProjectTree, TreeLink } from '../../components/ProjectTree/ProjectTree';
import { Toolbar, IToolbarItem } from '../../components/Toolbar';
import { createDiagnosticsPageUrl, getFocusPath, navigateTo, createBotSettingUrl } from '../../utils/navigation';
import { getFriendlyName } from '../../utils/dialogUtil';
import { useShell } from '../../shell';
import plugins, { mergePluginConfigs } from '../../plugins';
import { useElectronFeatures } from '../../hooks/useElectronFeatures';
Expand All @@ -45,6 +42,7 @@ import {
SkillInfo,
projectMetaDataState,
displaySkillManifestState,
designPageLocationState,
} from '../../recoilModel';
import { CreateQnAModal } from '../../components/QnA';
import { triggerNotSupported } from '../../utils/dialogValidator';
Expand All @@ -61,25 +59,11 @@ import { renderThinSplitter } from '../../components/Split/ThinSplitter';

import CreationModal from './creationModal';
import { WarningMessage } from './WarningMessage';
import {
breadcrumbClass,
contentWrapper,
deleteDialogContent,
editorContainer,
editorWrapper,
pageRoot,
visualPanel,
} from './styles';
import { contentWrapper, deleteDialogContent, editorContainer, editorWrapper, pageRoot, visualPanel } from './styles';
import { VisualEditor } from './VisualEditor';
import { PropertyEditor } from './PropertyEditor';
import { ManifestEditor } from './ManifestEditor';

type BreadcrumbItem = {
key: string;
label: string;
link?: Partial<TreeLink>;
onClick?: () => void;
};
import VisualEditorHeader from './VisualEditorHeader';

const CreateSkillModal = React.lazy(() => import('../../components/CreateSkillModal'));
const RepairSkillModal = React.lazy(() => import('../../components/RepairSkillModal'));
Expand Down Expand Up @@ -113,9 +97,7 @@ function getAllRef(targetId, dialogs) {
const getTabFromFragment = () => {
const tab = window.location.hash.substring(1);

if (Object.values<string>(PromptTab).includes(tab)) {
return tab;
}
return Object.values(PromptTab).find((value) => tab === value);
};

const parseTriggerId = (triggerId: string | undefined): number | undefined => {
Expand Down Expand Up @@ -150,6 +132,7 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
const { undo, redo, commitChanges, clearUndo } = undoFunction;
const [canUndo, canRedo] = useRecoilValue(undoStatusSelectorFamily(skillId ?? projectId));
const { isRemote: isRemoteSkill } = useRecoilValue(projectMetaDataState(skillId ?? projectId));
const designPageLocation = useRecoilValue(designPageLocationState(skillId ?? projectId));

const {
removeDialog,
Expand All @@ -175,10 +158,9 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
createDialogCancel,
} = useRecoilValue(dispatcherState);

const params = new URLSearchParams(location?.search);
const selected = decodeDesignerPathToArrayPath(
dialogs.find((x) => x.id === props.dialogId)?.content,
params.get('selected') || ''
designPageLocation.selected || ''
);

const [triggerModalInfo, setTriggerModalInfo] = useState<undefined | { projectId: string; dialogId: string }>(
Expand All @@ -192,7 +174,6 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
const [brokenSkillRepairCallback, setBrokenSkillRepairCallback] = useState<undefined | (() => void)>(undefined);
const [dialogJsonVisible, setDialogJsonVisibility] = useState(false);
const [warningIsVisible, setWarningIsVisible] = useState(true);
const [breadcrumbs, setBreadcrumbs] = useState<Array<BreadcrumbItem>>([]);

const shell = useShell('DesignPage', skillId ?? rootProjectId);
const shellForFlowEditor = useShell('FlowEditor', skillId ?? rootProjectId);
Expand Down Expand Up @@ -239,48 +220,12 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
const selected = decodeDesignerPathToArrayPath(dialogData, params.get('selected') ?? '');
const focused = decodeDesignerPathToArrayPath(dialogData, params.get('focused') ?? '');

// TODO: get these from a second return value from decodeDesignerPathToArrayPath
const triggerIndex = parseTriggerId(selected);

//make sure focusPath always valid
const focusPath = getFocusPath(selected, focused);
const trigger = triggerIndex != null && dialogData.triggers[triggerIndex];

const breadcrumbArray: Array<BreadcrumbItem> = [];

breadcrumbArray.push({
key: 'dialog-' + props.dialogId,
label: dialogMap[props.dialogId]?.$designer?.name ?? dialogMap[props.dialogId]?.$designer?.$designer?.name,
link: {
projectId: props.projectId,
dialogId: props.dialogId,
},
onClick: () => navTo(skillId ?? null, dialogId),
});
if (triggerIndex != null && trigger != null) {
breadcrumbArray.push({
key: 'trigger-' + triggerIndex,
label: trigger.$designer?.name || getFriendlyName(trigger),
link: {
projectId: props.projectId,
dialogId: props.dialogId,
trigger: triggerIndex,
},
onClick: () => navTo(skillId ?? null, dialogId, `${triggerIndex}`),
});
}

// getDialogData returns whatever's at the end of the path, which could be a trigger or an action
const possibleAction = getDialogData(dialogMap, dialogId, focusPath);

if (params.get('focused') != null) {
// we've linked to an action, so put that in too
breadcrumbArray.push({
key: 'action-' + focusPath,
label: getActionName(possibleAction),
});
}

if (typeof possibleAction === 'undefined') {
const { id: foundId } = dialogs.find(({ id }) => id === dialogId) || dialogs.find(({ isRoot }) => isRoot) || {};
/**
Expand All @@ -301,7 +246,7 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
focused,
promptTab: getTabFromFragment(),
});
setBreadcrumbs(breadcrumbArray);

/* eslint-disable no-underscore-dangle */
// @ts-ignore
globalHistory._onTransitionComplete();
Expand Down Expand Up @@ -330,28 +275,9 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
if (link.botError) {
setBrokenSkillInfo(link);
}
const { skillId, dialogId, trigger, parentLink } = link;
const { skillId, dialogId, trigger } = link;

updateZoomRate({ currentRate: 1 });
const breadcrumbArray: Array<BreadcrumbItem> = [];
if (dialogId != null) {
breadcrumbArray.push({
key: 'dialog-' + parentLink?.dialogId,
label: parentLink?.displayName ?? link.displayName,
link: { projectId, skillId, dialogId },
onClick: () => navTo(skillId ?? projectId, dialogId),
});
}
if (trigger != null) {
breadcrumbArray.push({
key: 'trigger-' + parentLink?.trigger,
label: link.displayName,
link: { projectId, skillId, dialogId, trigger },
onClick: () => selectTo(skillId ?? null, dialogId ?? null, `triggers[${trigger}]`),
});
}

setBreadcrumbs(breadcrumbArray);

if (trigger != null) {
selectTo(skillId ?? null, dialogId ?? null, `triggers[${trigger}]`);
Expand All @@ -376,26 +302,6 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
return mergePluginConfigs({ uiSchema: sdkUISchema }, plugins, { uiSchema: userUISchema });
}, [schemas?.ui?.content, schemas?.uiOverrides?.content]);

const getActionName = (action) => {
const nameFromAction = action?.$designer?.name as string | undefined;
let detectedActionName: string;

if (typeof nameFromAction === 'string') {
detectedActionName = nameFromAction;
} else {
const kind: string = action?.$kind as string;
const actionNameFromSchema = pluginConfig?.uiSchema?.[kind]?.form?.label as string | (() => string) | undefined;
if (typeof actionNameFromSchema === 'string') {
detectedActionName = actionNameFromSchema;
} else if (typeof actionNameFromSchema === 'function') {
detectedActionName = actionNameFromSchema();
} else {
detectedActionName = formatMessage('Unknown');
}
}
return detectedActionName;
};

const { actionSelected, showDisableBtn, showEnableBtn } = useMemo(() => {
const actionSelected = Array.isArray(visualEditorSelection) && visualEditorSelection.length > 0;
if (!actionSelected) {
Expand All @@ -405,13 +311,6 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
const showDisableBtn = selectedActions.some((x) => get(x, 'disabled') !== true);
const showEnableBtn = selectedActions.some((x) => get(x, 'disabled') === true);

if (selectedActions.length === 1 && selectedActions[0] != null) {
const action = selectedActions[0] as any;
const actionName = getActionName(action);

setBreadcrumbs((prev) => [...prev.slice(0, 2), { key: 'action-' + actionName, label: actionName }]);
}

return { actionSelected, showDisableBtn, showEnableBtn };
}, [visualEditorSelection, currentDialog?.content]);

Expand Down Expand Up @@ -559,39 +458,6 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
},
];

const createBreadcrumbItem: (breadcrumb: BreadcrumbItem) => IBreadcrumbItem = (breadcrumb: BreadcrumbItem) => {
return {
key: breadcrumb.key,
text: breadcrumb.label,
onClick: () => breadcrumb.onClick?.(),
};
};

const items = breadcrumbs.map(createBreadcrumbItem);

const breadcrumbItems = (
<div style={{ display: 'flex', justifyContent: 'space-between', height: '65px' }}>
<Breadcrumb
ariaLabel={formatMessage('Navigation Path')}
data-testid="Breadcrumb"
items={items}
maxDisplayedItems={3}
styles={breadcrumbClass}
onReduceData={() => undefined}
/>
<div style={{ padding: '10px' }}>
<ActionButton
onClick={() => {
setDialogJsonVisibility((current) => !current);
TelemetryClient.track('EditModeToggled', { jsonView: dialogJsonVisible });
}}
>
{dialogJsonVisible ? formatMessage('Hide code') : formatMessage('Show code')}
</ActionButton>
</div>
</div>
);

async function handleCreateDialogSubmit(projectId, dialogName, dialogData) {
setDialogModalInfo(undefined);
await createDialog({ id: dialogName, content: dialogData, projectId });
Expand Down Expand Up @@ -757,7 +623,16 @@ const DesignPage: React.FC<RouteComponentProps<{ dialogId: string; projectId: st
renderSplitter={renderThinSplitter}
>
<div aria-label={formatMessage('Authoring canvas')} css={visualPanel} role="region">
{!isRemoteSkill ? breadcrumbItems : null}
<VisualEditorHeader
pluginConfig={pluginConfig}
projectId={skillId ?? projectId}
showCode={dialogJsonVisible}
visible={!isRemoteSkill}
onShowCodeClick={() => {
setDialogJsonVisibility((current) => !current);
TelemetryClient.track('EditModeToggled', { jsonView: dialogJsonVisible });
}}
/>
{dialogJsonVisible ? (
<JsonEditor
key={'dialogjson'}
Expand Down
Loading

0 comments on commit 17d705a

Please sign in to comment.