in Composer/packages/client/src/shell/useShell.ts [91:380]
export function useShell(source: EventSource, projectId: string): Shell {
const dialogMapRef = useRef({});
const schemas = useRecoilValue(schemasState(projectId));
const dialogs = useRecoilValue(dialogsWithLuProviderSelectorFamily(projectId));
const topics = useRecoilValue(topicsSelectorFamily(projectId));
const focusPath = useRecoilValue(focusPathState(projectId));
const skills = useRecoilValue(skillsStateSelector);
const locale = useRecoilValue(localeState(projectId));
const qnaFiles = useRecoilValue(qnaFilesSelectorFamily(projectId));
const undoFunction = useRecoilValue(undoFunctionState(projectId));
const designPageLocation = useRecoilValue(designPageLocationState(projectId));
const { undo, redo, commitChanges } = undoFunction;
const luFiles = useRecoilValue(luFilesSelectorFamily(projectId));
const lgFiles = useRecoilValue(lgFilesSelectorFamily(projectId));
const dialogSchemas = useRecoilValue(dialogSchemasState(projectId));
const botName = useRecoilValue(botDisplayNameState(projectId));
const settings = useRecoilValue(settingsState(projectId));
const flowZoomRate = useRecoilValue(rateInfoState);
const flowCommentsVisible = useRecoilValue(flowCommentsVisibilityState);
const rootBotProjectId = useRecoilValue(rootBotProjectIdSelector);
const isRootBot = rootBotProjectId === projectId;
const isAuthenticated = useRecoilValue(isAuthenticatedState);
const currentUser = useRecoilValue(currentUserState);
const currentTenant = useRecoilValue(currentTenantIdState);
const showAuthDialog = useRecoilValue(showAuthDialogState);
const projectCollection = useRecoilValue<BotInProject[]>(botProjectSpaceSelector).map((bot) => ({
...bot,
hasWarnings: false,
}));
const userSettings = useRecoilValue(userSettingsState);
const clipboardActions = useRecoilValue(clipboardActionsState(projectId));
const featureFlags = useRecoilValue(featureFlagsState);
const {
updateDialog,
updateDialogSchema,
createDialogBegin,
navTo,
focusTo,
selectTo,
setVisualEditorSelection,
setVisualEditorClipboard,
onboardingAddCoachMarkRef,
updateUserSettings,
setMessage,
displayManifestModal,
updateSkillsDataInBotProjectFile: updateEndpointInBotProjectFile,
updateZoomRate,
toggleFlowComments,
reloadProject,
setApplicationLevelError,
updateRecognizer,
addNotification,
deleteNotification,
hideNotification,
markNotificationAsRead,
requireUserLogin,
} = useRecoilValue(dispatcherState);
const lgApi = useLgApi(projectId);
const luApi = useLuApi(projectId);
const qnaApi = useQnaApi(projectId);
const triggerApi = useTriggerApi(projectId);
const actionApi = useActionApi(projectId);
const { dialogId, selected, focused, promptTab } = designPageLocation;
const { stopSingleBot } = useBotOperations();
const dialogsMap = useMemo(() => {
return dialogs.reduce((result, dialog) => {
result[dialog.id] = dialog.content;
return result;
}, {});
}, [dialogs]);
function updateRegExIntentHandler(id, intentName, pattern) {
const dialog = dialogs.find((dialog) => dialog.id === id);
if (!dialog) throw new Error(formatMessage(`dialog {dialogId} not found`, { dialogId }));
const newDialog = updateRegExIntent(dialog, intentName, pattern);
updateDialog({ id, content: newDialog.content, projectId });
}
function renameRegExIntentHandler(id: string, intentName: string, newIntentName: string) {
const dialog = dialogs.find((dialog) => dialog.id === id);
if (!dialog) throw new Error(`dialog ${dialogId} not found`);
const newDialog = renameRegExIntent(dialog, intentName, newIntentName);
updateDialog({ id, content: newDialog.content, projectId });
}
function updateIntentTriggerHandler(id: string, intentName: string, newIntentName: string) {
const dialog = dialogs.find((dialog) => dialog.id === id);
if (!dialog) throw new Error(`dialog ${dialogId} not found`);
const newDialog = updateIntentTrigger(dialog, intentName, newIntentName);
updateDialog({ id, content: newDialog.content, projectId });
}
async function navigationTo(path, rest?) {
if (rootBotProjectId == null) return;
await navTo(projectId, path, rest);
}
async function openDialog(dialogId: string) {
await navTo(projectId, dialogId, '"beginDialog"');
}
async function focusEvent(subPath) {
if (rootBotProjectId == null) return;
await selectTo(projectId, dialogId, subPath);
}
async function focusSteps(subPaths: string[] = [], fragment?: string) {
let dataPath: string = subPaths[0];
if (source === FORM_EDITOR) {
// nothing focused yet, prepend the selected path
if (!focused && selected) {
dataPath = `${selected}.${dataPath}`;
} else if (focused !== dataPath) {
dataPath = `${focused}.${dataPath}`;
}
}
await focusTo(rootBotProjectId ?? projectId, projectId, dataPath, fragment ?? '');
}
function updateFlowZoomRate(currentRate) {
updateZoomRate({ currentRate });
}
dialogMapRef.current = dialogsMap;
const api: ShellApi = {
getDialog: (dialogId: string) => {
return dialogMapRef.current[dialogId];
},
saveDialog: (dialogId: string, newDialogData: any) => {
dialogMapRef.current[dialogId] = newDialogData;
updateDialog({
id: dialogId,
content: newDialogData,
projectId,
});
},
saveData: (newData, updatePath, callback) => {
let dataPath = '';
if (source === FORM_EDITOR) {
dataPath = updatePath || focused || '';
}
const updatedDialog = setDialogData(dialogMapRef.current, dialogId, dataPath, newData);
const payload = {
id: dialogId,
content: updatedDialog,
projectId,
};
dialogMapRef.current[dialogId] = updatedDialog;
return updateDialog(payload).then(async () => {
if (typeof callback === 'function') {
await callback();
}
commitChanges();
});
},
updateRecognizer,
updateRegExIntent: updateRegExIntentHandler,
renameRegExIntent: renameRegExIntentHandler,
updateIntentTrigger: updateIntentTriggerHandler,
navTo: navigationTo,
onOpenDialog: openDialog,
onFocusEvent: focusEvent,
onFocusSteps: focusSteps,
onSelect: setVisualEditorSelection,
onCopy: (clipboardActions) => setVisualEditorClipboard(clipboardActions, projectId),
createDialog: (actionsSeed = []) => {
return new Promise((resolve) => {
createDialogBegin(
actionsSeed,
(newDialog: string | null) => {
resolve(newDialog);
},
projectId
);
});
},
undo,
redo,
commitChanges,
displayManifestModal: (skillId) => displayManifestModal(skillId),
isFeatureEnabled: (featureFlagKey: FeatureFlagKey): boolean => featureFlags?.[featureFlagKey]?.enabled ?? false,
updateDialogSchema: async (dialogSchema: DialogSchemaFile) => {
updateDialogSchema(dialogSchema, projectId);
},
updateSkill: async (skillId: string, skillsData) => {
updateEndpointInBotProjectFile(skillId, skillsData.skill, skillsData.selectedEndpointIndex);
},
updateFlowZoomRate,
toggleFlowComments,
reloadProject: () => reloadProject(projectId),
stopBot: (targetProjectId: string) => {
stopSingleBot(targetProjectId);
},
...lgApi,
...luApi,
...qnaApi,
...triggerApi,
...actionApi,
// application context
addCoachMarkRef: onboardingAddCoachMarkRef,
announce: setMessage,
navigateTo,
setApplicationLevelError,
updateUserSettings,
confirm: OpenConfirmModal,
telemetryClient: TelemetryClient,
getMemoryVariables,
addNotification: (notificationWithoutId: Notification): string => {
const notification = createNotification(notificationWithoutId);
addNotification(notification);
return notification.id;
},
deleteNotification,
markNotificationAsRead,
hideNotification,
requireUserLogin,
};
const currentDialog = useMemo(() => {
let result: any = dialogs.find((d) => d.id === dialogId) ?? dialogs.find((dialog) => dialog.isRoot);
if (!result) {
// Should not hit here as the seed content should atleast be the root dialog if no current dialog
result = stubDialog();
}
return result;
}, [dialogs, dialogId]);
const editorData = useMemo(() => {
return source === 'PropertyEditor'
? getDialogData(dialogsMap, dialogId, focused || selected || '')
: getDialogData(dialogsMap, dialogId);
}, [source, dialogsMap, dialogId, focused, selected]);
const data: ShellData = {
locale,
botName,
projectId,
projectCollection,
dialogs,
topics,
dialogSchemas,
dialogId,
focusPath,
schemas,
lgFiles,
luFiles,
qnaFiles,
currentUser,
currentTenant,
isAuthenticated,
showAuthDialog,
currentDialog,
userSettings,
designerId: editorData?.$designer?.id,
focusedEvent: selected,
focusedActions: focused ? [focused] : [],
focusedSteps: focused ? [focused] : selected ? [selected] : [],
focusedTab: promptTab,
clipboardActions,
hosted: !!isAbsHosted(),
luFeatures: settings.luFeatures,
skills,
skillsSettings: settings.skill || {},
flowZoomRate,
flowCommentsVisible,
forceDisabledActions: isRootBot
? []
: [
{
kind: SDKKinds.BeginSkill,
reason: formatMessage('You can only connect to a skill in the root bot.'),
},
],
settings,
httpClient,
};
return {
api,
data,
};
}