From 27fddbb95dab3dc345c6867b5c414a8e055f8cef Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Fri, 5 Mar 2021 16:15:52 +0800 Subject: [PATCH 01/10] save name and target state --- .../ProfileFormDialog.tsx | 21 ++++++++++++++++--- .../PublishProfileDialog.tsx | 7 +++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/Composer/packages/client/src/pages/botProject/create-publish-profile/ProfileFormDialog.tsx b/Composer/packages/client/src/pages/botProject/create-publish-profile/ProfileFormDialog.tsx index 69351c8b63..12321fbcd9 100644 --- a/Composer/packages/client/src/pages/botProject/create-publish-profile/ProfileFormDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/create-publish-profile/ProfileFormDialog.tsx @@ -32,6 +32,10 @@ type ProfileFormDialogProps = { updateSettings: (name: string, type: string, configuration: string) => Promise; projectId: string; setType: (value) => void; + name: string; + targetType: string; + setName: (value: string) => void; + setTargetType: (value: string) => void; current?: { index: number; item: PublishTarget } | null; }; const labelContainer = css` @@ -72,10 +76,21 @@ const onRenderLabel = (props) => { }; export const ProfileFormDialog: React.FC = (props) => { - const { onDismiss, targets, types, onNext, updateSettings, projectId, setType, current } = props; - const [name, setName] = useState(current?.item.name || ''); + const { + name, + setName, + targetType, + setTargetType, + onDismiss, + targets, + types, + onNext, + updateSettings, + projectId, + setType, + current, + } = props; const [errorMessage, setErrorMsg] = useState(''); - const [targetType, setTargetType] = useState(current?.item.type || ''); const { provisionToTarget } = useRecoilValue(dispatcherState); const updateName = (e, newName) => { diff --git a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx index d90430a80b..0d13612a8d 100644 --- a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx @@ -38,6 +38,9 @@ const Page = { export const PublishProfileDialog: React.FC = (props) => { const { current, types, projectId, closeDialog, targets, setPublishTargets } = props; + const [name, setName] = useState(current?.item.name || ''); + const [targetType, setTargetType] = useState(current?.item.type || ''); + const [page, setPage] = useState(Page.ProfileForm); const [publishSurfaceStyles, setStyles] = useState(defaultPublishSurface); const { provisionToTarget } = useRecoilValue(dispatcherState); @@ -176,9 +179,13 @@ export const PublishProfileDialog: React.FC = (props) Date: Mon, 8 Mar 2021 17:22:37 +0800 Subject: [PATCH 02/10] save extension status --- .../ProfileFormDialog.tsx | 56 +------------ .../PublishProfileDialog.tsx | 78 ++++++++++++------- Composer/packages/client/src/plugins/api.ts | 4 + .../src/hooks/usePublishApi.ts | 9 ++- .../src/components/azureProvisionDialog.tsx | 44 ++++++++--- 5 files changed, 96 insertions(+), 95 deletions(-) diff --git a/Composer/packages/client/src/pages/botProject/create-publish-profile/ProfileFormDialog.tsx b/Composer/packages/client/src/pages/botProject/create-publish-profile/ProfileFormDialog.tsx index 12321fbcd9..0844fc1c33 100644 --- a/Composer/packages/client/src/pages/botProject/create-publish-profile/ProfileFormDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/create-publish-profile/ProfileFormDialog.tsx @@ -6,8 +6,7 @@ import { jsx, css } from '@emotion/core'; import formatMessage from 'format-message'; import { SharedColors } from '@uifabric/fluent-theme'; import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; -import { useState, useMemo, useCallback, Fragment, useEffect } from 'react'; -import { useRecoilValue } from 'recoil'; +import { useState, useMemo, useCallback, Fragment } from 'react'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; @@ -17,20 +16,13 @@ import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; import { Icon } from 'office-ui-fabric-react/lib/Icon'; import { separator } from '../../publish/styles'; -import { armScopes, graphScopes } from '../../../constants'; import { PublishType } from '../../../recoilModel/types'; -import { PluginAPI } from '../../../plugins/api'; -import { dispatcherState } from '../../../recoilModel'; -import { AuthClient } from '../../../utils/authClient'; -import { getTokenFromCache, isGetTokenFromUser } from '../../../utils/auth'; type ProfileFormDialogProps = { onDismiss: () => void; targets: PublishTarget[]; types: PublishType[]; onNext: () => void; - updateSettings: (name: string, type: string, configuration: string) => Promise; - projectId: string; setType: (value) => void; name: string; targetType: string; @@ -76,22 +68,8 @@ const onRenderLabel = (props) => { }; export const ProfileFormDialog: React.FC = (props) => { - const { - name, - setName, - targetType, - setTargetType, - onDismiss, - targets, - types, - onNext, - updateSettings, - projectId, - setType, - current, - } = props; + const { name, setName, targetType, setTargetType, onDismiss, targets, types, onNext, setType, current } = props; const [errorMessage, setErrorMsg] = useState(''); - const { provisionToTarget } = useRecoilValue(dispatcherState); const updateName = (e, newName) => { setName(newName); @@ -131,36 +109,6 @@ export const ProfileFormDialog: React.FC = (props) => { return !targetType || !name || !!errorMessage; }, [errorMessage, name, targetType]); - // pass functions to extensions - useEffect(() => { - PluginAPI.publish.getType = () => { - return targetType; - }; - PluginAPI.publish.getSchema = () => { - return types.find((t) => t.name === targetType)?.schema; - }; - PluginAPI.publish.savePublishConfig = (config) => { - updateSettings(name, targetType, JSON.stringify(config) || '{}'); - }; - }, [targetType, name, types, updateSettings]); - - useEffect(() => { - PluginAPI.publish.startProvision = async (config) => { - const fullConfig = { ...config, name: name, type: targetType }; - let arm, graph; - if (!isGetTokenFromUser()) { - // login or get token implicit - arm = await AuthClient.getAccessToken(armScopes); - graph = await AuthClient.getAccessToken(graphScopes); - } else { - // get token from cache - arm = getTokenFromCache('accessToken'); - graph = getTokenFromCache('graphToken'); - } - provisionToTarget(fullConfig, config.type, projectId, arm, graph, current?.item); - }; - }, [name, targetType]); - return ( diff --git a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx index 0d13612a8d..b3150a7f93 100644 --- a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx @@ -31,6 +31,19 @@ type PublishProfileDialogProps = { setPublishTargets: (targets: PublishTarget[], projectId: string) => Promise; }; +type ExtensionState = AzureExtensionState; // can expand to pva like | PVAExtensionState + +type AzureExtensionState = { + subscriptionId: string; + resourceGroup: string; + hostName: string; + location: string; + luisLocation: string; + enableResources: any; + requireResources: any; + choice: any; +}; + const Page = { ProfileForm: Symbol('form'), ConfigProvision: Symbol('config'), @@ -40,6 +53,7 @@ export const PublishProfileDialog: React.FC = (props) const { current, types, projectId, closeDialog, targets, setPublishTargets } = props; const [name, setName] = useState(current?.item.name || ''); const [targetType, setTargetType] = useState(current?.item.type || ''); + const [extensionState, setExtensionState] = useState(); const [page, setPage] = useState(Page.ProfileForm); const [publishSurfaceStyles, setStyles] = useState(defaultPublishSurface); @@ -96,6 +110,9 @@ export const PublishProfileDialog: React.FC = (props) PluginAPI.publish.setTitle = (value) => { setTitle(value); }; + PluginAPI.publish.setExtensionState = (value) => { + setExtensionState(value); + }; }, []); // setup plugin APIs so that the provisioning plugin can initiate the process from inside the iframe @@ -104,12 +121,15 @@ export const PublishProfileDialog: React.FC = (props) PluginAPI.publish.currentProjectId = () => { return projectId; }; + PluginAPI.publish.getExtensionState = () => { + return extensionState; + }; if (current?.item.type) { setPage(Page.ConfigProvision); } else { setPage(Page.ProfileForm); } - }, [current, projectId]); + }, [current, projectId, extensionState]); const savePublishTarget = useCallback( async (name: string, type: string, configuration: string) => { @@ -128,32 +148,34 @@ export const PublishProfileDialog: React.FC = (props) ); useEffect(() => { - if (current?.item?.type) { - PluginAPI.publish.getType = () => { - return current?.item?.type; - }; - PluginAPI.publish.getSchema = () => { - return types.find((t) => t.name === current?.item?.type)?.schema; - }; - PluginAPI.publish.savePublishConfig = (config) => { - savePublishTarget(current?.item.name, current?.item?.type, JSON.stringify(config) || '{}'); - }; - PluginAPI.publish.startProvision = async (config) => { - const fullConfig = { ...config, name: current.item.name, type: current.item.type }; - let arm, graph; - if (!isGetTokenFromUser()) { - // login or get token implicit - arm = await AuthClient.getAccessToken(armScopes); - graph = await AuthClient.getAccessToken(graphScopes); - } else { - // get token from cache - arm = getTokenFromCache('accessToken'); - graph = getTokenFromCache('graphToken'); - } - provisionToTarget(fullConfig, config.type, projectId, arm, graph, current?.item); - }; - } - }, [current, types, savePublishTarget]); + PluginAPI.publish.startProvision = async (config) => { + const fullConfig = { ...config, name: name, type: targetType }; + let arm, graph; + if (!isGetTokenFromUser()) { + // login or get token implicit + arm = await AuthClient.getAccessToken(armScopes); + graph = await AuthClient.getAccessToken(graphScopes); + } else { + // get token from cache + arm = getTokenFromCache('accessToken'); + graph = getTokenFromCache('graphToken'); + } + provisionToTarget(fullConfig, config.type, projectId, arm, graph, current?.item); + }; + }, [name, targetType, projectId]); + + // pass functions to extensions + useEffect(() => { + PluginAPI.publish.getType = () => { + return targetType; + }; + PluginAPI.publish.getSchema = () => { + return types.find((t) => t.name === targetType)?.schema; + }; + PluginAPI.publish.savePublishConfig = (config) => { + savePublishTarget(name, targetType, JSON.stringify(config) || '{}'); + }; + }, [targetType, name, types, savePublishTarget]); return ( @@ -180,14 +202,12 @@ export const PublishProfileDialog: React.FC = (props) { setPage(Page.ConfigProvision); diff --git a/Composer/packages/client/src/plugins/api.ts b/Composer/packages/client/src/plugins/api.ts index ece74de137..b8bca30a2e 100644 --- a/Composer/packages/client/src/plugins/api.ts +++ b/Composer/packages/client/src/plugins/api.ts @@ -35,6 +35,8 @@ interface PublishAPI { savePublishConfig?: (config: PublishConfig) => void; getTokenFromCache?: () => { accessToken: string; graphToken: string }; isGetTokenFromUser?: () => boolean; + getExtensionState?: () => any; + setExtensionState?: (value: any) => void; } class API implements IAPI { @@ -59,6 +61,8 @@ class API implements IAPI { savePublishConfig: undefined, getTokenFromCache: undefined, isGetTokenFromUser: undefined, + getExtensionState: undefined, + setExtensionState: undefined, }; } } diff --git a/Composer/packages/extension-client/src/hooks/usePublishApi.ts b/Composer/packages/extension-client/src/hooks/usePublishApi.ts index 45cbc9e09d..e5309c88d3 100644 --- a/Composer/packages/extension-client/src/hooks/usePublishApi.ts +++ b/Composer/packages/extension-client/src/hooks/usePublishApi.ts @@ -49,7 +49,12 @@ export function usePublishApi() { function isGetTokenFromUser(): boolean { return window[ComposerGlobalName].isGetTokenFromUser(); } - + function getExtensionState(): any { + return window[ComposerGlobalName].getExtensionState(); + } + function setExtensionState(value: any) { + return window[ComposerGlobalName].setExtensionState(value); + } return { publishConfig: getPublishConfig(), startProvision, @@ -62,5 +67,7 @@ export function usePublishApi() { savePublishConfig, getTokenFromCache, isGetTokenFromUser, + getExtensionState, + setExtensionState, }; } diff --git a/extensions/azurePublish/src/components/azureProvisionDialog.tsx b/extensions/azurePublish/src/components/azureProvisionDialog.tsx index 6ae9bc881c..f5d0f87029 100644 --- a/extensions/azurePublish/src/components/azureProvisionDialog.tsx +++ b/extensions/azurePublish/src/components/azureProvisionDialog.tsx @@ -38,7 +38,6 @@ import { AzureResourceTypes, ResourcesItem, authConfig } from '../types'; import { getResourceList, getSubscriptions, - getResourceGroups, getDeployLocations, getPreview, getLuisAuthoringRegions, @@ -306,10 +305,14 @@ export const AzureProvisionDialog: React.FC = () => { getType, getTokenFromCache, isGetTokenFromUser, + setExtensionState, + getExtensionState, } = usePublishApi(); // set type of publish - azurePublish or azureFunctionsPublish const publishType = getType(); const currentConfig = removePlaceholder(publishConfig); + const extensionState = getExtensionState(); + console.log(extensionState); const [subscriptions, setSubscriptions] = useState([]); const [deployLocations, setDeployLocations] = useState([]); @@ -318,17 +321,19 @@ export const AzureProvisionDialog: React.FC = () => { const [token, setToken] = useState(); const [currentUser, setCurrentUser] = useState(); - const [choice, setChoice] = useState(choiceOptions[0]); - const [currentSubscription, setSubscription] = useState(''); - const [currentResourceGroup, setResourceGroup] = useState(''); - const [currentHostName, setHostName] = useState(''); + const [choice, setChoice] = useState(extensionState?.choice || choiceOptions[0]); + const [currentSubscription, setSubscription] = useState(extensionState?.subscriptionId || ''); + const [currentResourceGroup, setResourceGroup] = useState(extensionState?.resourceGroup || ''); + const [currentHostName, setHostName] = useState(extensionState?.hostName || ''); const [errorHostName, setErrorHostName] = useState(''); const [errorResourceGroupName, setErrorResourceGroupName] = useState(''); - const [currentLocation, setLocation] = useState(currentConfig?.region); - const [currentLuisLocation, setCurrentLuisLocation] = useState(currentConfig?.settings?.luis?.region); + const [currentLocation, setLocation] = useState(extensionState?.location || currentConfig?.region); + const [currentLuisLocation, setCurrentLuisLocation] = useState( + extensionState?.luisLocation || currentConfig?.settings?.luis?.region + ); const [extensionResourceOptions, setExtensionResourceOptions] = useState([]); - const [enabledResources, setEnabledResources] = useState([]); // create from optional list - const [requireResources, setRequireResources] = useState([]); + const [enabledResources, setEnabledResources] = useState(extensionState?.enableResources || []); // create from optional list + const [requireResources, setRequireResources] = useState(extensionState?.requireResources || []); const [isEditorError, setEditorError] = useState(false); const [importConfig, setImportConfig] = useState(); @@ -624,7 +629,7 @@ export const AzureProvisionDialog: React.FC = () => { const PageFormConfig = ( - + {subscriptionOption?.length > 0 && choice.key === 'create' && (
{ /> ) : null}
- + { + // setExtensionState + setExtensionState({ + subscriptionId: currentSubscription, + resourceGroup: currentResourceGroup, + hostName: currentHostName, + location: currentLocation, + luisLocation: currentLuisLocation, + enableResources: enabledResources, + requireResources: requireResources, + choice: choice, + }); + onBack(); + }} + /> {choice.key === 'create' ? ( Date: Mon, 8 Mar 2021 23:23:38 +0800 Subject: [PATCH 03/10] remove annotation --- extensions/azurePublish/src/components/azureProvisionDialog.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/azurePublish/src/components/azureProvisionDialog.tsx b/extensions/azurePublish/src/components/azureProvisionDialog.tsx index f5d0f87029..6b8e15fe15 100644 --- a/extensions/azurePublish/src/components/azureProvisionDialog.tsx +++ b/extensions/azurePublish/src/components/azureProvisionDialog.tsx @@ -312,7 +312,6 @@ export const AzureProvisionDialog: React.FC = () => { const publishType = getType(); const currentConfig = removePlaceholder(publishConfig); const extensionState = getExtensionState(); - console.log(extensionState); const [subscriptions, setSubscriptions] = useState([]); const [deployLocations, setDeployLocations] = useState([]); From 62efde6f5c1bb9d4e238cc4ad0f2e82cb931215f Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Tue, 9 Mar 2021 18:58:23 +0800 Subject: [PATCH 04/10] fix comments --- .../PublishProfileDialog.tsx | 26 +++++++++----- Composer/packages/client/src/plugins/api.ts | 4 +-- .../src/components/azureProvisionDialog.tsx | 36 ++++++++++++------- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx index b3150a7f93..064783fe50 100644 --- a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx @@ -8,6 +8,7 @@ import { PublishTarget } from '@bfc/shared'; import formatMessage from 'format-message'; import { Dialog } from 'office-ui-fabric-react/lib/Dialog'; import { Link } from 'office-ui-fabric-react/lib/Link'; +import { IChoiceGroupOption } from 'office-ui-fabric-react/lib/ChoiceGroup'; import { useRecoilValue } from 'recoil'; import { getTokenFromCache, isGetTokenFromUser } from '../../../utils/auth'; @@ -30,18 +31,25 @@ type PublishProfileDialogProps = { projectId: string; setPublishTargets: (targets: PublishTarget[], projectId: string) => Promise; }; - +type ResourcesItem = { + description: string; + text: string; + tier: string; + group: string; + key: string; + required: boolean; + [key: string]: any; +}; type ExtensionState = AzureExtensionState; // can expand to pva like | PVAExtensionState - type AzureExtensionState = { subscriptionId: string; resourceGroup: string; hostName: string; location: string; luisLocation: string; - enableResources: any; - requireResources: any; - choice: any; + enabledResources: ResourcesItem[]; + requiredResources: ResourcesItem[]; + choice: IChoiceGroupOption; }; const Page = { @@ -110,8 +118,8 @@ export const PublishProfileDialog: React.FC = (props) PluginAPI.publish.setTitle = (value) => { setTitle(value); }; - PluginAPI.publish.setExtensionState = (value) => { - setExtensionState(value); + PluginAPI.publish.setExtensionState = (value: T): void => { + setExtensionState(value as any); }; }, []); @@ -121,8 +129,8 @@ export const PublishProfileDialog: React.FC = (props) PluginAPI.publish.currentProjectId = () => { return projectId; }; - PluginAPI.publish.getExtensionState = () => { - return extensionState; + PluginAPI.publish.getExtensionState = (): T | undefined => { + return extensionState as T | undefined; }; if (current?.item.type) { setPage(Page.ConfigProvision); diff --git a/Composer/packages/client/src/plugins/api.ts b/Composer/packages/client/src/plugins/api.ts index b8bca30a2e..16cd6cc3e1 100644 --- a/Composer/packages/client/src/plugins/api.ts +++ b/Composer/packages/client/src/plugins/api.ts @@ -35,8 +35,8 @@ interface PublishAPI { savePublishConfig?: (config: PublishConfig) => void; getTokenFromCache?: () => { accessToken: string; graphToken: string }; isGetTokenFromUser?: () => boolean; - getExtensionState?: () => any; - setExtensionState?: (value: any) => void; + getExtensionState?: () => T | undefined; + setExtensionState?: (value: T) => void; } class API implements IAPI { diff --git a/extensions/azurePublish/src/components/azureProvisionDialog.tsx b/extensions/azurePublish/src/components/azureProvisionDialog.tsx index 6b8e15fe15..1f86d77263 100644 --- a/extensions/azurePublish/src/components/azureProvisionDialog.tsx +++ b/extensions/azurePublish/src/components/azureProvisionDialog.tsx @@ -292,6 +292,16 @@ const reviewCols: IColumn[] = [ isPadded: true, }, ]; +const defaultExtensionState = { + subscriptionId: '', + resourceGroup: '', + hostName: '', + location: '', + luisLocation: '', + enabledResources: [], + requiredResources: [], + choice: choiceOptions[0], +}; export const AzureProvisionDialog: React.FC = () => { const { currentProjectId, @@ -311,7 +321,7 @@ export const AzureProvisionDialog: React.FC = () => { // set type of publish - azurePublish or azureFunctionsPublish const publishType = getType(); const currentConfig = removePlaceholder(publishConfig); - const extensionState = getExtensionState(); + const extensionState = { ...defaultExtensionState, ...getExtensionState() }; const [subscriptions, setSubscriptions] = useState([]); const [deployLocations, setDeployLocations] = useState([]); @@ -320,19 +330,19 @@ export const AzureProvisionDialog: React.FC = () => { const [token, setToken] = useState(); const [currentUser, setCurrentUser] = useState(); - const [choice, setChoice] = useState(extensionState?.choice || choiceOptions[0]); - const [currentSubscription, setSubscription] = useState(extensionState?.subscriptionId || ''); - const [currentResourceGroup, setResourceGroup] = useState(extensionState?.resourceGroup || ''); - const [currentHostName, setHostName] = useState(extensionState?.hostName || ''); + const [choice, setChoice] = useState(extensionState.choice); + const [currentSubscription, setSubscription] = useState(extensionState.subscriptionId); + const [currentResourceGroup, setResourceGroup] = useState(extensionState.resourceGroup); + const [currentHostName, setHostName] = useState(extensionState.hostName); const [errorHostName, setErrorHostName] = useState(''); const [errorResourceGroupName, setErrorResourceGroupName] = useState(''); - const [currentLocation, setLocation] = useState(extensionState?.location || currentConfig?.region); + const [currentLocation, setLocation] = useState(extensionState.location || currentConfig?.region); const [currentLuisLocation, setCurrentLuisLocation] = useState( - extensionState?.luisLocation || currentConfig?.settings?.luis?.region + extensionState.luisLocation || currentConfig?.settings?.luis?.region ); const [extensionResourceOptions, setExtensionResourceOptions] = useState([]); - const [enabledResources, setEnabledResources] = useState(extensionState?.enableResources || []); // create from optional list - const [requireResources, setRequireResources] = useState(extensionState?.requireResources || []); + const [enabledResources, setEnabledResources] = useState(extensionState.enabledResources); // create from optional list + const [requireResources, setRequireResources] = useState(extensionState.requiredResources); const [isEditorError, setEditorError] = useState(false); const [importConfig, setImportConfig] = useState(); @@ -628,7 +638,7 @@ export const AzureProvisionDialog: React.FC = () => { const PageFormConfig = ( - + {subscriptionOption?.length > 0 && choice.key === 'create' && ( {
{ // setExtensionState setExtensionState({ @@ -829,8 +839,8 @@ export const AzureProvisionDialog: React.FC = () => { hostName: currentHostName, location: currentLocation, luisLocation: currentLuisLocation, - enableResources: enabledResources, - requireResources: requireResources, + enabledResources: enabledResources, + requiredResources: requireResources, choice: choice, }); onBack(); From 025cb972f5fcb5cb154ae7d160c220e401ed6efc Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 11 Mar 2021 18:43:23 +0800 Subject: [PATCH 05/10] use localStorage instead of setExtensionState to parent component to save state --- .../PublishProfileDialog.tsx | 100 +++++++----------- Composer/packages/client/src/plugins/api.ts | 4 - .../src/hooks/usePublishApi.ts | 8 -- .../src/components/azureProvisionDialog.tsx | 33 +++--- .../azurePublish/src/components/util.ts | 49 +++++++++ 5 files changed, 102 insertions(+), 92 deletions(-) diff --git a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx index 67ff07a51b..f746b14701 100644 --- a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx @@ -8,7 +8,6 @@ import { PublishTarget } from '@bfc/shared'; import formatMessage from 'format-message'; import { Dialog } from 'office-ui-fabric-react/lib/Dialog'; import { Link } from 'office-ui-fabric-react/lib/Link'; -import { IChoiceGroupOption } from 'office-ui-fabric-react/lib/ChoiceGroup'; import { useRecoilValue } from 'recoil'; import { getTokenFromCache, isGetTokenFromUser, setTenantId, getTenantIdFromCache } from '../../../utils/auth'; @@ -31,26 +30,6 @@ type PublishProfileDialogProps = { projectId: string; setPublishTargets: (targets: PublishTarget[], projectId: string) => Promise; }; -type ResourcesItem = { - description: string; - text: string; - tier: string; - group: string; - key: string; - required: boolean; - [key: string]: any; -}; -type ExtensionState = AzureExtensionState; // can expand to pva like | PVAExtensionState -type AzureExtensionState = { - subscriptionId: string; - resourceGroup: string; - hostName: string; - location: string; - luisLocation: string; - enabledResources: ResourcesItem[]; - requiredResources: ResourcesItem[]; - choice: IChoiceGroupOption; -}; const Page = { ProfileForm: Symbol('form'), @@ -61,7 +40,6 @@ export const PublishProfileDialog: React.FC = (props) const { current, types, projectId, closeDialog, targets, setPublishTargets } = props; const [name, setName] = useState(current?.item.name || ''); const [targetType, setTargetType] = useState(current?.item.type || ''); - const [extensionState, setExtensionState] = useState(); const [page, setPage] = useState(Page.ProfileForm); const [publishSurfaceStyles, setStyles] = useState(defaultPublishSurface); @@ -98,7 +76,9 @@ export const PublishProfileDialog: React.FC = (props) // setup plugin APIs useEffect(() => { - PluginAPI.publish.closeDialog = closeDialog; + PluginAPI.publish.closeDialog = () => { + closeDialog(); + }; PluginAPI.publish.onBack = () => { setPage(Page.ProfileForm); setTitle({ @@ -118,9 +98,6 @@ export const PublishProfileDialog: React.FC = (props) PluginAPI.publish.setTitle = (value) => { setTitle(value); }; - PluginAPI.publish.setExtensionState = (value: T): void => { - setExtensionState(value as any); - }; PluginAPI.publish.getTenantIdFromCache = () => { return getTenantIdFromCache(); }; @@ -135,15 +112,12 @@ export const PublishProfileDialog: React.FC = (props) PluginAPI.publish.currentProjectId = () => { return projectId; }; - PluginAPI.publish.getExtensionState = (): T | undefined => { - return extensionState as T | undefined; - }; if (current?.item.type) { setPage(Page.ConfigProvision); } else { setPage(Page.ProfileForm); } - }, [current, projectId, extensionState]); + }, [current, projectId]); const savePublishTarget = useCallback( async (name: string, type: string, configuration: string) => { @@ -162,38 +136,36 @@ export const PublishProfileDialog: React.FC = (props) ); useEffect(() => { - if (current?.item?.type) { - PluginAPI.publish.getType = () => { - return current?.item?.type; - }; - PluginAPI.publish.getSchema = () => { - return types.find((t) => t.name === current?.item?.type)?.schema; - }; - PluginAPI.publish.savePublishConfig = (config) => { - savePublishTarget(current?.item.name, current?.item?.type, JSON.stringify(config) || '{}'); - }; - PluginAPI.publish.startProvision = async (config) => { - const fullConfig = { ...config, name: current.item.name, type: current.item.type }; - let arm, graph; - if (!isGetTokenFromUser()) { - // login or get token implicit - let tenantId = getTenantIdFromCache(); - if (!tenantId) { - const tenants = await AuthClient.getTenants(); - tenantId = tenants?.[0]?.tenantId; - setTenantId(tenantId); - } - arm = await AuthClient.getARMTokenForTenant(tenantId); - graph = await AuthClient.getAccessToken(graphScopes); - } else { - // get token from cache - arm = getTokenFromCache('accessToken'); - graph = getTokenFromCache('graphToken'); + PluginAPI.publish.getType = () => { + return targetType; + }; + PluginAPI.publish.getSchema = () => { + return types.find((t) => t.name === targetType)?.schema; + }; + PluginAPI.publish.savePublishConfig = (config) => { + savePublishTarget(name, targetType, JSON.stringify(config) || '{}'); + }; + PluginAPI.publish.startProvision = async (config) => { + const fullConfig = { ...config, name: name, type: targetType }; + let arm, graph; + if (!isGetTokenFromUser()) { + // login or get token implicit + let tenantId = getTenantIdFromCache(); + if (!tenantId) { + const tenants = await AuthClient.getTenants(); + tenantId = tenants?.[0]?.tenantId; + setTenantId(tenantId); } - provisionToTarget(fullConfig, config.type, projectId, arm, graph, current?.item); - }; - } - }, [current, types, savePublishTarget]); + arm = await AuthClient.getARMTokenForTenant(tenantId); + graph = await AuthClient.getAccessToken(graphScopes); + } else { + // get token from cache + arm = getTokenFromCache('accessToken'); + graph = getTokenFromCache('graphToken'); + } + provisionToTarget(fullConfig, config.type, projectId, arm, graph, current?.item); + }; + }, [name, targetType, types, savePublishTarget]); return ( @@ -207,7 +179,11 @@ export const PublishProfileDialog: React.FC = (props) modalProps={{ isBlocking: true, }} - onDismiss={closeDialog} + onDismiss={() => { + closeDialog(); + // remove extension state when parent component destroy + window.localStorage.removeItem(`${targetType}:state`); + }} > {page !== Page.ConfigProvision && (
diff --git a/Composer/packages/client/src/plugins/api.ts b/Composer/packages/client/src/plugins/api.ts index 00dbc43305..66c09b0e69 100644 --- a/Composer/packages/client/src/plugins/api.ts +++ b/Composer/packages/client/src/plugins/api.ts @@ -35,8 +35,6 @@ interface PublishAPI { savePublishConfig?: (config: PublishConfig) => void; getTokenFromCache?: () => { accessToken: string; graphToken: string }; isGetTokenFromUser?: () => boolean; - getExtensionState?: () => T | undefined; - setExtensionState?: (value: T) => void; getTenantIdFromCache?: () => string; setTenantId?: (value: string) => void; } @@ -63,8 +61,6 @@ class API implements IAPI { savePublishConfig: undefined, getTokenFromCache: undefined, isGetTokenFromUser: undefined, - getExtensionState: undefined, - setExtensionState: undefined, getTenantIdFromCache: undefined, setTenantId: undefined, }; diff --git a/Composer/packages/extension-client/src/hooks/usePublishApi.ts b/Composer/packages/extension-client/src/hooks/usePublishApi.ts index a549be2b11..a3e1a0b71f 100644 --- a/Composer/packages/extension-client/src/hooks/usePublishApi.ts +++ b/Composer/packages/extension-client/src/hooks/usePublishApi.ts @@ -55,12 +55,6 @@ export function usePublishApi() { function isGetTokenFromUser(): boolean { return window[ComposerGlobalName].isGetTokenFromUser(); } - function getExtensionState(): any { - return window[ComposerGlobalName].getExtensionState(); - } - function setExtensionState(value: any) { - return window[ComposerGlobalName].setExtensionState(value); - } return { publishConfig: getPublishConfig(), startProvision, @@ -73,8 +67,6 @@ export function usePublishApi() { savePublishConfig, getTokenFromCache, isGetTokenFromUser, - getExtensionState, - setExtensionState, getTenantIdFromCache, setTenantId, }; diff --git a/extensions/azurePublish/src/components/azureProvisionDialog.tsx b/extensions/azurePublish/src/components/azureProvisionDialog.tsx index 1fdcb10d10..3ad28e8689 100644 --- a/extensions/azurePublish/src/components/azureProvisionDialog.tsx +++ b/extensions/azurePublish/src/components/azureProvisionDialog.tsx @@ -42,7 +42,14 @@ import { getLuisAuthoringRegions, CheckWebAppNameAvailability, } from './api'; -import { getExistResources, removePlaceholder, decodeToken } from './util'; +import { + getExistResources, + removePlaceholder, + decodeToken, + getExtensionState, + setExtensionState, + clearExtensionState, +} from './util'; const iconStyle = (required) => { return { @@ -235,16 +242,7 @@ const reviewCols: IColumn[] = [ isPadded: true, }, ]; -const defaultExtensionState = { - subscriptionId: '', - resourceGroup: '', - hostName: '', - location: '', - luisLocation: '', - enabledResources: [], - requiredResources: [], - choice: choiceOptions[0], -}; + export const AzureProvisionDialog: React.FC = () => { const { currentProjectId, @@ -258,15 +256,13 @@ export const AzureProvisionDialog: React.FC = () => { getType, getTokenFromCache, isGetTokenFromUser, - setExtensionState, - getExtensionState, getTenantIdFromCache, setTenantId, } = usePublishApi(); // set type of publish - azurePublish or azureFunctionsPublish const publishType = getType(); const currentConfig = removePlaceholder(publishConfig); - const extensionState = { ...defaultExtensionState, ...getExtensionState() }; + const extensionState = getExtensionState(); const [subscriptions, setSubscriptions] = useState(); const [deployLocations, setDeployLocations] = useState([]); @@ -282,9 +278,9 @@ export const AzureProvisionDialog: React.FC = () => { const [currentHostName, setHostName] = useState(extensionState.hostName); const [errorHostName, setErrorHostName] = useState(''); const [errorResourceGroupName, setErrorResourceGroupName] = useState(''); - const [currentLocation, setLocation] = useState(extensionState.location || currentConfig?.region); + const [currentLocation, setLocation] = useState(currentConfig?.region || extensionState.location); const [currentLuisLocation, setCurrentLuisLocation] = useState( - extensionState.luisLocation || currentConfig?.settings?.luis?.region + currentConfig?.settings?.luis?.region || extensionState.luisLocation ); const [extensionResourceOptions, setExtensionResourceOptions] = useState([]); const [enabledResources, setEnabledResources] = useState(extensionState.enabledResources); // create from optional list @@ -560,14 +556,16 @@ export const AzureProvisionDialog: React.FC = () => { // call back to the main Composer API to begin this process... startProvision(options); closeDialog(); + clearExtensionState(); }, [] ); const onSave = useMemo( - () => () => { + () => async () => { savePublishConfig(importConfig); closeDialog(); + clearExtensionState(); }, [importConfig] ); @@ -823,7 +821,6 @@ export const AzureProvisionDialog: React.FC = () => { style={{ margin: '0 4px' }} text={formatMessage('Back')} onClick={() => { - // setExtensionState setExtensionState({ subscriptionId: currentSubscription, resourceGroup: currentResourceGroup, diff --git a/extensions/azurePublish/src/components/util.ts b/extensions/azurePublish/src/components/util.ts index 5bcaee9313..373aebae07 100644 --- a/extensions/azurePublish/src/components/util.ts +++ b/extensions/azurePublish/src/components/util.ts @@ -60,3 +60,52 @@ export const getExistResources = (config) => { return result; } else return []; }; + +const defaultExtensionState = { + subscriptionId: '', + resourceGroup: '', + hostName: '', + location: '', + luisLocation: '', + enabledResources: [], + requiredResources: [], + choice: { key: 'create', text: 'Create new Azure resources' }, +}; + +type ResourcesItem = { + description: string; + text: string; + tier: string; + group: string; + key: string; + required: boolean; + [key: string]: any; +}; + +type AzureExtensionState = { + subscriptionId: string; + resourceGroup: string; + hostName: string; + location: string; + luisLocation: string; + enabledResources: ResourcesItem[]; + requiredResources: ResourcesItem[]; + choice: { key: string; text: string }; +}; + +export const getExtensionState = (): AzureExtensionState => { + const state = window.localStorage.getItem(`${window.Composer?.__extensionId}:state`); + if (state) { + return JSON.parse(state); + } else { + return defaultExtensionState; + } +}; + +export const setExtensionState = (value) => { + window.localStorage.setItem(`${window.Composer?.__extensionId}:state`, JSON.stringify(value)); +}; + +export const clearExtensionState = () => { + window.localStorage.removeItem(`${window.Composer?.__extensionId}:state`); +}; From b55d5b44be1a40db3d54400cb008481b0bba59cc Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Sat, 13 Mar 2021 00:35:26 +0800 Subject: [PATCH 06/10] remove setExtensionState, add useLocalStorage --- .../src/components/PluginHost/PluginHost.tsx | 4 +- .../client/src/plugin-host-preload.ts | 1 + .../src/hooks/useLocalStorage.ts | 43 +++++++++++++++++++ .../extension-client/src/types/window.d.ts | 1 + .../azurePublish/src/components/util.ts | 17 -------- 5 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 Composer/packages/extension-client/src/hooks/useLocalStorage.ts diff --git a/Composer/packages/client/src/components/PluginHost/PluginHost.tsx b/Composer/packages/client/src/components/PluginHost/PluginHost.tsx index fe9edda001..b3c228a774 100644 --- a/Composer/packages/client/src/components/PluginHost/PluginHost.tsx +++ b/Composer/packages/client/src/components/PluginHost/PluginHost.tsx @@ -45,6 +45,7 @@ interface PluginHostProps { async function attachPluginAPI( win: Window, id: string, + bundleId: string, type: PluginType, shell?: object, settings?: ExtensionSettings @@ -56,6 +57,7 @@ async function attachPluginAPI( } win.Composer.__extensionId = id; + win.Composer.__bundleId = bundleId; win.Composer.__pluginType = type; win.Composer.settings = settings ?? {}; win.Composer.sync(shell); @@ -96,7 +98,7 @@ export const PluginHost: React.FC = (props) => { const iframeWindow = targetRef.current?.contentWindow as Window; const iframeDocument = targetRef.current?.contentDocument as Document; - await attachPluginAPI(iframeWindow, name, type, shell, extensionSettings); + await attachPluginAPI(iframeWindow, name, bundle, type, shell, extensionSettings); //load the bundle for the specified plugin const pluginScriptId = `plugin-${type}-${name}`; diff --git a/Composer/packages/client/src/plugin-host-preload.ts b/Composer/packages/client/src/plugin-host-preload.ts index d2ab3d4c9a..7365c817b1 100644 --- a/Composer/packages/client/src/plugin-host-preload.ts +++ b/Composer/packages/client/src/plugin-host-preload.ts @@ -51,6 +51,7 @@ window.CodeEditors = CodeEditors; window.UIShared = UIShared; window.Composer = { __extensionId: '', + __bundleId: '', __pluginType: '', render: (component: React.ReactElement) => { ReactDOM.render(component, document.getElementById('root')); diff --git a/Composer/packages/extension-client/src/hooks/useLocalStorage.ts b/Composer/packages/extension-client/src/hooks/useLocalStorage.ts new file mode 100644 index 0000000000..c964490e11 --- /dev/null +++ b/Composer/packages/extension-client/src/hooks/useLocalStorage.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { useMemo } from 'react'; + +const KEY = 'composer:extensions'; + +export function useLocalStorage() { + const { __extensionId, __bundleId } = window.Composer; + + const getItem = useMemo( + () => (key: string) => { + const allStates = window.localStorage.getItem(KEY); + if (allStates) { + const extensionState = JSON.parse(allStates)?.[__extensionId]; + return extensionState?.[__bundleId]?.[key]; + } + return undefined; + }, + [__extensionId, __bundleId] + ); + + const setItem = useMemo( + () => (key: string, value: string) => { + const allStatesString = window.localStorage.getItem(KEY); + const allStates = allStatesString ? JSON.parse(allStatesString) : {}; + let extensionState; + if (allStates) { + extensionState = allStates?.[__extensionId] || {}; + const bundleState = extensionState?.[__bundleId] || {}; + bundleState[key] = value; + // eslint-disable-next-line no-underscore-dangle + extensionState[__bundleId] = bundleState; + // eslint-disable-next-line no-underscore-dangle + allStates[__extensionId] = extensionState; + } + window.localStorage.setItem(KEY, JSON.stringify(allStates)); + }, + [__extensionId, __bundleId] + ); + + return { getItem, setItem }; +} diff --git a/Composer/packages/extension-client/src/types/window.d.ts b/Composer/packages/extension-client/src/types/window.d.ts index 281ae4ae4b..6b0c61c376 100644 --- a/Composer/packages/extension-client/src/types/window.d.ts +++ b/Composer/packages/extension-client/src/types/window.d.ts @@ -22,6 +22,7 @@ declare global { */ Composer: { __extensionId: string; + __bundleId: string; __pluginType: string; render: (component: React.ReactElement) => void; sync: (shell: Shell) => void; diff --git a/extensions/azurePublish/src/components/util.ts b/extensions/azurePublish/src/components/util.ts index 373aebae07..fc1f8604bb 100644 --- a/extensions/azurePublish/src/components/util.ts +++ b/extensions/azurePublish/src/components/util.ts @@ -92,20 +92,3 @@ type AzureExtensionState = { requiredResources: ResourcesItem[]; choice: { key: string; text: string }; }; - -export const getExtensionState = (): AzureExtensionState => { - const state = window.localStorage.getItem(`${window.Composer?.__extensionId}:state`); - if (state) { - return JSON.parse(state); - } else { - return defaultExtensionState; - } -}; - -export const setExtensionState = (value) => { - window.localStorage.setItem(`${window.Composer?.__extensionId}:state`, JSON.stringify(value)); -}; - -export const clearExtensionState = () => { - window.localStorage.removeItem(`${window.Composer?.__extensionId}:state`); -}; From 053557277becd08e81b57eef0d6ae78108a80ce3 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Sat, 13 Mar 2021 00:38:50 +0800 Subject: [PATCH 07/10] polish --- .../create-publish-profile/PublishProfileDialog.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx index 477ff0dedb..0269dcc79f 100644 --- a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx @@ -76,9 +76,7 @@ export const PublishProfileDialog: React.FC = (props) // setup plugin APIs useEffect(() => { - PluginAPI.publish.closeDialog = () => { - closeDialog(); - }; + PluginAPI.publish.closeDialog = closeDialog; PluginAPI.publish.onBack = () => { setPage(Page.ProfileForm); setTitle({ From 8b57d4c5bdedf20a8743593b0fcf624b92f4ed28 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 15 Mar 2021 18:09:44 +0800 Subject: [PATCH 08/10] update uselocalStorage, use hooks in extension to save state; --- .../PublishProfileDialog.tsx | 9 ++- Composer/packages/client/src/plugins/api.ts | 1 + .../extension-client/src/hooks/index.ts | 1 + .../src/hooks/useLocalStorage.ts | 59 +++++++++++++++---- .../src/hooks/usePublishApi.ts | 4 ++ .../src/components/azureProvisionDialog.tsx | 26 ++++---- .../azurePublish/src/components/util.ts | 2 +- 7 files changed, 73 insertions(+), 29 deletions(-) diff --git a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx index 0269dcc79f..ee38c09061 100644 --- a/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/create-publish-profile/PublishProfileDialog.tsx @@ -146,6 +146,9 @@ export const PublishProfileDialog: React.FC = (props) PluginAPI.publish.getType = () => { return targetType; }; + PluginAPI.publish.getName = () => { + return name; + }; PluginAPI.publish.getSchema = () => { return types.find((t) => t.name === targetType)?.schema; }; @@ -186,11 +189,7 @@ export const PublishProfileDialog: React.FC = (props) modalProps={{ isBlocking: true, }} - onDismiss={() => { - closeDialog(); - // remove extension state when parent component destroy - window.localStorage.removeItem(`${targetType}:state`); - }} + onDismiss={closeDialog} > {page !== Page.ConfigProvision && (
diff --git a/Composer/packages/client/src/plugins/api.ts b/Composer/packages/client/src/plugins/api.ts index 66c09b0e69..839fdb6ffc 100644 --- a/Composer/packages/client/src/plugins/api.ts +++ b/Composer/packages/client/src/plugins/api.ts @@ -32,6 +32,7 @@ interface PublishAPI { setTitle?: (value) => void; getSchema?: () => any; getType?: () => string; + getName?: () => string; savePublishConfig?: (config: PublishConfig) => void; getTokenFromCache?: () => { accessToken: string; graphToken: string }; isGetTokenFromUser?: () => boolean; diff --git a/Composer/packages/extension-client/src/hooks/index.ts b/Composer/packages/extension-client/src/hooks/index.ts index 9ff8373ee8..6681a49b0d 100644 --- a/Composer/packages/extension-client/src/hooks/index.ts +++ b/Composer/packages/extension-client/src/hooks/index.ts @@ -17,3 +17,4 @@ export * from './useLgApi'; export * from './useProjectApi'; export * from './usePublishApi'; export * from './useTelemetryClient'; +export * from './useLocalStorage'; diff --git a/Composer/packages/extension-client/src/hooks/useLocalStorage.ts b/Composer/packages/extension-client/src/hooks/useLocalStorage.ts index c964490e11..4c6fa77b9c 100644 --- a/Composer/packages/extension-client/src/hooks/useLocalStorage.ts +++ b/Composer/packages/extension-client/src/hooks/useLocalStorage.ts @@ -20,24 +20,63 @@ export function useLocalStorage() { [__extensionId, __bundleId] ); + const getAll = useMemo( + () => () => { + const allStatesString = window.localStorage.getItem(KEY); + if (allStatesString) { + return JSON.parse(allStatesString)?.[__extensionId]?.[__bundleId]; + } + }, + [__extensionId, __bundleId] + ); + const setItem = useMemo( - () => (key: string, value: string) => { + () => (key: string, value: any) => { const allStatesString = window.localStorage.getItem(KEY); const allStates = allStatesString ? JSON.parse(allStatesString) : {}; - let extensionState; - if (allStates) { - extensionState = allStates?.[__extensionId] || {}; - const bundleState = extensionState?.[__bundleId] || {}; - bundleState[key] = value; + + const extensionState = allStates?.[__extensionId] || {}; + const bundleState = extensionState?.[__bundleId] || {}; + bundleState[key] = value; + // eslint-disable-next-line no-underscore-dangle + extensionState[__bundleId] = bundleState; + // eslint-disable-next-line no-underscore-dangle + allStates[__extensionId] = extensionState; + + window.localStorage.setItem(KEY, JSON.stringify(allStates)); + }, + [__extensionId, __bundleId] + ); + + const replaceAll = useMemo( + () => (value: any) => { + const allStatesString = window.localStorage.getItem(KEY); + const allStates = allStatesString ? JSON.parse(allStatesString) : {}; + + const extensionState = allStates?.[__extensionId] || {}; + // eslint-disable-next-line no-underscore-dangle + extensionState[__bundleId] = value; + // eslint-disable-next-line no-underscore-dangle + allStates[__extensionId] = extensionState; + window.localStorage.setItem(KEY, JSON.stringify(allStates)); + }, + [__extensionId, __bundleId] + ); + + const clearAll = useMemo( + () => () => { + const allStatesString = window.localStorage.getItem(KEY); + if (allStatesString) { + const allStates = allStatesString ? JSON.parse(allStatesString) : {}; + const extensionState = allStates?.[__extensionId] || {}; // eslint-disable-next-line no-underscore-dangle - extensionState[__bundleId] = bundleState; + delete extensionState[__bundleId]; // eslint-disable-next-line no-underscore-dangle allStates[__extensionId] = extensionState; + window.localStorage.setItem(KEY, JSON.stringify(allStates)); } - window.localStorage.setItem(KEY, JSON.stringify(allStates)); }, [__extensionId, __bundleId] ); - - return { getItem, setItem }; + return { getItem, setItem, getAll, clearAll, replaceAll }; } diff --git a/Composer/packages/extension-client/src/hooks/usePublishApi.ts b/Composer/packages/extension-client/src/hooks/usePublishApi.ts index a3e1a0b71f..7603091498 100644 --- a/Composer/packages/extension-client/src/hooks/usePublishApi.ts +++ b/Composer/packages/extension-client/src/hooks/usePublishApi.ts @@ -40,6 +40,9 @@ export function usePublishApi() { function getType(): string { return window[ComposerGlobalName].getType(); } + function getName(): string { + return window[ComposerGlobalName].getName(); + } function savePublishConfig(config): void { return window[ComposerGlobalName].savePublishConfig(config); } @@ -64,6 +67,7 @@ export function usePublishApi() { setTitle, getSchema, getType, + getName, savePublishConfig, getTokenFromCache, isGetTokenFromUser, diff --git a/extensions/azurePublish/src/components/azureProvisionDialog.tsx b/extensions/azurePublish/src/components/azureProvisionDialog.tsx index 3ad28e8689..aeefafdf36 100644 --- a/extensions/azurePublish/src/components/azureProvisionDialog.tsx +++ b/extensions/azurePublish/src/components/azureProvisionDialog.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { useState, useMemo, useEffect, Fragment, useCallback, useRef, Suspense } from 'react'; import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button'; -import { logOut, usePublishApi, getTenants, getARMTokenForTenant } from '@bfc/extension-client'; +import { logOut, usePublishApi, getTenants, getARMTokenForTenant, useLocalStorage } from '@bfc/extension-client'; import { Subscription } from '@azure/arm-subscriptions/esm/models'; import { DeployLocation } from '@botframework-composer/types'; import { NeutralColors } from '@uifabric/fluent-theme'; @@ -31,6 +31,7 @@ import { } from 'office-ui-fabric-react'; import { JsonEditor } from '@bfc/code-editor'; import { SharedColors } from '@uifabric/fluent-theme'; +import { get } from 'request'; import { AzureResourceTypes, ResourcesItem } from '../types'; @@ -42,14 +43,7 @@ import { getLuisAuthoringRegions, CheckWebAppNameAvailability, } from './api'; -import { - getExistResources, - removePlaceholder, - decodeToken, - getExtensionState, - setExtensionState, - clearExtensionState, -} from './util'; +import { getExistResources, removePlaceholder, decodeToken, defaultExtensionState } from './util'; const iconStyle = (required) => { return { @@ -254,15 +248,19 @@ export const AzureProvisionDialog: React.FC = () => { setTitle, getSchema, getType, + getName, getTokenFromCache, isGetTokenFromUser, getTenantIdFromCache, setTenantId, } = usePublishApi(); + + const { setItem, getItem, clearAll } = useLocalStorage(); // set type of publish - azurePublish or azureFunctionsPublish const publishType = getType(); + const profileName = getName(); const currentConfig = removePlaceholder(publishConfig); - const extensionState = getExtensionState(); + const extensionState = { ...defaultExtensionState, ...getItem(profileName) }; const [subscriptions, setSubscriptions] = useState(); const [deployLocations, setDeployLocations] = useState([]); @@ -555,8 +553,8 @@ export const AzureProvisionDialog: React.FC = () => { () => async (options) => { // call back to the main Composer API to begin this process... startProvision(options); + clearAll(); closeDialog(); - clearExtensionState(); }, [] ); @@ -564,8 +562,8 @@ export const AzureProvisionDialog: React.FC = () => { const onSave = useMemo( () => async () => { savePublishConfig(importConfig); + clearAll(); closeDialog(); - clearExtensionState(); }, [importConfig] ); @@ -809,6 +807,7 @@ export const AzureProvisionDialog: React.FC = () => {
{ + clearAll(); closeDialog(); logOut(); }} @@ -821,7 +820,8 @@ export const AzureProvisionDialog: React.FC = () => { style={{ margin: '0 4px' }} text={formatMessage('Back')} onClick={() => { - setExtensionState({ + clearAll(); + setItem(profileName, { subscriptionId: currentSubscription, resourceGroup: currentResourceGroup, hostName: currentHostName, diff --git a/extensions/azurePublish/src/components/util.ts b/extensions/azurePublish/src/components/util.ts index fc1f8604bb..3f5e9ba8eb 100644 --- a/extensions/azurePublish/src/components/util.ts +++ b/extensions/azurePublish/src/components/util.ts @@ -61,7 +61,7 @@ export const getExistResources = (config) => { } else return []; }; -const defaultExtensionState = { +export const defaultExtensionState = { subscriptionId: '', resourceGroup: '', hostName: '', From 2c2a087d876c2b0a271d96927af0cf13afa767dc Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Wed, 17 Mar 2021 11:22:22 +0800 Subject: [PATCH 09/10] fix comments --- .../src/hooks/useLocalStorage.ts | 94 ++++++++----------- .../src/components/azureProvisionDialog.tsx | 5 +- 2 files changed, 41 insertions(+), 58 deletions(-) diff --git a/Composer/packages/extension-client/src/hooks/useLocalStorage.ts b/Composer/packages/extension-client/src/hooks/useLocalStorage.ts index 4c6fa77b9c..cb5d0dfcdd 100644 --- a/Composer/packages/extension-client/src/hooks/useLocalStorage.ts +++ b/Composer/packages/extension-client/src/hooks/useLocalStorage.ts @@ -1,82 +1,66 @@ +/* eslint-disable no-underscore-dangle */ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { useMemo } from 'react'; +import { useCallback } from 'react'; const KEY = 'composer:extensions'; export function useLocalStorage() { const { __extensionId, __bundleId } = window.Composer; - const getItem = useMemo( - () => (key: string) => { - const allStates = window.localStorage.getItem(KEY); - if (allStates) { - const extensionState = JSON.parse(allStates)?.[__extensionId]; - return extensionState?.[__bundleId]?.[key]; - } - return undefined; - }, - [__extensionId, __bundleId] - ); + const getExtensionState = useCallback(() => { + const allStatesString = window.localStorage.getItem(KEY); + const allStates = allStatesString ? JSON.parse(allStatesString) : {}; + const extensionState = allStates?.[__extensionId] || {}; + return extensionState; + }, [__extensionId]); - const getAll = useMemo( - () => () => { - const allStatesString = window.localStorage.getItem(KEY); - if (allStatesString) { - return JSON.parse(allStatesString)?.[__extensionId]?.[__bundleId]; - } - }, - [__extensionId, __bundleId] - ); - - const setItem = useMemo( - () => (key: string, value: any) => { + const updateExtensionState = useCallback( + (value = undefined) => { const allStatesString = window.localStorage.getItem(KEY); const allStates = allStatesString ? JSON.parse(allStatesString) : {}; - const extensionState = allStates?.[__extensionId] || {}; - const bundleState = extensionState?.[__bundleId] || {}; - bundleState[key] = value; - // eslint-disable-next-line no-underscore-dangle - extensionState[__bundleId] = bundleState; - // eslint-disable-next-line no-underscore-dangle + if (value) { + extensionState[__bundleId] = value; + } else { + delete extensionState[__bundleId]; + } allStates[__extensionId] = extensionState; - window.localStorage.setItem(KEY, JSON.stringify(allStates)); }, [__extensionId, __bundleId] ); - const replaceAll = useMemo( - () => (value: any) => { - const allStatesString = window.localStorage.getItem(KEY); - const allStates = allStatesString ? JSON.parse(allStatesString) : {}; + const getAll = useCallback(() => { + return getExtensionState()?.[__bundleId]; + }, [getExtensionState, __bundleId]); - const extensionState = allStates?.[__extensionId] || {}; - // eslint-disable-next-line no-underscore-dangle - extensionState[__bundleId] = value; - // eslint-disable-next-line no-underscore-dangle - allStates[__extensionId] = extensionState; - window.localStorage.setItem(KEY, JSON.stringify(allStates)); + const getItem = useCallback( + (key: string) => { + return getAll()?.[key]; }, - [__extensionId, __bundleId] + [getAll] ); - const clearAll = useMemo( - () => () => { - const allStatesString = window.localStorage.getItem(KEY); - if (allStatesString) { - const allStates = allStatesString ? JSON.parse(allStatesString) : {}; - const extensionState = allStates?.[__extensionId] || {}; - // eslint-disable-next-line no-underscore-dangle - delete extensionState[__bundleId]; - // eslint-disable-next-line no-underscore-dangle - allStates[__extensionId] = extensionState; - window.localStorage.setItem(KEY, JSON.stringify(allStates)); - } + const setItem = useCallback( + (key: string, value: any) => { + const bundleState = getAll() || {}; + bundleState[key] = value; + updateExtensionState(bundleState); }, - [__extensionId, __bundleId] + [getAll] ); + + const replaceAll = useCallback( + (value: any) => { + updateExtensionState(value); + }, + [updateExtensionState] + ); + + const clearAll = useCallback(() => { + updateExtensionState(); + }, [updateExtensionState]); return { getItem, setItem, getAll, clearAll, replaceAll }; } diff --git a/extensions/azurePublish/src/components/azureProvisionDialog.tsx b/extensions/azurePublish/src/components/azureProvisionDialog.tsx index aeefafdf36..cc12a5c4a2 100644 --- a/extensions/azurePublish/src/components/azureProvisionDialog.tsx +++ b/extensions/azurePublish/src/components/azureProvisionDialog.tsx @@ -31,7 +31,6 @@ import { } from 'office-ui-fabric-react'; import { JsonEditor } from '@bfc/code-editor'; import { SharedColors } from '@uifabric/fluent-theme'; -import { get } from 'request'; import { AzureResourceTypes, ResourcesItem } from '../types'; @@ -550,7 +549,7 @@ export const AzureProvisionDialog: React.FC = () => { ); const onSubmit = useMemo( - () => async (options) => { + () => (options) => { // call back to the main Composer API to begin this process... startProvision(options); clearAll(); @@ -560,7 +559,7 @@ export const AzureProvisionDialog: React.FC = () => { ); const onSave = useMemo( - () => async () => { + () => () => { savePublishConfig(importConfig); clearAll(); closeDialog(); From 654624987ec4631da8110a90d1708fad73e61b87 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Wed, 17 Mar 2021 18:23:47 +0800 Subject: [PATCH 10/10] polish --- .../src/components/azureProvisionDialog.tsx | 1 - .../azurePublish/src/components/util.ts | 21 ------------------- 2 files changed, 22 deletions(-) diff --git a/extensions/azurePublish/src/components/azureProvisionDialog.tsx b/extensions/azurePublish/src/components/azureProvisionDialog.tsx index cc12a5c4a2..19f8235e65 100644 --- a/extensions/azurePublish/src/components/azureProvisionDialog.tsx +++ b/extensions/azurePublish/src/components/azureProvisionDialog.tsx @@ -348,7 +348,6 @@ export const AzureProvisionDialog: React.FC = () => { }); }) .catch((err) => { - console.log(err); setCurrentUser(undefined); setLoginErrorMsg(err.message || err.toString()); }); diff --git a/extensions/azurePublish/src/components/util.ts b/extensions/azurePublish/src/components/util.ts index 3f5e9ba8eb..5a24b93ffc 100644 --- a/extensions/azurePublish/src/components/util.ts +++ b/extensions/azurePublish/src/components/util.ts @@ -71,24 +71,3 @@ export const defaultExtensionState = { requiredResources: [], choice: { key: 'create', text: 'Create new Azure resources' }, }; - -type ResourcesItem = { - description: string; - text: string; - tier: string; - group: string; - key: string; - required: boolean; - [key: string]: any; -}; - -type AzureExtensionState = { - subscriptionId: string; - resourceGroup: string; - hostName: string; - location: string; - luisLocation: string; - enabledResources: ResourcesItem[]; - requiredResources: ResourcesItem[]; - choice: { key: string; text: string }; -};