Skip to content

Commit

Permalink
Merge pull request #779 from Microsoft/toanzian/migration-v3
Browse files Browse the repository at this point in the history
V3 - > V4 Migration [V3 SIDE]
  • Loading branch information
Justin Wilaby committed Aug 21, 2018
2 parents 5fc7a53 + c27c406 commit fd7d7dc
Show file tree
Hide file tree
Showing 12 changed files with 681 additions and 17 deletions.
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -79,6 +79,7 @@
"crypto-js": "3.1.9-1",
"electron-debug": "1.1.0",
"electron-localshortcut": "2.0.2",
"electron-updater": "^3.0.3",
"es6-shim": "0.35.2",
"formidable": "1.0.17",
"got": "7.1.0",
Expand All @@ -87,6 +88,7 @@
"lock": "0.1.3",
"mkdirp": "0.5.1",
"moment": "2.17.1",
"msbot": "^1.0.51",
"node-uuid": "1.4.7",
"react": "15.6.1",
"react-dom": "15.6.1",
Expand Down
123 changes: 123 additions & 0 deletions src/client/css/emulator.css
Expand Up @@ -1201,3 +1201,126 @@
color: #ed4556;
display: inline-block;
}

/** Update Dialog */

.update-dialog-bg {
background: transparent;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 9998;
}

.update-dialog {
max-height: 350px;
height: auto;
width: 400px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 24px;
z-index: 9999;
background-color: #F4F4F4;
box-shadow: 0 0 4px 0 rgba(0,0,0,0.3);
color: #4A4A4A;
line-height: initial;
overflow: hidden;
text-align: left;
cursor: default;
}

.update-dialog-close-icon {
position: absolute;
top: 20px;
right: 20px;
border: 0;
padding: 0;
height: 16px;
width: 16px;
cursor: pointer;
background-color: transparent;
}

.update-dialog-close-icon:hover {
background-color: transparent;
}

.update-dialog-close-icon:after {
content: "\2716";
height: 16px;
width: 16px;
color: #333;
}

.update-dialog-header {
color: #4A4A4A;
font-family: "Segoe UI";
font-size: 19px;
line-height: 26px;
margin: 0;
margin-bottom: 24px;
font-weight: normal;
}

.update-dialog-progress-header {
color: #4A4A4A;
font-family: "Segoe UI";
font-size: 13px;
line-height: 18px;
margin: 0;
margin-bottom: 18px;
font-weight: normal;
}

.update-progress-track {
position: relative;
height: 4px;
width: 100%;
background-color: rgba(0,0,0,0.2);
}

.update-progress-bar {
position: absolute;
top: 0;
left: 0;
height: 4px;
background-color: #0078D7;
}

.update-dialog-btn-row {
display: flex;
flex-flow: row nowrap;
margin-top: 50px;
}

.update-dialog-btn-row > button {
height: 16px;
width: 166px;
font-family: "Segoe UI";
font-size: 12px;
line-height: 13px;
text-align: center;
}

.update-dialog-gray-btn {
background-color: #CCC;
color: #444444;
}

.update-dialog-gray-btn:hover {
background-color: #CCC;
}

.update-dialog-blue-btn {
background-color: #3062D6;
color: #FFF;
}

.update-dialog-btn-row > button:first-child {
margin-right: 8px;
margin-left: auto;
}
147 changes: 147 additions & 0 deletions src/client/dialogs/updateDialog.tsx
@@ -0,0 +1,147 @@
import * as React from 'react';
import { UpdateActions } from '../reducers';
import * as Electron from 'electron';
import { UpdateStatus } from '../../types/updateStatus';

export interface UpdateDialogProps {
showing?: boolean;
}

export interface UpdateDialogState {
openAfterInstallation: boolean;
progress: string;
status: UpdateStatus;
}

