Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import { useRecoilValue } from 'recoil';
import { DialogCreationCopy } from '../../../constants';
import { getAliasFromPayload, isElectron } from '../../../utils/electronUtil';
import { userHasNodeInstalledState } from '../../../recoilModel';
import { InstallDepModal } from '../../InstallDepModal';

import { CreateBotV2 } from './CreateBot';
import { NodeModal } from './NodeModal';

// -------------------- CreateOptions -------------------- //
type CreateOptionsProps = {
Expand Down Expand Up @@ -133,7 +133,18 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
onDismiss={onDismiss}
onNext={onNext}
/>
{isElectron() && showNodeModal && <NodeModal isOpen={showNodeModal} setIsOpen={setShowNodeModal} />}
{isElectron() && showNodeModal && (
<InstallDepModal
isOpen={showNodeModal}
link={'https://nodejs.org/en/download/'}
linkText={formatMessage('Install Node.js')}
setIsOpen={setShowNodeModal}
text={formatMessage(
'Bot Framework Composer requires Node.js in order to create and run a new bot. Click “Install Node.js” to install the latest version'
)}
title={formatMessage('Node.js required')}
/>
)}
</Fragment>
);
}
63 changes: 63 additions & 0 deletions Composer/packages/client/src/components/InstallDepModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable react/no-danger */
Comment thread
benbrown marked this conversation as resolved.
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

/** @jsx jsx */
import { DialogTypes, DialogWrapper } from '@bfc/ui-shared/lib/components/DialogWrapper';
import { jsx } from '@emotion/core';
import formatMessage from 'format-message';
import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/components/Button';
import { DialogFooter } from 'office-ui-fabric-react/lib/components/Dialog';
import React from 'react';
import { Text } from 'office-ui-fabric-react/lib/Text';
import { Link } from 'office-ui-fabric-react/lib/Link';
import { mergeStyles } from 'office-ui-fabric-react/lib/Styling';

const dialogFooterClass = mergeStyles({
Comment thread
benbrown marked this conversation as resolved.
Outdated
marginTop: '25px',
});

type InstallDepModalProps = {
Comment thread
benbrown marked this conversation as resolved.
setIsOpen: Function;
isOpen: boolean;
title: string;
text: string;
link: string;
linkText: string;
learnMore?: string;
learnMoreText?: string;
};

export const InstallDepModal: React.FC<InstallDepModalProps> = (props) => {
return (
<DialogWrapper
dialogType={DialogTypes.DesignFlow}
isOpen={props.isOpen}
title={props.title}
onDismiss={() => {
props.setIsOpen(false);
}}
>
<Text>
<span>{props.text}</span>
{props.learnMore && (
<span>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this and nbsp and use margin instead

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. We've talked about this before several times. An NBSP is the appropriate way to add a space between 2 inline elements. Margin is overkill and inapprorpriate for inline elements.

&nbsp;
<Link href={props.learnMore} target="_blank">
{props.learnMoreText}
</Link>
</span>
)}
</Text>
<DialogFooter className={dialogFooterClass}>
<PrimaryButton data-testid="InstallButton" href={props.link} target="_blank" text={props.linkText} />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This testId seems too generic. installDotNet? In addition, if its not required we can remove it

<DefaultButton
text={formatMessage('Cancel')}
onClick={() => {
props.setIsOpen(false);
}}
/>
</DialogFooter>
</DialogWrapper>
);
};
20 changes: 19 additions & 1 deletion Composer/packages/client/src/pages/home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ import {
feedState,
templateIdState,
currentProjectIdState,
warnAboutDotNetState,
} from '../../recoilModel/atoms/appState';
import TelemetryClient from '../../telemetry/TelemetryClient';
import composerDocumentIcon from '../../images/composerDocumentIcon.svg';
import stackoverflowIcon from '../../images/stackoverflowIcon.svg';
import githubIcon from '../../images/githubIcon.svg';
import noRecentBotsCover from '../../images/noRecentBotsCover.svg';
import { InstallDepModal } from '../../components/InstallDepModal';
import { missingDotnetVersionError } from '../../utils/runtimeErrors';

