diff --git a/Composer/packages/client/src/pages/design/PropertyEditor.tsx b/Composer/packages/client/src/pages/design/PropertyEditor.tsx
index 73c40582d1..b3c489d3da 100644
--- a/Composer/packages/client/src/pages/design/PropertyEditor.tsx
+++ b/Composer/packages/client/src/pages/design/PropertyEditor.tsx
@@ -104,7 +104,7 @@ const PropertyEditor: React.FC = () => {
if (!isEqual(dialogData, localData)) {
shellApi.saveData(localData, focusedSteps[0]);
} else {
- shellApi.commitChanges();
+ shellApi.commitChanges?.();
}
}, 300);
diff --git a/Composer/packages/client/src/recoilModel/atoms/botState.ts b/Composer/packages/client/src/recoilModel/atoms/botState.ts
index 09772c3cc2..fc6c316599 100644
--- a/Composer/packages/client/src/recoilModel/atoms/botState.ts
+++ b/Composer/packages/client/src/recoilModel/atoms/botState.ts
@@ -24,7 +24,7 @@ import { BotLoadError, DesignPageLocation } from '../../recoilModel/types';
import FilePersistence from '../persistence/FilePersistence';
import { BotStatus } from './../../constants';
-import { BreadcrumbItem, PublishType } from './../../recoilModel/types';
+import { PublishType } from './../../recoilModel/types';
const getFullyQualifiedKey = (value: string) => {
return `Bot_${value}_State`;
@@ -180,13 +180,6 @@ export const skillManifestsState = atomFamily
({
},
});
-export const breadcrumbState = atomFamily({
- key: getFullyQualifiedKey('breadcrumb'),
- default: (id) => {
- return [];
- },
-});
-
export const showCreateDialogModalState = atomFamily({
key: getFullyQualifiedKey('showCreateDialogModal'),
default: (id) => {
diff --git a/Composer/packages/client/src/recoilModel/dispatchers/__tests__/navigation.test.tsx b/Composer/packages/client/src/recoilModel/dispatchers/__tests__/navigation.test.tsx
index 9661ff113f..1ae9e0ca06 100644
--- a/Composer/packages/client/src/recoilModel/dispatchers/__tests__/navigation.test.tsx
+++ b/Composer/packages/client/src/recoilModel/dispatchers/__tests__/navigation.test.tsx
@@ -7,20 +7,12 @@ import { SDKKinds } from '@bfc/shared';
import { navigationDispatcher } from '../navigation';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
-import { focusPathState, breadcrumbState, designPageLocationState } from '../../atoms/botState';
+import { focusPathState, designPageLocationState } from '../../atoms/botState';
import { dialogsSelectorFamily } from '../../selectors';
import { dispatcherState } from '../../../recoilModel/DispatcherWrapper';
import { Dispatcher } from '../../../recoilModel/dispatchers';
-import {
- convertPathToUrl,
- navigateTo,
- checkUrl,
- updateBreadcrumb,
- getUrlSearch,
- BreadcrumbUpdateType,
-} from '../../../utils/navigation';
+import { convertPathToUrl, navigateTo, checkUrl, getUrlSearch } from '../../../utils/navigation';
import { createSelectedPath, getSelected } from '../../../utils/dialogUtil';
-import { BreadcrumbItem } from '../../../recoilModel/types';
import { currentProjectIdState, botProjectIdsState, botProjectFileState, projectMetaDataState } from '../../atoms';
jest.mock('../../../utils/navigation');
@@ -29,7 +21,6 @@ jest.mock('../../../utils/dialogUtil');
const mockCheckUrl = checkUrl as jest.Mock;
const mockNavigateTo = navigateTo as jest.Mock;
const mockGetSelected = getSelected as jest.Mock;
-const mockUpdateBreadcrumb = updateBreadcrumb as jest.Mock;
const mockGetUrlSearch = getUrlSearch as jest.Mock;
const mockConvertPathToUrl = convertPathToUrl as jest.Mock;
const mockCreateSelectedPath = createSelectedPath as jest.Mock;
@@ -37,8 +28,8 @@ const mockCreateSelectedPath = createSelectedPath as jest.Mock;
const projectId = '12345.678';
const skillId = '98765.4321';
-function expectNavTo(location: string, state: {} | null = null) {
- expect(mockNavigateTo).toHaveBeenLastCalledWith(location, state == null ? expect.anything() : state);
+function expectNavTo(location: string) {
+ expect(mockNavigateTo).toHaveBeenLastCalledWith(location);
}
describe('navigation dispatcher', () => {
@@ -46,7 +37,6 @@ describe('navigation dispatcher', () => {
beforeEach(() => {
mockCheckUrl.mockClear();
mockNavigateTo.mockClear();
- mockUpdateBreadcrumb.mockReturnValue([]);
mockConvertPathToUrl.mockClear();
mockCreateSelectedPath.mockClear();
@@ -54,7 +44,6 @@ describe('navigation dispatcher', () => {
const useRecoilTestHook = () => {
const focusPath = useRecoilValue(focusPathState(projectId));
- const breadcrumb = useRecoilValue(breadcrumbState(projectId));
const designPageLocation = useRecoilValue(designPageLocationState(projectId));
const dialogs = useRecoilValue(dialogsSelectorFamily(projectId));
const currentDispatcher = useRecoilValue(dispatcherState);
@@ -62,7 +51,6 @@ describe('navigation dispatcher', () => {
return {
dialogs,
focusPath,
- breadcrumb,
designPageLocation,
projectId,
currentDispatcher,
@@ -72,7 +60,6 @@ describe('navigation dispatcher', () => {
const { result } = renderRecoilHook(useRecoilTestHook, {
states: [
{ recoilState: focusPathState(projectId), initialValue: 'path' },
- { recoilState: breadcrumbState(projectId), initialValue: [{ dialogId: '100', selected: 'a', focused: 'b' }] },
{
recoilState: designPageLocationState(projectId),
initialValue: {
@@ -124,17 +111,11 @@ describe('navigation dispatcher', () => {
await act(async () => {
await dispatcher.setDesignPageLocation(projectId, {
dialogId: 'dialogId',
- breadcrumb: [],
promptTab: undefined,
});
});
expect(renderedComponent.current.focusPath).toEqual('dialogId#');
- expect(renderedComponent.current.breadcrumb).toHaveLength(1);
- expect(renderedComponent.current.breadcrumb[0]).toEqual({
- dialogId: 'dialogId',
- focused: '',
- selected: '',
- });
+
expect(renderedComponent.current.designPageLocation).toEqual({
dialogId: 'dialogId',
promptTab: undefined,
@@ -147,18 +128,12 @@ describe('navigation dispatcher', () => {
await act(async () => {
await dispatcher.setDesignPageLocation(projectId, {
dialogId: 'dialogId',
- breadcrumb: [],
selected: 'select',
promptTab: undefined,
});
});
expect(renderedComponent.current.focusPath).toEqual('dialogId#.select');
- expect(renderedComponent.current.breadcrumb).toHaveLength(1);
- expect(renderedComponent.current.breadcrumb[0]).toEqual({
- dialogId: 'dialogId',
- focused: '',
- selected: 'select',
- });
+
expect(renderedComponent.current.designPageLocation).toEqual({
dialogId: 'dialogId',
promptTab: undefined,
@@ -171,19 +146,13 @@ describe('navigation dispatcher', () => {
await act(async () => {
await dispatcher.setDesignPageLocation(projectId, {
dialogId: 'dialogId',
- breadcrumb: [],
focused: 'focus',
selected: 'select',
promptTab: undefined,
});
});
expect(renderedComponent.current.focusPath).toEqual('dialogId#.focus');
- expect(renderedComponent.current.breadcrumb).toHaveLength(1);
- expect(renderedComponent.current.breadcrumb[0]).toEqual({
- dialogId: 'dialogId',
- focused: 'focus',
- selected: 'select',
- });
+
expect(renderedComponent.current.designPageLocation).toEqual({
dialogId: 'dialogId',
promptTab: undefined,
@@ -197,7 +166,7 @@ describe('navigation dispatcher', () => {
it('navigates to a destination', async () => {
mockConvertPathToUrl.mockReturnValue(`/bot/${projectId}/dialogs/dialogId`);
await act(async () => {
- await dispatcher.navTo(projectId, 'dialogId', []);
+ await dispatcher.navTo(projectId, 'dialogId');
});
expectNavTo(`/bot/${projectId}/dialogs/dialogId`);
expect(mockConvertPathToUrl).toBeCalledWith(projectId, projectId, 'dialogId');
@@ -206,7 +175,7 @@ describe('navigation dispatcher', () => {
it("doesn't navigate to a destination where we already are", async () => {
mockCheckUrl.mockReturnValue(true);
await act(async () => {
- await dispatcher.navTo(projectId, 'dialogId', []);
+ await dispatcher.navTo(projectId, 'dialogId');
});
expect(mockNavigateTo).not.toBeCalled();
});
@@ -261,8 +230,6 @@ describe('navigation dispatcher', () => {
await dispatcher.focusTo(projectId, null, 'focus', '');
});
expectNavTo(`/bot/${projectId}/dialogs/dialogId?selected=select&focused=focus`);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Selected);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Focused);
});
it('goes to a focused page with skill', async () => {
@@ -271,8 +238,6 @@ describe('navigation dispatcher', () => {
await dispatcher.focusTo(projectId, skillId, 'focus', '');
});
expectNavTo(`/bot/${projectId}/skill/${skillId}/dialogs/dialogInSkillId?selected=select&focused=focus`);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Selected);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Focused);
});
it('goes to a focused page with fragment', async () => {
@@ -281,8 +246,6 @@ describe('navigation dispatcher', () => {
await dispatcher.focusTo(projectId, null, 'focus', 'fragment');
});
expectNavTo(`/bot/${projectId}/dialogs/dialogId?selected=select&focused=focus#fragment`);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Selected);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Focused);
});
it('goes to a focused page with skill and fragment', async () => {
@@ -291,8 +254,6 @@ describe('navigation dispatcher', () => {
await dispatcher.focusTo(projectId, skillId, 'focus', 'fragment');
});
expectNavTo(`/bot/${projectId}/skill/${skillId}/dialogs/dialogInSkillId?selected=select&focused=focus#fragment`);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Selected);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Focused);
});
it('stays on the same page but updates breadcrumbs with a checked URL', async () => {
@@ -302,8 +263,6 @@ describe('navigation dispatcher', () => {
await dispatcher.focusTo(projectId, null, 'focus', 'fragment');
});
expect(mockNavigateTo).not.toBeCalled();
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Selected);
- expect(mockUpdateBreadcrumb).toHaveBeenCalledWith(expect.anything(), BreadcrumbUpdateType.Focused);
});
});
diff --git a/Composer/packages/client/src/recoilModel/dispatchers/navigation.ts b/Composer/packages/client/src/recoilModel/dispatchers/navigation.ts
index e18df80e4d..c4440aa69b 100644
--- a/Composer/packages/client/src/recoilModel/dispatchers/navigation.ts
+++ b/Composer/packages/client/src/recoilModel/dispatchers/navigation.ts
@@ -11,22 +11,14 @@ import { encodeArrayPathToDesignerPath } from '../../utils/convertUtils/designer
import { dialogsSelectorFamily, rootBotProjectIdSelector } from '../selectors';
import { getSelected } from './../../utils/dialogUtil';
-import { BreadcrumbItem } from './../../recoilModel/types';
-import { breadcrumbState, designPageLocationState, focusPathState } from './../atoms/botState';
-import {
- BreadcrumbUpdateType,
- checkUrl,
- convertPathToUrl,
- getUrlSearch,
- navigateTo,
- updateBreadcrumb,
-} from './../../utils/navigation';
+import { designPageLocationState, focusPathState } from './../atoms/botState';
+import { checkUrl, convertPathToUrl, getUrlSearch, navigateTo } from './../../utils/navigation';
export const navigationDispatcher = () => {
const setDesignPageLocation = useRecoilCallback(
({ set }: CallbackInterface) => async (
projectId: string,
- { dialogId = '', selected = '', focused = '', breadcrumb = [], promptTab }
+ { dialogId = '', selected = '', focused = '', promptTab }
) => {
let focusPath = dialogId + '#';
if (focused) {
@@ -36,8 +28,6 @@ export const navigationDispatcher = () => {
}
set(currentProjectIdState, projectId);
set(focusPathState(projectId), focusPath);
- //add current path to the breadcrumb
- set(breadcrumbState(projectId), [...breadcrumb, { dialogId, selected, focused }]);
set(designPageLocationState(projectId), {
dialogId,
selected,
@@ -51,7 +41,7 @@ export const navigationDispatcher = () => {
({ snapshot, set }: CallbackInterface) => async (
skillId: string | null,
dialogId: string | null,
- breadcrumb: BreadcrumbItem[] = []
+ trigger?: string
) => {
const rootBotProjectId = await snapshot.getPromise(rootBotProjectIdSelector);
if (rootBotProjectId == null) return;
@@ -61,10 +51,13 @@ export const navigationDispatcher = () => {
const designPageLocation = await snapshot.getPromise(designPageLocationState(projectId));
set(currentProjectIdState, projectId);
- const currentUri = convertPathToUrl(rootBotProjectId, projectId, dialogId);
+ const currentUri =
+ trigger == null
+ ? convertPathToUrl(rootBotProjectId, skillId, dialogId)
+ : convertPathToUrl(rootBotProjectId, skillId, dialogId, `selected=triggers[${trigger}]`);
if (checkUrl(currentUri, rootBotProjectId, projectId, designPageLocation)) return;
- navigateTo(currentUri, { state: { breadcrumb } });
+ navigateTo(currentUri);
}
);
@@ -82,7 +75,6 @@ export const navigationDispatcher = () => {
set(currentProjectIdState, projectId);
const designPageLocation = await snapshot.getPromise(designPageLocationState(projectId));
- const breadcrumb = await snapshot.getPromise(breadcrumbState(projectId));
// target dialogId, projectId maybe empty string ""
const dialogId = destinationDialogId ?? designPageLocation.dialogId ?? 'Main';
@@ -93,7 +85,7 @@ export const navigationDispatcher = () => {
const currentUri = convertPathToUrl(rootBotProjectId, skillId, dialogId, encodedSelectPath);
if (checkUrl(currentUri, rootBotProjectId, skillId, designPageLocation)) return;
- navigateTo(currentUri, { state: { breadcrumb: updateBreadcrumb(breadcrumb, BreadcrumbUpdateType.Selected) } });
+ navigateTo(currentUri);
}
);
@@ -106,12 +98,10 @@ export const navigationDispatcher = () => {
) => {
set(currentProjectIdState, skillId ?? projectId);
const designPageLocation = await snapshot.getPromise(designPageLocationState(skillId ?? projectId));
- const breadcrumb = await snapshot.getPromise(breadcrumbState(skillId ?? projectId));
- let updatedBreadcrumb = [...breadcrumb];
const { dialogId, selected } = designPageLocation;
let currentUri =
- skillId == null
+ skillId == null || skillId === projectId
? `/bot/${projectId}/dialogs/${dialogId}`
: `/bot/${projectId}/skill/${skillId}/dialogs/${dialogId}`;
@@ -121,22 +111,17 @@ export const navigationDispatcher = () => {
const encodedFocusPath = encodeArrayPathToDesignerPath(currentDialog?.content, focusPath);
const targetSelected = getSelected(encodedFocusPath);
- if (targetSelected !== selected) {
- updatedBreadcrumb = updateBreadcrumb(breadcrumb, BreadcrumbUpdateType.Selected);
- updatedBreadcrumb.push({ dialogId, selected: targetSelected, focused: '' });
- }
+
currentUri = `${currentUri}?selected=${targetSelected}&focused=${encodedFocusPath}`;
- updatedBreadcrumb = updateBreadcrumb(breadcrumb, BreadcrumbUpdateType.Focused);
} else {
currentUri = `${currentUri}?selected=${selected}`;
- updatedBreadcrumb = updateBreadcrumb(breadcrumb, BreadcrumbUpdateType.Selected);
}
if (fragment && typeof fragment === 'string') {
currentUri += `#${fragment}`;
}
if (checkUrl(currentUri, projectId, skillId, designPageLocation)) return;
- navigateTo(currentUri, { state: { breadcrumb: updatedBreadcrumb } });
+ navigateTo(currentUri);
}
);
@@ -146,8 +131,7 @@ export const navigationDispatcher = () => {
skillId: string | null,
dialogId: string,
selectPath: string,
- focusPath: string,
- breadcrumb: BreadcrumbItem[] = []
+ focusPath: string
) => {
set(currentProjectIdState, projectId);
@@ -159,14 +143,14 @@ export const navigationDispatcher = () => {
const designPageLocation = await snapshot.getPromise(designPageLocationState(projectId));
if (search) {
const currentUri =
- skillId == null
+ skillId == null || skillId === projectId
? `/bot/${projectId}/dialogs/${dialogId}${search}`
: `/bot/${projectId}/skill/${skillId}/dialogs/${dialogId}${search}`;
if (checkUrl(currentUri, projectId, skillId, designPageLocation)) return;
- navigateTo(currentUri, { state: { breadcrumb } });
+ navigateTo(currentUri);
} else {
- navTo(skillId ?? projectId, dialogId, breadcrumb);
+ navTo(skillId ?? projectId, dialogId);
}
}
);
diff --git a/Composer/packages/client/src/recoilModel/dispatchers/publisher.ts b/Composer/packages/client/src/recoilModel/dispatchers/publisher.ts
index e4ec8f5a2e..96f342d7c7 100644
--- a/Composer/packages/client/src/recoilModel/dispatchers/publisher.ts
+++ b/Composer/packages/client/src/recoilModel/dispatchers/publisher.ts
@@ -68,6 +68,7 @@ export const publisherDispatcher = () => {
};
const updatePublishStatus = ({ set }: CallbackInterface, projectId: string, target: any, data: any) => {
+ if (data == null) return;
const { endpointURL, status, id } = data;
// the action below only applies to when a bot is being started using the "start bot" button
// a check should be added to this that ensures this ONLY applies to the "default" profile.
@@ -173,9 +174,9 @@ export const publisherDispatcher = () => {
(callbackHelpers: CallbackInterface) => async (projectId: string, target: any) => {
try {
const response = await httpClient.get(`/publish/${projectId}/status/${target.name}`);
- updatePublishStatus(callbackHelpers, projectId, target, response.data);
+ updatePublishStatus(callbackHelpers, projectId, target, response?.data);
} catch (err) {
- updatePublishStatus(callbackHelpers, projectId, target, err.response.data);
+ updatePublishStatus(callbackHelpers, projectId, target, err.response?.data);
}
}
);
diff --git a/Composer/packages/client/src/recoilModel/types.ts b/Composer/packages/client/src/recoilModel/types.ts
index 453eca41b0..b22608fec3 100644
--- a/Composer/packages/client/src/recoilModel/types.ts
+++ b/Composer/packages/client/src/recoilModel/types.ts
@@ -76,13 +76,6 @@ export interface AppUpdateState {
version?: string;
}
-export interface BreadcrumbItem {
- skillId?: string;
- dialogId: string;
- selected: string;
- focused: string;
-}
-
export type dialogPayload = {
id: string;
content: any;
@@ -94,7 +87,7 @@ export type DesignPageLocationPayload = {
dialogId: string;
selected: string;
focused: string;
- breadcrumb: BreadcrumbItem[];
+ breadcrumb: string[];
promptTab?: string;
};
diff --git a/Composer/packages/client/src/recoilModel/undo/history.ts b/Composer/packages/client/src/recoilModel/undo/history.ts
index 1a431b8212..f34c92ae16 100644
--- a/Composer/packages/client/src/recoilModel/undo/history.ts
+++ b/Composer/packages/client/src/recoilModel/undo/history.ts
@@ -13,7 +13,6 @@ import isEmpty from 'lodash/isEmpty';
import { navigateTo, getUrlSearch } from '../../utils/navigation';
-import { breadcrumbState } from './../atoms/botState';
import { designPageLocationState } from './../atoms';
import { trackedAtoms, AtomAssetsMap } from './trackedAtoms';
import UndoHistory from './undoHistory';
@@ -55,7 +54,6 @@ const getAtomAssetsMap = (snap: Snapshot, projectId: string): AtomAssetsMap => {
//should record the location state
atomMap.set(designPageLocationState(projectId), snap.getLoadable(designPageLocationState(projectId)).contents);
- atomMap.set(breadcrumbState(projectId), snap.getLoadable(breadcrumbState(projectId)).contents);
return atomMap;
};
@@ -75,15 +73,13 @@ const checkAtomsChanged = (current: AtomAssetsMap, previous: AtomAssetsMap, atom
function navigate(next: AtomAssetsMap, projectId: string) {
const location = next.get(designPageLocationState(projectId));
- const breadcrumb = [...next.get(breadcrumbState(projectId))];
if (location) {
const { dialogId, selected, focused, promptTab } = location;
let currentUri = `/bot/${projectId}/dialogs/${dialogId}${getUrlSearch(selected, focused)}`;
if (promptTab) {
currentUri += `#${promptTab}`;
}
- breadcrumb.pop();
- navigateTo(currentUri, { state: { breadcrumb } });
+ navigateTo(currentUri);
}
}
@@ -105,10 +101,8 @@ function mapTrackedAtomsOntoSnapshot(
function setInitialLocation(snapshot: Snapshot, projectId: string, undoHistory: UndoHistory) {
const location = snapshot.getLoadable(designPageLocationState(projectId));
- const breadcrumb = snapshot.getLoadable(breadcrumbState(projectId));
if (location.state === 'hasValue') {
undoHistory.setInitialValue(designPageLocationState(projectId), location.contents);
- undoHistory.setInitialValue(breadcrumbState(projectId), breadcrumb.contents);
}
}
interface UndoRootProps {
@@ -184,11 +178,11 @@ export const UndoRoot = React.memo((props: UndoRootProps) => {
});
const canUndo = () => {
- return history.canUndo();
+ return history?.canUndo?.();
};
const canRedo = () => {
- return history.canRedo();
+ return history?.canRedo?.();
};
const commit = useRecoilCallback(({ snapshot }) => () => {
diff --git a/Composer/packages/client/src/shell/useShell.ts b/Composer/packages/client/src/shell/useShell.ts
index 2026a0414e..f7bab76ed6 100644
--- a/Composer/packages/client/src/shell/useShell.ts
+++ b/Composer/packages/client/src/shell/useShell.ts
@@ -16,7 +16,6 @@ import {
clipboardActionsState,
schemasState,
validateDialogsSelectorFamily,
- breadcrumbState,
focusPathState,
skillsState,
localeState,
@@ -66,7 +65,6 @@ export function useShell(source: EventSource, projectId: string): Shell {
const schemas = useRecoilValue(schemasState(projectId));
const dialogs = useRecoilValue(validateDialogsSelectorFamily(projectId));
- const breadcrumb = useRecoilValue(breadcrumbState(projectId));
const focusPath = useRecoilValue(focusPathState(projectId));
const skills = useRecoilValue(skillsState(projectId));
const locale = useRecoilValue(localeState(projectId));
@@ -137,9 +135,9 @@ export function useShell(source: EventSource, projectId: string): Shell {
updateDialog({ id, content: newDialog.content, projectId });
}
- function navigationTo(path) {
+ function navigationTo(path, rest?) {
if (rootBotProjectId == null) return;
- navTo(projectId, path, breadcrumb);
+ navTo(projectId, path, rest);
}
function focusEvent(subPath) {
diff --git a/Composer/packages/client/src/utils/convertUtils/designerPathEncoder.ts b/Composer/packages/client/src/utils/convertUtils/designerPathEncoder.ts
index 657a050f33..402edc1874 100644
--- a/Composer/packages/client/src/utils/convertUtils/designerPathEncoder.ts
+++ b/Composer/packages/client/src/utils/convertUtils/designerPathEncoder.ts
@@ -101,7 +101,12 @@ export const decodeDesignerPathToArrayPath = (dialog, path: string): string => {
let arrayIndex = -1;
if (designerPathInfo) {
const { designerId } = designerPathInfo;
- arrayIndex = arrayData.findIndex((x) => get(x, '$designer.id') === designerId);
+ if (designerId === 'beginDialog') {
+ // special notation to route to this specific trigger
+ arrayIndex = arrayData.findIndex((x) => get(x, '$kind') === 'Microsoft.OnBeginDialog');
+ } else {
+ arrayIndex = arrayData.findIndex((x) => get(x, '$designer.id') === designerId);
+ }
} else if (arrayPathInfo) {
arrayIndex = arrayPathInfo.index;
}
diff --git a/Composer/packages/client/src/utils/navigation.ts b/Composer/packages/client/src/utils/navigation.ts
index dcc7b19fe4..7a8432d25e 100644
--- a/Composer/packages/client/src/utils/navigation.ts
+++ b/Composer/packages/client/src/utils/navigation.ts
@@ -1,20 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-import cloneDeep from 'lodash/cloneDeep';
import { navigate, NavigateOptions } from '@reach/router';
-import { BreadcrumbItem, DesignPageLocation } from '../recoilModel/types';
+import { DesignPageLocation } from '../recoilModel/types';
import { BASEPATH } from '../constants';
import { parsePathToFocused } from './convertUtils/parsePathToFocused';
import { parsePathToSelected } from './convertUtils/parsePathToSelected';
import { parseTypeToFragment } from './convertUtils/parseTypeToFragment';
import { resolveToBasePath } from './fileUtil';
-export const BreadcrumbUpdateType = {
- Selected: 'selected',
- Focused: 'focused',
-};
export function getFocusPath(selected: string, focused: string): string {
if (selected && focused) return focused;
@@ -24,31 +19,6 @@ export function getFocusPath(selected: string, focused: string): string {
return '';
}
-export function clearBreadcrumb(breadcrumb: BreadcrumbItem[], fromIndex?: number): BreadcrumbItem[] {
- let breadcrumbCopy = cloneDeep(breadcrumb);
- if (fromIndex) {
- breadcrumbCopy.splice(fromIndex, breadcrumbCopy.length - fromIndex);
- } else {
- breadcrumbCopy = [];
- }
- return breadcrumbCopy;
-}
-
-export function updateBreadcrumb(breadcrumb: BreadcrumbItem[], type: string): BreadcrumbItem[] {
- const breadcrumbCopy = cloneDeep(breadcrumb);
- if (breadcrumbCopy.length === 0) {
- return breadcrumbCopy;
- }
-
- let lastIndex = breadcrumbCopy.length - 1;
- while (lastIndex > 0 && breadcrumbCopy[lastIndex][type]) {
- breadcrumbCopy.pop();
- lastIndex--;
- }
-
- return breadcrumbCopy;
-}
-
export function getUrlSearch(selected: string, focused: string): string {
const search = new URLSearchParams();
if (selected) {
@@ -83,7 +53,7 @@ export function checkUrl(
}
export interface NavigationState {
- breadcrumb?: BreadcrumbItem[];
+ breadcrumb?: string[];
qnaKbUrls?: string[];
}
@@ -97,7 +67,7 @@ export function convertPathToUrl(
//uri = id?selected=triggers[0]&focused=triggers[0].actions[0]
let uri = `/bot/${projectId}`;
- if (skillId != null) {
+ if (skillId != null && skillId !== projectId) {
uri += `/skill/${skillId}`;
}
if (dialogId != null) {
diff --git a/Composer/packages/client/src/utils/onboardingStorage.ts b/Composer/packages/client/src/utils/onboardingStorage.ts
index 9349feee7f..1391b97685 100644
--- a/Composer/packages/client/src/utils/onboardingStorage.ts
+++ b/Composer/packages/client/src/utils/onboardingStorage.ts
@@ -4,7 +4,7 @@
import storage from './storage';
const KEY = 'OnboardingState';
-const DEFAULT_STATE = { complete: false };
+const DEFAULT_STATE = { complete: process.env.NODE_ENV !== 'production' };
interface IOnboardingState {
complete: boolean;
diff --git a/Composer/packages/lib/shared/__tests__/dialogUtils/validateDialogName.test.ts b/Composer/packages/lib/shared/__tests__/dialogUtils/validateDialogName.test.ts
index 8edcbec0a2..0666c0dbc1 100644
--- a/Composer/packages/lib/shared/__tests__/dialogUtils/validateDialogName.test.ts
+++ b/Composer/packages/lib/shared/__tests__/dialogUtils/validateDialogName.test.ts
@@ -4,7 +4,7 @@
import { validateDialogName } from '../../src/dialogUtils/validateDialogName';
const error = new Error(
- "Spaces and special characters are not allowed. Use letters, numbers, -, or _ and don't use number at the beginning."
+ 'Spaces and special characters are not allowed. Use letters, numbers, -, or _, and begin the name with a letter.'
);
const emptyError = new Error('The file name can not be empty');
diff --git a/Composer/packages/lib/shared/src/dialogUtils/validateDialogName.ts b/Composer/packages/lib/shared/src/dialogUtils/validateDialogName.ts
index cf6d2fad1c..6450599600 100644
--- a/Composer/packages/lib/shared/src/dialogUtils/validateDialogName.ts
+++ b/Composer/packages/lib/shared/src/dialogUtils/validateDialogName.ts
@@ -13,7 +13,7 @@ export const validateDialogName = (name: string) => {
if (!nameRegex.test(name)) {
throw new Error(
formatMessage(
- "Spaces and special characters are not allowed. Use letters, numbers, -, or _ and don't use number at the beginning."
+ 'Spaces and special characters are not allowed. Use letters, numbers, -, or _, and begin the name with a letter.'
)
);
}
diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json
index 29c2613422..ab91373303 100644
--- a/Composer/packages/server/src/locales/en-US.json
+++ b/Composer/packages/server/src/locales/en-US.json
@@ -2366,12 +2366,12 @@
"spaces_and_special_characters_are_not_allowed_20d47684": {
"message": "Spaces and special characters are not allowed."
},
- "spaces_and_special_characters_are_not_allowed_use__2a61c454": {
- "message": "Spaces and special characters are not allowed. Use letters, numbers, -, or _ and don''t use number at the beginning."
- },
"spaces_and_special_characters_are_not_allowed_use__48acec3c": {
"message": "Spaces and special characters are not allowed. Use letters, numbers, -, or _."
},
+ "spaces_and_special_characters_are_not_allowed_use__d24a8636": {
+ "message": "Spaces and special characters are not allowed. Use letters, numbers, -, or _, and begin the name with a letter."
+ },
"specify_a_name_and_description_for_your_new_dialog_86eb3130": {
"message": "Specify a name and description for your new dialog."
},
@@ -2672,6 +2672,9 @@
"uninstall_8730233": {
"message": "Uninstall"
},
+ "unknown_47a3b725": {
+ "message": "Unknown"
+ },
"unknown_intent_44b962ba": {
"message": "Unknown intent"
},
diff --git a/Composer/packages/server/src/models/bot/__tests__/botProject.test.ts b/Composer/packages/server/src/models/bot/__tests__/botProject.test.ts
index baeebe3c52..e7fcfa384a 100644
--- a/Composer/packages/server/src/models/bot/__tests__/botProject.test.ts
+++ b/Composer/packages/server/src/models/bot/__tests__/botProject.test.ts
@@ -356,7 +356,7 @@ describe('dialog schema operations', () => {
describe('should validate the file name when create a new one', () => {
const error = new Error(
- "Spaces and special characters are not allowed. Use letters, numbers, -, or _ and don't use number at the beginning."
+ 'Spaces and special characters are not allowed. Use letters, numbers, -, or _, and begin the name with a letter.'
);
const emptyError = new Error('The file name can not be empty');