export class UpdateDialog extends React.Component<UpdateDialogProps, UpdateDialogState> {
constructor(props: UpdateDialogProps) {
super(props);

Electron.ipcRenderer.on('update-progress', (event, progress: string) => {
this.setState({ progress });
})

Electron.ipcRenderer.on('update-status', (event, status: UpdateStatus) => {
this.setState({ status });
});

this.state = {
openAfterInstallation: false,
progress: '0%',
status: null
};
}

public render(): JSX.Element {
if (this.props.showing) {
return (
<div>
<div className="update-dialog-bg"></div>
{ this.content }
</div>
);
} else {
return null;
}
}

public get content(): JSX.Element {
switch (this.state.status) {
case UpdateStatus.downloading:
return this.props.showing && this.downloadView;
case UpdateStatus.downloaded:
return this.props.showing && this.postDownloadView;
default:
return this.defaultView;
}
}

public get defaultView(): JSX.Element {
return (
<div className="update-dialog">
<button className="update-dialog-close-icon" onClick={ this.onClickCancel }></button>
<h1 className="update-dialog-header">A new version of the Bot Framework Emulator is available</h1>
<p className="update-dialog-content">
Bot Framework Emulator 4.0.16 is available. Would you like to install the new version?
Learn more about Emulator v4.
</p>
<div className="input-group appsettings-checkbox-group">
<label className="form-label clickable">
<input
type="checkbox"
name="openAfterInstallation"
className="form-input"
checked={ this.state.openAfterInstallation }
onChange={ this.onCheckOpenAfterInstallation }
/>
Open Emulator v4 when installation is complete.
</label>
</div>
<div className="update-dialog-btn-row">
<button className="update-dialog-gray-btn" onClick={ this.onClickCancel }>Cancel</button>
<button className="update-dialog-blue-btn" onClick={ this.onClickInstall }>Install the new version</button>
</div>
</div>
);
}

public get downloadView(): JSX.Element {
return (
<div className="update-dialog">
<button className="update-dialog-close-icon" onClick={ this.onClickCancel }></button>
<h1 className="update-dialog-progress-header">Downloading ...</h1>
{ this.progressBar }
<div className="update-dialog-btn-row">
<button className="update-dialog-gray-btn" onClick={ this.onClickCancel }>Cancel download</button>
</div>
</div>
);
}

public get postDownloadView(): JSX.Element {
return (
<div className="update-dialog">
<button className="update-dialog-close-icon" onClick={ this.onClickCancel }></button>
<button className="update-dialog-close-icon"></button>
<h1 className="update-dialog-header">Download complete</h1>
<p className="update-dialog-content">
Would you like to migrate your bots and install Bot Framework Emulator v4?
</p>
<div className="update-dialog-btn-row">
<button className="update-dialog-gray-btn" onClick={ this.onClickCancel }>No, thanks</button>
<button className="update-dialog-blue-btn" onClick={ this.onClickMigrate }>Migrate and open</button>
</div>
</div>
);
}

public get progressBar(): JSX.Element {
let { progress = '0%' } = this.state;

return (
<div className="update-progress-track">
<div className="update-progress-bar" style={{ width: progress }}></div>
</div>
);
}

private onCheckOpenAfterInstallation = () => {
this.setState({ openAfterInstallation: !this.state.openAfterInstallation });
};

private onClickCancel = () => {
UpdateActions.setShowing(false);
}

private onClickInstall = () => {
// tell server side to download
Electron.ipcRenderer.send('download-v4-update');
}

private onClickMigrate = () => {
// tell server side to migrate and restart
const openAfterInstall = this.state.openAfterInstallation;
Electron.ipcRenderer.send('migrate-and-restart', openAfterInstall);
UpdateActions.setShowing(false);
}
}
17 changes: 16 additions & 1 deletion src/client/mainView.tsx
Expand Up @@ -48,14 +48,18 @@ import { BotEmulatorContext } from './botEmulatorContext';
import { AddressBarOperators } from './addressBar/addressBarOperators';
import * as log from './log';
import { ISpeechTokenInfo } from '../types/speechTypes';
import { UpdateDialog } from './dialogs/updateDialog';

const CognitiveServices = require('../../node_modules/botframework-webchat/CognitiveServices');
const remote = require('electron').remote;
const ipcRenderer = require('electron').ipcRenderer;
const AdaptiveCardsHostConfig = require('./adaptivecards-hostconfig.json');

export interface MainViewState {
showUpdateDialog: boolean;
}

export class MainView extends React.Component<{}, {}> {
export class MainView extends React.Component<{}, MainViewState> {
settingsUnsubscribe: any;
settingsLoadUnsubscribe: any;
reuseKey: number = 0;
Expand All @@ -65,6 +69,15 @@ export class MainView extends React.Component<{}, {}> {
botId: string;
botChatContainer: HTMLElement;
shouldWarnOfBotChange: boolean = false;
showUpdateDialog: boolean = false;

constructor(props: {}) {
super(props);

this.state = {
showUpdateDialog: false
};
}

componentWillMount() {
this.settingsUnsubscribe = addSettingsListener((settings: Settings) => {
Expand Down Expand Up @@ -109,6 +122,7 @@ export class MainView extends React.Component<{}, {}> {
} catch(e) {
//log.error(e.message);
}
this.setState({ showUpdateDialog: settings.update.showing });
});
}

Expand Down Expand Up @@ -345,6 +359,7 @@ export class MainView extends React.Component<{}, {}> {
</Splitter>
</div>
<AppSettingsDialog />
<UpdateDialog showing={ this.state.showUpdateDialog }/>
</div>
);
}
Expand Down

0 comments on commit fd7d7dc

Please sign in to comment.