Skip to content

Commit

Permalink
Merge branch 'main' into fix/lg-multiline
Browse files Browse the repository at this point in the history
  • Loading branch information
boydc2014 committed Mar 12, 2021
2 parents ad9a980 + ad82bf1 commit 2d3df2f
Show file tree
Hide file tree
Showing 27 changed files with 423 additions and 321 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('ExternalAdapterSettings', () => {
initRecoilState
);

const link = getByText('Package Settings');
const link = getByText('the package manager');

expect(link.attributes.getNamedItem('href')?.value).toEqual('plugin/package-manager/package-manager');
});
Expand Down
2 changes: 1 addition & 1 deletion Composer/packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"re-resizable": "^6.9.0",
"react": "16.13.1",
"react-app-polyfill": "^0.2.1",
"react-dev-utils": "^7.0.3",
"react-dev-utils": "^11.0.4",
"react-dom": "16.13.1",
"react-frame-component": "^4.0.2",
"react-markdown": "^5.0.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import querystring from 'query-string';
import axios from 'axios';

import { DialogCreationCopy, EmptyBotTemplateId } from '../../constants';
import { creationFlowTypeState } from '../../recoilModel';
import { creationFlowTypeState, featureFlagsState } from '../../recoilModel';
import TelemetryClient from '../../telemetry/TelemetryClient';
import { getAliasFromPayload } from '../../utils/electronUtil';

