Skip to content

Commit

Permalink
Added first-time data collection dialog. (#1624)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyanziano authored and cwhitten committed Jun 18, 2019
1 parent fb4d1af commit e437344
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [main] Added ability to launch into a bot inspector mode session via protocol url in PR [1617](https://github.com/microsoft/BotFramework-Emulator/pull/1617)
- [main/shared] Added 'Clear State' menu item in PR [1596](https://github.com/microsoft/BotFramework-Emulator/pull/1596)
- [main] Encrypted bot secrets are now stored in the user's OS secret store in PR [1618](https://github.com/microsoft/BotFramework-Emulator/pull/1618)
- [client] Added first-time data collection dialog in PR [1624](https://github.com/microsoft/BotFramework-Emulator/pull/1624)

## Fixed
- [client/main] Auto update is now opt-in by default and changed UX to reflect this in PR [1575](https://github.com/microsoft/BotFramework-Emulator/pull/1575)
Expand Down
1 change: 1 addition & 0 deletions PRIVACY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**Data Collection.** The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the repository. There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at https://go.microsoft.com/fwlink/?LinkID=824704. You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
8 changes: 8 additions & 0 deletions packages/app/client/src/commands/uiCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
SecretPromptDialogContainer,
UpdateAvailableDialogContainer,
UpdateUnavailableDialogContainer,
DataCollectionDialogContainer,
} from '../ui/dialogs';
import { openBotViaUrlAction } from '../data/action/botActions';
import { beginAdd } from '../data/action/notificationActions';
Expand Down Expand Up @@ -245,4 +246,11 @@ export class UiCommands {
protected openBotViaURL(conversationParams: StartConversationParams): void {
store.dispatch(openBotViaUrlAction(conversationParams));
}

// ---------------------------------------------------------------------------
// Shows the data collection dialog
@Command(UI.ShowDataCollectionDialog)
protected showDataCollectionDialog() {
return DialogService.showDialog(DataCollectionDialogContainer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import * as React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import { createStore } from 'redux';

import { DialogService } from '../service';

import { DataCollectionDialog } from './dataCollectionDialog';
import { DataCollectionDialogContainer } from './dataCollectionDialogContainer';

describe('<DataCollectionDialogContainer />', () => {
let wrapper;
let instance;

beforeEach(() => {
wrapper = mount(
<Provider store={createStore((state, _action) => state)}>
<DataCollectionDialogContainer />
</Provider>
);
instance = wrapper.find(DataCollectionDialog).instance();
});

it('should render properly', () => {
expect(wrapper.find(DataCollectionDialog)).toHaveLength(1);
});

it('should hide the dialog and return the proper result', () => {
const hideDialogSpy = jest.spyOn(DialogService, 'hideDialog');
instance.onConfirmOrCancel({ target: { name: 'yes' } });

expect(hideDialogSpy).toHaveBeenCalledWith(true);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import { DefaultButton, Dialog, DialogFooter, PrimaryButton } from '@bfemulator/ui-react';
import * as React from 'react';
import { Component, ReactNode } from 'react';

import * as styles from '../dialogStyles.scss';

export interface DataCollectionDialogProps {
hideDialog: (dataCollectionEnabled: boolean) => void;
}

export class DataCollectionDialog extends Component<DataCollectionDialogProps, {}> {
public render(): ReactNode {
return (
<Dialog className={styles.dialogMedium} cancel={this.onConfirmOrCancel} title="Help us improve?">
<p>
The Emulator includes a telemetry feature that collects usage information. It is important that the Emulator
team understands how the tool is being used so that it can be improved.
</p>
<p>You can turn data collection on or off at any time in your Emulator Settings.</p>
<p>
<a target="__blank" href="https://privacy.microsoft.com/privacystatement">
Privacy statement
</a>
</p>
<DialogFooter>
<DefaultButton text="Not now" onClick={this.onConfirmOrCancel} />
<PrimaryButton text="Yes, collect data" name="yes" onClick={this.onConfirmOrCancel} />
</DialogFooter>
</Dialog>
);
}

private onConfirmOrCancel = (ev: React.MouseEvent<HTMLButtonElement>): void => {
const collectData = !!(ev.target as HTMLButtonElement).name;
this.props.hideDialog(collectData);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
//
// Microsoft Bot Framework: http://botframework.com
//
// Bot Framework Emulator Github:
// https://github.com/Microsoft/BotFramwork-Emulator
//
// Copyright (c) Microsoft Corporation
// All rights reserved.
//
// MIT License:
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import { connect } from 'react-redux';

import { DialogService } from '../service';

import { DataCollectionDialog, DataCollectionDialogProps } from './dataCollectionDialog';

function mapDispatchToProps(): DataCollectionDialogProps {
return {
hideDialog: (collectData: boolean) => {
DialogService.hideDialog(collectData);
},
};
}

export const DataCollectionDialogContainer = connect(
null,
mapDispatchToProps
)(DataCollectionDialog);
1 change: 1 addition & 0 deletions packages/app/client/src/ui/dialogs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export * from './azureLoginSuccessDialog/azureLoginSuccessDialogContainer';
export * from './azureLoginPromptDialog/azureLoginPromptDialogContainer';
export * from './azureLoginFailedDialog/azureLoginFailedDialogContainer';
export * from './connectServicePromptDialog/connectServicePromptDialogContainer';
export * from './dataCollectionDialog/dataCollectionDialogContainer';
export * from './getStartedWithCSDialog/getStartedWithCSDialogContainer';
export * from './postMigrationDialog/postMigrationDialogContainer';
export * from './progressIndicator/progressIndicatorContainer';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,8 @@ export class AppSettingsEditor extends React.Component<AppSettingsEditorProps, A
name="collectUsageData"
disabled={true}
/>
<a target="_blank" href="https://aka.ms/bot-framework-emulator-data-collection" rel="noopener noreferrer">
Learn more.
<a target="_blank" href="https://privacy.microsoft.com/privacystatement" rel="noopener noreferrer">
Privacy statement
</a>
</Column>
</Row>
Expand Down
6 changes: 3 additions & 3 deletions packages/app/main/src/appMenuBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,20 +460,20 @@ export class AppMenuBuilder {
{ type: 'separator' },
{
label: 'Privacy',
click: () => shell.openExternal('https://go.microsoft.com/fwlink/?LinkId=512132', { activate: true }),
click: () => shell.openExternal('https://privacy.microsoft.com/privacystatement', { activate: true }),
},
{
// TODO: Proper link for the license instead of third party credits
label: 'License',
click: () =>
shell.openExternal('https://aka.ms/O10ww2', {
shell.openExternal('https://aka.ms/bot-framework-emulator-license', {
activate: true,
}),
},
{
label: 'Credits',
click: () =>
shell.openExternal('https://aka.ms/Ud5ga6', {
shell.openExternal('https://aka.ms/bot-framework-emulator-credits', {
activate: true,
}),
},
Expand Down
4 changes: 4 additions & 0 deletions packages/app/main/src/commands/clientInitCommands.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ describe('The clientInitCommands', () => {
} as any);
process.argv.push('/path/to/transcript.transcript');
process.argv.push('bfemulator://bot.open?path=path/to/bot.bot');
const dispatchSpy = jest.spyOn(mockSettingsStore, 'dispatch');

const remoteCommandArgs = [];
const localCommandArgs = [];
Expand Down Expand Up @@ -221,6 +222,7 @@ describe('The clientInitCommands', () => {
]);

expect(remoteCommandArgs).toEqual([
[SharedConstants.Commands.UI.ShowDataCollectionDialog],
[
'transcript:open',
'/path/to/transcript.transcript',
Expand Down Expand Up @@ -252,5 +254,7 @@ describe('The clientInitCommands', () => {
},
],
]);

expect(dispatchSpy).toHaveBeenCalled();
});
});
20 changes: 17 additions & 3 deletions packages/app/main/src/commands/clientInitCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,20 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
import { dialog } from 'electron';
import { SharedConstants } from '@bfemulator/app-shared';
import { SharedConstants, Settings } from '@bfemulator/app-shared';
import { Command, CommandServiceImpl, CommandServiceInstance } from '@bfemulator/sdk-shared';
import { Store } from 'redux';

import * as BotActions from '../data/actions/botActions';
import { getStore } from '../data/store';
import { Protocol } from '../constants';
import { ExtensionManagerImpl } from '../extensions';
import { Migrator } from '../migrator';
import { ProtocolHandler } from '../protocolHandler';
import { getStore as getSettingsStore } from '../settingsData/store';
import { getStore as getSettingsStore, dispatch } from '../settingsData/store';
import { getBotsFromDisk } from '../utils';
import { openFileFromCommandLine } from '../utils/openFileFromCommandLine';
import { pushClientAwareSettings } from '../settingsData/actions/frameworkActions';
import { pushClientAwareSettings, setFramework } from '../settingsData/actions/frameworkActions';
import { AppMenuBuilder } from '../appMenuBuilder';

const Commands = SharedConstants.Commands;
Expand Down Expand Up @@ -84,6 +85,19 @@ export class ClientInitCommands {
protected async postWelcomeScreen(): Promise<void> {
await this.commandService.call(Commands.Electron.UpdateFileMenu);

// show the data collection modal if necessary
const settingsStore: Store<Settings> = getSettingsStore();
const { framework = {} } = settingsStore.getState();
if (!framework.hasBeenShownDataCollectionModal) {
const collectUsageData = await this.commandService.remoteCall<boolean>(Commands.UI.ShowDataCollectionDialog);
const updatedSettings = {
...framework,
collectUsageData,
hasBeenShownDataCollectionModal: true,
};
dispatch(setFramework(updatedSettings));
}

// Parse command line args for a protocol url
const args = process.argv.length ? process.argv.slice(1) : [];
if (args.some(arg => arg.includes(Protocol))) {
Expand Down
1 change: 1 addition & 0 deletions packages/app/shared/src/constants/sharedConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export const SharedConstants = {
ShowUpdateUnavailableDialog: 'update-unavailable-dialog:show',
ShowProgressIndicator: 'progress-indicator:show',
OpenBotViaUrl: 'connect-bot',
ShowDataCollectionDialog: 'data-collection:show',
},
},
ContentTypes: {
Expand Down
3 changes: 3 additions & 0 deletions packages/app/shared/src/types/serverSettingsTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export interface FrameworkSettings {
usePrereleases?: boolean;
// enables instrumentation
collectUsageData?: boolean;
// if false, the user is shown the data collection modal on startup
hasBeenShownDataCollectionModal?: boolean;
// Digest of k/v pairs for integrity
hash?: string;
// GUID set by the user
Expand Down Expand Up @@ -138,6 +140,7 @@ export const frameworkDefault: FrameworkSettings = {
usePrereleases: false,
autoUpdate: false,
collectUsageData: false,
hasBeenShownDataCollectionModal: false,
userGUID: '',
useCustomId: false,
};
Expand Down

0 comments on commit e437344

Please sign in to comment.