import { RecentBotList } from './RecentBotList';
import { WhatsNewsList } from './WhatsNewsList';
Expand Down Expand Up @@ -70,7 +73,10 @@ const Home: React.FC<RouteComponentProps> = () => {
const recentProjects = useRecoilValue(recentProjectsState);
const feed = useRecoilValue(feedState);
const templateId = useRecoilValue(templateIdState);
const { openProject, setCreationFlowStatus, setCreationFlowType } = useRecoilValue(dispatcherState);
const { openProject, setCreationFlowStatus, setCreationFlowType, setWarnAboutDotNet } = useRecoilValue(
dispatcherState
);
const warnAboutDotNet = useRecoilValue(warnAboutDotNetState);

const onItemChosen = async (item) => {
if (item?.path) {
Expand Down Expand Up @@ -234,6 +240,18 @@ const Home: React.FC<RouteComponentProps> = () => {
<WhatsNewsList newsList={feed.whatsNewLinks} />
</div>
</div>
{warnAboutDotNet && (
<InstallDepModal
isOpen
learnMore={missingDotnetVersionError.linkAfterMessage.url}
learnMoreText={formatMessage('Learn more')}
link={missingDotnetVersionError.link.url}
linkText={formatMessage('Install .NET Core SDK')}
setIsOpen={setWarnAboutDotNet}
text={missingDotnetVersionError.message}
title={formatMessage('.NET required')}
/>
)}
</div>
);
};
Expand Down
5 changes: 5 additions & 0 deletions Composer/packages/client/src/recoilModel/atoms/appState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,8 @@ export const userHasNodeInstalledState = atom<boolean>({
key: getFullyQualifiedKey('userHasNodeInstalled'),
default: true,
});

export const warnAboutDotNetState = atom<boolean>({
key: getFullyQualifiedKey('warnAboutDotNetState'),
default: false,
});
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
projectMetaDataState,
selectedTemplateReadMeState,
showCreateQnAFromUrlDialogState,
warnAboutDotNetState,
} from '../atoms';
import { botRuntimeOperationsSelector, rootBotProjectIdSelector } from '../selectors';
import { mergePropertiesManagedByRootBot, postRootBotCreation } from '../../recoilModel/dispatchers/utils/project';
Expand Down Expand Up @@ -627,6 +628,10 @@ export const projectDispatcher = () => {
setError(callbackHelpers, error);
});

const setWarnAboutDotNet = useRecoilCallback((callbackHelpers: CallbackInterface) => (warn: boolean) => {
callbackHelpers.set(warnAboutDotNetState, warn);
});

return {
openProject,
createNewBot,
Expand All @@ -649,6 +654,7 @@ export const projectDispatcher = () => {
updateCreationMessage,
setCurrentProjectId,
setProjectError,
setWarnAboutDotNet,
fetchReadMe,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { CallbackInterface } from 'recoil';
import { v4 as uuid } from 'uuid';
import isEmpty from 'lodash/isEmpty';

import { checkIfDotnetVersionMissing } from '../../../utils/runtimeErrors';
import { BASEURL, BotStatus } from '../../../constants';
import settingStorage from '../../../utils/dialogSettingStorage';
import { getUniqueName } from '../../../utils/fileUtil';
Expand Down Expand Up @@ -73,6 +74,7 @@ import {
createQnAOnState,
botEndpointsState,
dispatcherState,
warnAboutDotNetState,
} from '../../atoms';
import * as botstates from '../../atoms/botState';
import lgWorker from '../../parsers/lgWorker';
Expand Down Expand Up @@ -377,8 +379,16 @@ export const fetchProjectDataById = async (projectId): Promise<{ botFiles: any;
}
};

export const handleProjectFailure = (callbackHelpers: CallbackInterface, ex) => {
setError(callbackHelpers, ex);
export const handleProjectFailure = (callbackHelpers: CallbackInterface, error) => {
const isDotnetError = checkIfDotnetVersionMissing({
message: error.response?.data?.message ?? error.message ?? '',
});

if (isDotnetError) {
callbackHelpers.set(warnAboutDotNetState, true);
} else {
setError(callbackHelpers, error);
Comment thread
benbrown marked this conversation as resolved.
}
};

export const processSchema = (projectId: string, schema: any) => ({
Expand Down