Expand Down Expand Up @@ -129,6 +129,7 @@ export function CreateOptions(props: CreateOptionsProps) {
const [currentTemplate, setCurrentTemplate] = useState('');
const [emptyBotKey, setEmptyBotKey] = useState('');
const creationFlowType = useRecoilValue(creationFlowTypeState);
const featureFlags = useRecoilValue(featureFlagsState);

const selection = useMemo(() => {
return new Selection({
Expand Down Expand Up @@ -235,6 +236,12 @@ export function CreateOptions(props: CreateOptionsProps) {
return null;
};

useEffect(() => {
if (featureFlags.NEW_CREATION_FLOW?.enabled) {
navigate(`/v2/projects/create${props?.location?.search}`);
}
});

useEffect(() => {
if (templates.length > 1) {
const emptyBotTemplate = find(templates, ['id', EmptyBotTemplateId]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import DefineConversation from './DefineConversation';

type CreationFlowProps = RouteComponentProps<{}>;

const CreationFlow: React.FC<CreationFlowProps> = () => {
const CreationFlow: React.FC<CreationFlowProps> = (props: CreationFlowProps) => {
const {
fetchTemplates,
fetchTemplatesV2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ import {
import { BotTemplate, QnABotTemplateId } from '@bfc/shared';
import { DialogWrapper, DialogTypes, LoadingSpinner } from '@bfc/ui-shared';
import { NeutralColors } from '@uifabric/fluent-theme';
import { RouteComponentProps } from '@reach/router';
import { navigate, RouteComponentProps } from '@reach/router';
import { IPivotItemProps, Pivot, PivotItem } from 'office-ui-fabric-react/lib/Pivot';
import { Link } from 'office-ui-fabric-react/lib/Link';
import { FontIcon } from 'office-ui-fabric-react/lib/Icon';
import { csharpFeedKey } from '@botframework-composer/types';
import { useRecoilState, useRecoilValue } from 'recoil';
import axios from 'axios';
import querystring from 'query-string';

import msftIcon from '../../../images/msftIcon.svg';
import { DialogCreationCopy, EmptyBotTemplateId, feedDictionary } from '../../../constants';
import { fetchReadMePendingState, selectedTemplateReadMeState } from '../../../recoilModel';
import TelemetryClient from '../../../telemetry/TelemetryClient';
import { getAliasFromPayload } from '../../../utils/electronUtil';

import { TemplateDetailView } from './TemplateDetailView';

Expand Down Expand Up @@ -118,14 +121,15 @@ const defaultTemplateId = '@microsoft/generator-microsoft-bot-empty';
type CreateOptionsProps = {
templates: BotTemplate[];
onDismiss: () => void;
onNext: (data: string) => void;
onNext: (templateName: string, urlData?: string) => void;
fetchTemplates: (feedUrls?: string[]) => Promise<void>;
fetchReadMe: (moduleName: string) => {};
} & RouteComponentProps<{}>;

export function CreateOptionsV2(props: CreateOptionsProps) {
const [option] = useState(optionKeys.createFromTemplate);
const [disabled] = useState(false);
const [isOpen, setIsOpen] = useState(false);
const { templates, onDismiss, onNext } = props;
const [currentTemplateId, setCurrentTemplateId] = useState(defaultTemplateId);
const [emptyBotKey, setEmptyBotKey] = useState('');
Expand Down Expand Up @@ -154,13 +158,13 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
routeToTemplate = QnABotTemplateId;
}

if (props.location && props.location.search) {
routeToTemplate += props.location.search;
}

TelemetryClient.track('CreateNewBotProjectNextButton', { template: routeToTemplate });

onNext(routeToTemplate);
if (props.location && props.location.search) {
onNext(routeToTemplate, props.location.search);
} else {
onNext(routeToTemplate);
}
};

const tableColumns = [
Expand Down Expand Up @@ -213,6 +217,31 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
}
}, [templates]);

useEffect(() => {
// open bot directly if alias exist.
if (props.location?.search) {
const decoded = decodeURIComponent(props.location.search);
const { source, payload } = querystring.parse(decoded);
if (typeof source === 'string' && typeof payload === 'string') {
const alias = getAliasFromPayload(source, payload);
// check to see if Composer currently has a bot project corresponding to the alias
axios
.get<any>(`/api/projects/alias/${alias}`)
.then((aliasRes) => {
if (aliasRes.status === 200) {
navigate(`/bot/${aliasRes.data.id}`);
return;
}
})
.catch((e) => {
setIsOpen(true);
});
return;
}
}
setIsOpen(true);
}, [props.location?.search]);

useEffect(() => {
if (selectedFeed?.props?.itemKey) {
props.fetchTemplates([feedDictionary[selectedFeed.props.itemKey]]);
Expand All @@ -228,7 +257,7 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
return (
<Fragment>
<DialogWrapper
isOpen
isOpen={isOpen}
{...DialogCreationCopy.CREATE_NEW_BOT_V2}
dialogType={DialogTypes.CreateFlow}
onDismiss={onDismiss}
Expand Down Expand Up @@ -268,12 +297,7 @@ export function CreateOptionsV2(props: CreateOptionsProps) {
</div>
</div>
<DialogFooter>
<Link
underline
href={templateRequestUrl}
styles={{ root: { fontSize: '12px', float: 'left' } }}
target="_blank"
>
<Link href={templateRequestUrl} styles={{ root: { fontSize: '12px', float: 'left' } }} target="_blank">
<FontIcon iconName="ChatInviteFriend" style={{ marginRight: '5px' }} />
{formatMessage('Need another template? Send us a request')}
</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,10 @@ const CreationFlowV2: React.FC<CreationFlowProps> = () => {
templateDir: formData?.pvaData?.templateDir,
eTag: formData?.pvaData?.eTag,
urlSuffix: formData?.pvaData?.urlSuffix,
alias: formData?.pvaData?.alias,
preserveRoot: formData?.pvaData?.preserveRoot,
alias: formData?.alias,
profile: formData?.profile,
source: formData?.source,
};
TelemetryClient.track('CreateNewBotProjectStarted', { template: templateId });

Expand All @@ -146,9 +148,12 @@ const CreationFlowV2: React.FC<CreationFlowProps> = () => {
}
};

const handleCreateNext = async (data: string) => {
const handleCreateNext = async (templateName: string, urlData?: string) => {
setCreationFlowStatus(CreationFlowStatus.NEW_FROM_TEMPLATE);
navigate(`./create/${encodeURIComponent(data)}`);
const navString = urlData
? `./create/${encodeURIComponent(templateName)}${urlData}`
: `./create/${encodeURIComponent(templateName)}`;
navigate(navString);
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { createNotification } from '../../../recoilModel/dispatchers/notificatio
import { ImportSuccessNotificationWrapper } from '../../ImportModal/ImportSuccessNotification';
import { dispatcherState } from '../../../recoilModel';
import { LocationSelectContent } from '../LocationSelectContent';
import { getAliasFromPayload, Profile } from '../../../utils/electronUtil';

// -------------------- Styles -------------------- //

Expand Down Expand Up @@ -73,12 +74,14 @@ type DefineConversationFormData = {
runtimeChoice: string;
location?: string;
templateVersion?: string;
profile?: Profile; // abs payload to create bot
source?: string; // where the payload come from
alias?: string; // identifier that is used to track bots between imports

pvaData?: {
templateDir?: string; // location of the imported template
eTag?: string; // e tag used for content sync between composer and imported bot content
urlSuffix?: string; // url to deep link to after creation
alias?: string; // identifier that is used to track bots between imports
preserveRoot?: boolean; // identifier that is used to determine ay project file renames upon creation
};
};
Expand Down Expand Up @@ -250,9 +253,9 @@ const DefineConversationV2: React.FC<DefineConversationProps> = (props) => {
templateDir: templateDir,
eTag: eTag,
urlSuffix: urlSuffix,
alias: alias,
preserveRoot: true,
};
dataToSubmit.alias = alias;

// create a notification to indicate import success
const notification = createNotification({
Expand All @@ -266,6 +269,16 @@ const DefineConversationV2: React.FC<DefineConversationProps> = (props) => {
}
}

if (props.location?.search) {
const decoded = decodeURIComponent(props.location.search);
const { source, payload } = querystring.parse(decoded);
if (payload && typeof payload === 'string' && typeof source === 'string') {
dataToSubmit.profile = JSON.parse(payload);
dataToSubmit.source = source;
dataToSubmit.alias = getAliasFromPayload(source, payload);
}
}

onSubmit({ ...dataToSubmit }, templateId || '');
},
[hasErrors, formData]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ const AdapterModal = (props: Props) => {
<Text key="helptext">
{formatMessage.rich('To learn more about the { title }, <a>visit its documentation page</a>.', {
title: schema.title,
a: ({ children }) => <Link href={uiSchema.helpLink}>{children}</Link>,
a: ({ children }) => (
<Link href={uiSchema.helpLink} target="_blank">
{children}
</Link>
),
})}
</Text>
<DialogFooter>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const ExternalAdapterSettings = (props: Props) => {
const externalServices = (schemas: (JSONSchema7 & { key: string; packageName?: string; firstTime?: boolean })[]) => (
<div>
<div key={'subtitle'} css={subtitle}>
{formatMessage.rich('Install more adapters in <a>Package Settings</a>.', {
{formatMessage.rich('Install more adapters in <a>the package manager</a>.', {
a: ({ children }) => (
<Link key="link" href={packageManagerLink}>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const getActionName = (action, pluginConfig?: PluginConfig) => {
detectedActionName = nameFromAction;
} else {
const kind: string = action?.$kind as string;
const actionNameFromSchema = pluginConfig?.uiSchema?.[kind]?.form?.label as string | (() => string) | undefined;
const actionNameFromSchema = pluginConfig?.uiSchema?.[kind]?.form?.label;
if (typeof actionNameFromSchema === 'string') {
detectedActionName = actionNameFromSchema;
} else if (typeof actionNameFromSchema === 'function') {
Expand Down Expand Up @@ -145,7 +145,7 @@ const useBreadcrumbs = (projectId: string, pluginConfig?: PluginConfig) => {
b.label = getFriendlyName(get(currentDialog.content, `triggers[${name}]`));
break;
case BreadcrumbKeyPrefix.Action:
b.label = getActionName(get(currentDialog.content, name));
b.label = getActionName(get(currentDialog.content, name), pluginConfig);
break;
}
return b;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,21 @@ import { Locales } from '../../locales';

import { createNotification } from './notification';

export const downloadModel = (addr: string, model: 'en' | 'multilang') => {
export const downloadModel = (addr: string, model: 'en' | 'multilang', notificationStartCallback: () => void) => {
return new Promise<boolean>((resolve, reject) => {
httpClient.post(addr, { language: model }).then((resp) => {
if (resp.status === 201) {
resolve(true);
return;
}

if (resp.status !== 200) {
reject(false);
return;
}

notificationStartCallback();

const statusUri = resp.data;

const timer = setInterval(async () => {
Expand Down Expand Up @@ -74,11 +82,12 @@ export const orchestratorDispatcher = () => {
// Download Model Notification
const { addNotification, deleteNotification } = await snapshot.getPromise(dispatcherState);
const notification = createNotification(orchestratorDownloadNotificationProps());
addNotification(notification);

try {
for (const languageModel of availableLanguageModels(recognizers)) {
await downloadModel('/orchestrator/download', languageModel);
await downloadModel('/orchestrator/download', languageModel, () => {
addNotification(notification);
});
}
} finally {
deleteNotification(notification.id);
Expand Down
20 changes: 18 additions & 2 deletions Composer/packages/client/src/recoilModel/dispatchers/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,10 @@ export const projectDispatcher = () => {
urlSuffix,
alias,
preserveRoot,
profile,
source,
} = newProjectData;

// starts the creation process and stores the jobID in state for tracking
const response = await createNewBotFromTemplateV2(
callbackHelpers,
Expand All @@ -370,8 +373,9 @@ export const projectDispatcher = () => {
alias,
preserveRoot
);

if (response.data.jobId) {
dispatcher.updateCreationMessage(response.data.jobId, templateId, urlSuffix);
dispatcher.updateCreationMessage(response.data.jobId, templateId, urlSuffix, profile, source);
}
} catch (ex) {
set(botProjectIdsState, []);
Expand Down Expand Up @@ -483,7 +487,13 @@ export const projectDispatcher = () => {
});

const updateCreationMessage = useRecoilCallback(
(callbackHelpers: CallbackInterface) => async (jobId: string, templateId: string, urlSuffix: string) => {
(callbackHelpers: CallbackInterface) => async (
jobId: string,
templateId: string,
urlSuffix: string,
profile: any,
source: any
) => {
const timer = setInterval(async () => {
try {
const response = await httpClient.get(`/status/${jobId}`);
Expand All @@ -509,7 +519,13 @@ export const projectDispatcher = () => {
callbackHelpers.set(createQnAOnState, { projectId, dialogId: mainDialog });
callbackHelpers.set(showCreateQnAFromUrlDialogState(projectId), true);
}
if (profile) {
// ABS Create Flow, update publishProfile after create project
const dispatcher = await callbackHelpers.snapshot.getPromise(dispatcherState);
const newProfile = getPublishProfileFromPayload(profile, source);

newProfile && dispatcher.setPublishTargets([newProfile], projectId);
}
projectIdCache.set(projectId);

// navigate to the new get started section
Expand Down
Loading

0 comments on commit 2d3df2f

Please sign in to comment.