diff --git a/package-lock.json b/package-lock.json index dc6fe7af6..d7a257b62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13647,7 +13647,7 @@ }, "rimraf": { "version": "2.4.5", - "resolved": "https://botbuilder.myget.org/F/botbuilder-tools-daily/npm/rimraf/-/rimraf-2.4.5.tgz", + "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "optional": true, "requires": { @@ -13697,7 +13697,7 @@ }, "ncp": { "version": "2.0.0", - "resolved": "https://botbuilder.myget.org/F/botbuilder-tools-daily/npm/ncp/-/ncp-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, @@ -20058,7 +20058,7 @@ }, "os-locale": { "version": "1.4.0", - "resolved": "https://botbuilder.myget.org/F/botbuilder-tools-daily/npm/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { "lcid": "^1.0.0" diff --git a/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.scss b/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.scss index 682aad712..2df290692 100644 --- a/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.scss +++ b/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.scss @@ -19,3 +19,8 @@ line-height: 16px; color: var(--notifications-clear-all-btn-color); } + +.no-notifications-msg { + margin: 0; + margin-top: 15px; +} diff --git a/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.scss.d.ts b/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.scss.d.ts index 075a02e7b..8b1d610d0 100644 --- a/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.scss.d.ts +++ b/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.scss.d.ts @@ -1,3 +1,4 @@ // This is a generated file. Changes are likely to result in being overwritten export const notificationsExplorer: string; export const clearAllNotificationsBtn: string; +export const noNotificationsMsg: string; diff --git a/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.tsx b/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.tsx index 56ca98a3a..932ad2de0 100644 --- a/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.tsx +++ b/packages/app/client/src/ui/shell/explorer/notificationsExplorer/notificationsExplorer.tsx @@ -60,10 +60,13 @@ class NotificationsExplorerComp extends React.Component { + notifications.length ? notifications.map(n => { const notification = NotificationManager.get(n); return ; }) + : +

No new notifications.

} diff --git a/packages/app/client/src/ui/shell/explorer/servicesExplorer/servicesExplorer.tsx b/packages/app/client/src/ui/shell/explorer/servicesExplorer/servicesExplorer.tsx index 7e58221ba..c4a93d605 100644 --- a/packages/app/client/src/ui/shell/explorer/servicesExplorer/servicesExplorer.tsx +++ b/packages/app/client/src/ui/shell/explorer/servicesExplorer/servicesExplorer.tsx @@ -138,7 +138,7 @@ export class ServicesExplorer extends ServicePane {
  • diff --git a/packages/app/main/package.json b/packages/app/main/package.json index 6eae6772f..4aef76ed5 100644 --- a/packages/app/main/package.json +++ b/packages/app/main/package.json @@ -65,7 +65,6 @@ "@types/formidable": "^1.0.30", "@types/fs-extra": "^5.0.4", "@types/jest": "^22.2.3", - "@types/jsonpath": "^0.2.0", "@types/mkdirp": "^0.5.2", "@types/node": "8.9.3", "@types/request": "^2.47.0", @@ -126,7 +125,6 @@ "got": "^7.1.0", "http-status-codes": "^1.3.0", "jest-fetch-mock": "^1.6.2", - "jsonpath": "^1.0.0", "jsonwebtoken": "^8.3.0", "lock": "^0.1.2", "mkdirp": "^0.5.1", diff --git a/packages/app/main/src/appMenuBuilder.ts b/packages/app/main/src/appMenuBuilder.ts index 60a1372c4..21d4a8d6b 100644 --- a/packages/app/main/src/appMenuBuilder.ts +++ b/packages/app/main/src/appMenuBuilder.ts @@ -36,7 +36,6 @@ import * as Electron from 'electron'; import { mainWindow } from './main'; import { AppUpdater, UpdateStatus } from './appUpdater'; import { BotInfo, SharedConstants } from '@bfemulator/app-shared'; -import * as jsonpath from 'jsonpath'; import { ConversationService } from './services/conversationService'; import { getStore as getSettingsStore } from './settingsData/store'; import { rememberTheme } from './settingsData/actions/windowStateActions'; @@ -80,31 +79,7 @@ export const AppMenuBuilder = new class AppMenuBuilderImpl implements AppMenuBui ]; if (process.platform === 'darwin') { - /* - // Create the Application's main menu - var template2: MenuOpts[] = [ - { - label: windowTitle, - submenu: [ - { label: "About", click: () => Emulator.send('showExplorer-about') }, - { type: "separator" }, - { label: "Quit", accelerator: "Command+Q", click: () => Electron.app.quit() } - ] - }, { - label: "Edit", - submenu: [ - { label: "Undo", accelerator: "CmdOrCtrl+Z", role: "undo" }, - { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", role: "redo" }, - { type: "separator" }, - { label: "Cut", accelerator: "CmdOrCtrl+X", role: "cut" }, - { label: "Copy", accelerator: "CmdOrCtrl+C", role: "copy" }, - { label: "Paste", accelerator: "CmdOrCtrl+V", role: "paste" }, - { label: "Select All", accelerator: "CmdOrCtrl+A", role: "selectall" } - ] - }*/ - template.unshift(this.getAppMenuMac()); - // Window menu template.splice(4, 0, { label: 'Window', @@ -145,12 +120,12 @@ export const AppMenuBuilder = new class AppMenuBuilderImpl implements AppMenuBui click: () => { mainWindow.commandService.remoteCall(Bot.OpenBrowse); } - }]; + } ]; if (recentBots && recentBots.length) { const recentBotsList = this.createRecentBotsList(recentBots); subMenu.push({ label: 'Open Recent...', - submenu: [...recentBotsList] + submenu: [ ...recentBotsList ] }); } else { subMenu.push({ @@ -250,9 +225,7 @@ export const AppMenuBuilder = new class AppMenuBuilderImpl implements AppMenuBui { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, - { role: 'delete' }, - process.platform === 'win32' ? { type: 'separator' } : null, - { role: 'selectall' } + { role: 'delete' } ].filter(item => item) as any[] }; } @@ -390,8 +363,8 @@ export const AppMenuBuilder = new class AppMenuBuilderImpl implements AppMenuBui const getConversationId = async () => { const state = await getState(); const { editors, activeEditor } = state.editor; - const { activeDocumentId } = editors[activeEditor]; - return state.chat.chats[activeDocumentId].conversationId; + const { activeDocumentId } = editors[ activeEditor ]; + return state.chat.chats[ activeDocumentId ].conversationId; }; const getServiceUrl = () => emulator.framework.serverUrl.replace('[::]', 'localhost'); @@ -447,16 +420,17 @@ export const AppMenuBuilder = new class AppMenuBuilderImpl implements AppMenuBui */ setFileMenu(fileMenuTemplate: MenuOpts, appMenuTemplate: MenuOpts[]): MenuOpts[] { if (process.platform === 'darwin') { - appMenuTemplate[1] = fileMenuTemplate; + appMenuTemplate[ 1 ] = fileMenuTemplate; } else { - appMenuTemplate[0] = fileMenuTemplate; + appMenuTemplate[ 0 ] = fileMenuTemplate; } return appMenuTemplate; } refreshAppUpdateMenu() { - jsonpath.value(this.menuTemplate, - '$..[?(@.role == "help")].submenu[?(@.id == "auto-update")]', this.getUpdateMenuItem()); + const helpMenu = this.menuTemplate.find(menuItem => menuItem.role === 'help'); + const autoUpdateMenuItem = (helpMenu.submenu as Array).find(menuItem => menuItem.id === 'auto-update'); + Object.assign(autoUpdateMenuItem, this.getUpdateMenuItem()); Electron.Menu.setApplicationMenu(Electron.Menu.buildFromTemplate(this.menuTemplate)); } }; diff --git a/packages/app/main/src/commands/clientInitCommands.ts b/packages/app/main/src/commands/clientInitCommands.ts index be4c4ad88..06e917803 100644 --- a/packages/app/main/src/commands/clientInitCommands.ts +++ b/packages/app/main/src/commands/clientInitCommands.ts @@ -38,13 +38,13 @@ import { ExtensionManagerImpl } from '../extensions'; import { Protocol } from '../constants'; import { ProtocolHandler } from '../protocolHandler'; import { getStore } from '../botData/store'; -import { getBotsFromDisk, readFileSync } from '../utils'; -import * as Path from 'path'; +import { getBotsFromDisk } from '../utils'; import { CommandRegistryImpl } from '@bfemulator/sdk-shared'; import { ClientAwareSettings, Settings, SharedConstants } from '@bfemulator/app-shared'; import { Migrator } from '../migrator'; import { getStore as getSettingsStore } from '../settingsData/store'; import { Store } from 'redux'; +import { openFileFromCommandLine } from '../utils/openFileFromCommandLine'; /** Registers client initialization commands */ export function registerCommands(commandRegistry: CommandRegistryImpl) { @@ -97,30 +97,9 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) { } // Parse command line args to see if we are opening a .bot or .transcript file - if (args.some(arg => /(\.transcript)|(\.bot)$/.test(arg))) { - const fileToBeOpened = args.find(arg => /(\.transcript)|(\.bot)$/.test(arg)); - if (Path.extname(fileToBeOpened) === '.bot') { - try { - const bot = await mainWindow.commandService.call(Commands.Bot.Open, fileToBeOpened); - await mainWindow.commandService.call(Commands.Bot.SetActive, bot); - await mainWindow.commandService.remoteCall(Commands.Bot.Load, bot); - } catch (e) { - throw new Error(`Error while trying to open a .bot file via double click at: ${fileToBeOpened}`); - } - } else if (Path.extname(fileToBeOpened) === '.transcript') { - const transcript = readFileSync(fileToBeOpened); - const conversationActivities = JSON.parse(transcript); - if (!Array.isArray(conversationActivities)) { - throw new Error('Invalid transcript file contents; should be an array of conversation activities.'); - } - - // open a transcript on the client side and pass in - // some extra info to differentiate it from a transcript on disk - await mainWindow.commandService.remoteCall(Commands.Emulator.OpenTranscript, 'deepLinkedTranscript', { - activities: conversationActivities, - inMemory: true - }); - } + const fileToBeOpened = args.find(arg => /(\.transcript)|(\.bot)$/.test(arg)); + if (fileToBeOpened) { + await openFileFromCommandLine(fileToBeOpened, mainWindow.commandService); } }); } diff --git a/packages/app/main/src/main.ts b/packages/app/main/src/main.ts index c66791263..61920ceb8 100644 --- a/packages/app/main/src/main.ts +++ b/packages/app/main/src/main.ts @@ -57,6 +57,7 @@ import { azureLoggedInUserChanged } from './settingsData/actions/azureAuthAction import { ngrokEmitter } from './ngrok'; import { sendNotificationToClient } from './utils/sendNotificationToClient'; import Users from '@bfemulator/emulator-core/lib/facility/users'; +import { openFileFromCommandLine } from './utils/openFileFromCommandLine'; export let mainWindow: Window; export let windowManager: WindowManager; @@ -89,7 +90,7 @@ AppUpdater.on('update-available', (update: UpdateInfo) => { mainWindow.commandService.call(SharedConstants.Commands.Electron.ShowMessageBox, true, { title: app.getName(), message: `An update is available. Download it now?`, - buttons: ['Cancel', 'OK'], + buttons: [ 'Cancel', 'OK' ], defaultId: 1, cancelId: 0 }).then(result => { @@ -107,7 +108,7 @@ AppUpdater.on('update-downloaded', (update: UpdateInfo) => { mainWindow.commandService.call(SharedConstants.Commands.Electron.ShowMessageBox, true, { title: app.getName(), message: 'Finished downloading update. Restart and install now?', - buttons: ['Cancel', 'OK'], + buttons: [ 'Cancel', 'OK' ], defaultId: 1, cancelId: 0 }).then(result => { @@ -202,6 +203,15 @@ Electron.app.on('will-finish-launching', () => { Electron.app.on('open-url', onOpenUrl); }); +let fileToOpen: string; +Electron.app.on('open-file', async (event: Event, file: string) => { + if (!mainWindow || !mainWindow.commandService) { + fileToOpen = file; + } else { + await openFileFromCommandLine(file, mainWindow.commandService); + } +}); + const windowIsOffScreen = function (windowBounds: Electron.Rectangle): boolean { const nearestDisplay = Electron.screen.getDisplayMatching(windowBounds).workArea; return ( @@ -372,6 +382,11 @@ const createMainWindow = async () => { await mainWindow.commandService.call(SharedConstants.Commands.Electron.UpdateFileMenu); } } + + if (fileToOpen) { + await openFileFromCommandLine(fileToOpen, mainWindow.commandService); + fileToOpen = null; + } }); mainWindow.browserWindow.on('close', async function (event: Event) { @@ -387,9 +402,9 @@ const createMainWindow = async () => { function loadMainPage() { let queryString = ''; - if (process.argv[1] && process.argv[1].indexOf('botemulator') !== -1) { + if (process.argv[ 1 ] && process.argv[ 1 ].indexOf('botemulator') !== -1) { // add a query string with the botemulator protocol handler content - queryString = '?' + process.argv[1]; + queryString = '?' + process.argv[ 1 ]; } let page = process.env.ELECTRON_TARGET_URL || url.format({ diff --git a/packages/app/main/src/services/contextMenuService.ts b/packages/app/main/src/services/contextMenuService.ts index 7915f8d38..7d01b7d11 100644 --- a/packages/app/main/src/services/contextMenuService.ts +++ b/packages/app/main/src/services/contextMenuService.ts @@ -31,12 +31,12 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -import { Menu, MenuItem, MenuItemConstructorOptions } from 'electron'; +import { Menu, MenuItemConstructorOptions } from 'electron'; export class ContextMenuService { private static currentMenu: Menu; - public static showMenuAndWaitForInput(options: Partial[]): Promise { + public static showMenuAndWaitForInput(options: Partial[] = []): Promise { if (ContextMenuService.currentMenu) { ContextMenuService.currentMenu.closePopup(); } @@ -53,7 +53,7 @@ export class ContextMenuService { }); const menu = ContextMenuService.currentMenu = Menu.buildFromTemplate(template); - menu.popup(); + menu.popup({}); }); } } diff --git a/packages/app/main/src/utils/openFileFromCommandLine.ts b/packages/app/main/src/utils/openFileFromCommandLine.ts new file mode 100644 index 000000000..10f76df43 --- /dev/null +++ b/packages/app/main/src/utils/openFileFromCommandLine.ts @@ -0,0 +1,31 @@ +import { readFileSync } from './readFileSync'; +import { CommandService } from '@bfemulator/sdk-shared'; +import * as path from 'path'; +import { SharedConstants } from '@bfemulator/app-shared'; + +export async function openFileFromCommandLine(fileToBeOpened: string, commandService: CommandService): Promise { + const { Bot, Emulator } = SharedConstants.Commands; + if (path.extname(fileToBeOpened) === '.bot') { + try { + + const bot = await commandService.call(Bot.Open, fileToBeOpened); + await commandService.call(Bot.SetActive, bot); + await commandService.remoteCall(Bot.Load, bot); + } catch (e) { + throw new Error(`Error while trying to open a .bot file via double click at: ${fileToBeOpened}`); + } + } else if (path.extname(fileToBeOpened) === '.transcript') { + const transcript = readFileSync(fileToBeOpened); + const conversationActivities = JSON.parse(transcript); + if (!Array.isArray(conversationActivities)) { + throw new Error('Invalid transcript file contents; should be an array of conversation activities.'); + } + + // open a transcript on the client side and pass in + // some extra info to differentiate it from a transcript on disk + await commandService.remoteCall(Emulator.OpenTranscript, 'deepLinkedTranscript', { + activities: conversationActivities, + inMemory: true + }); + } +} diff --git a/packages/extensions/luis/client/src/Controls/Editor/Editor.tsx b/packages/extensions/luis/client/src/Controls/Editor/Editor.tsx index 90151a60e..ce618b8da 100644 --- a/packages/extensions/luis/client/src/Controls/Editor/Editor.tsx +++ b/packages/extensions/luis/client/src/Controls/Editor/Editor.tsx @@ -66,19 +66,20 @@ class Editor extends Component { if (!this.props.recognizerResult || !this.props.recognizerResult.intents) { return { intent: NoneIntent, score: 0.0 }; } - let intents: { [key: string]: RecognizerResultIntent } = this.props.recognizerResult.intents; + let intents: { [ key: string ]: RecognizerResultIntent } = this.props.recognizerResult.intents; let topIntent = Object.keys(intents).reduce((a, b) => { - return intents[a].score > intents[b].score ? a : b; + return intents[ a ].score > intents[ b ].score ? a : b; }); - return { intent: topIntent, score: intents[topIntent].score }; + return { intent: topIntent, score: intents[ topIntent ].score }; } render() { let topScoringIntent = this.getTopScoringIntent(); let mode: IntentEditorMode; - if (this.props.appInfo.authorized) { + const { appInfo = { authorized: false, isDispatchApp: false } } = this.props; + if (appInfo.authorized) { mode = IntentEditorMode.Enabled; - } else if (this.props.appInfo.isDispatchApp) { + } else if (appInfo.isDispatchApp) { mode = IntentEditorMode.Hidden; } else { mode = IntentEditorMode.Disabled; diff --git a/packages/extensions/luis/client/src/Luis/Client.tsx b/packages/extensions/luis/client/src/Luis/Client.ts similarity index 77% rename from packages/extensions/luis/client/src/Luis/Client.tsx rename to packages/extensions/luis/client/src/Luis/Client.ts index 2c52c11c2..1fa5c3b54 100644 --- a/packages/extensions/luis/client/src/Luis/Client.tsx +++ b/packages/extensions/luis/client/src/Luis/Client.ts @@ -80,7 +80,7 @@ class LuisClient { private publishService: Publish; private luisAppInfo: LuisAppInfo; - private static getCacheKey(apiName: string, appId: string, versionId: string | undefined= undefined): string { + private static getCacheKey(apiName: string, appId: string, versionId: string | undefined = undefined): string { let key: string = apiName + '_' + appId; if (versionId) { key += '_'; @@ -113,8 +113,8 @@ class LuisClient { let r = await this.appsService.getApplicationInfo({ appId: this.luisAppInfo.appId }); let appInfo: AppInfo = {} as AppInfo; if (r.status === 401 || - // Cortana Built in app (static, user cannot author it) - (r.status === 400 && this.luisAppInfo.appId.toLowerCase() === CortanaAppId)) { + // Cortana Built in app (static, user cannot author it) + (r.status === 400 && this.luisAppInfo.appId.toLowerCase() === CortanaAppId)) { appInfo = { authorized: false, activeVersion: Unauthorized, @@ -155,12 +155,12 @@ class LuisClient { text: luisResponse.query, intentName: newIntent, entityLabels: luisResponse.entities.map(e => { - return { - entityName: this.getNormalizedEntityType(e.type), - startCharIndex: e.startIndex, - endCharIndex: e.endIndex - }; - }) + return { + entityName: this.getNormalizedEntityType(e.type), + startCharIndex: e.startIndex, + endCharIndex: e.endIndex + }; + }) }; let addLabelParapms: AddLabelParams = { @@ -177,7 +177,7 @@ class LuisClient { async publish(appInfo: AppInfo, staging: boolean): Promise { this.configureClient(); let endpointKey: string = staging ? 'STAGING' : 'PRODUCTION'; - let region: string = appInfo.endpoints[endpointKey].endpointRegion; + let region: string = appInfo.endpoints[ endpointKey ].endpointRegion; if (!region) { throw new LuisClientError('Unknown publishing region'); } @@ -186,7 +186,7 @@ class LuisClient { region: region, versionId: appInfo.activeVersion }; - let r = await this.publishService.publishApplication({appId: appInfo.appId}, applicationPublishRequest); + let r = await this.publishService.publishApplication({ appId: appInfo.appId }, applicationPublishRequest); if (r.status !== 201) { throw new LuisClientError('Publish Failed', r.status); } @@ -194,7 +194,7 @@ class LuisClient { async train(appInfo: AppInfo): Promise { this.configureClient(); - let r = await this.trainService.trainApplicationVersion({appId: appInfo.appId, versionId: appInfo.activeVersion}); + let r = await this.trainService.trainApplicationVersion({ appId: appInfo.appId, versionId: appInfo.activeVersion }); if (r.status !== 202) { throw new LuisClientError('Failed to queue training request', r.status); } @@ -202,27 +202,28 @@ class LuisClient { let retryCounter = 0; return new Promise((resolve, reject) => { let intervalId = setInterval(async () => { - r = await this.trainService.getVersionTrainingStatus({ - appId: appInfo.appId, - versionId: appInfo.activeVersion}); - - if (retryCounter++ >= TrainStatusRetryCount) { - clearInterval(intervalId); - reject('Failed to train the application'); - } - - if (r.status !== 200) { - return; - } - - let appTrainingStatus: ModelTrainStatus[] = await r.json(); - if (appTrainingStatus.every(s => - s.details.statusId === TrainStatus.UpToDate || - s.details.statusId === TrainStatus.Success )) { - clearInterval(intervalId); - resolve(); - } - }, WaitIntervalInMs); + r = await this.trainService.getVersionTrainingStatus({ + appId: appInfo.appId, + versionId: appInfo.activeVersion + }); + + if (retryCounter++ >= TrainStatusRetryCount) { + clearInterval(intervalId); + reject('Failed to train the application'); + } + + if (r.status !== 200) { + return; + } + + let appTrainingStatus: ModelTrainStatus[] = await r.json(); + if (appTrainingStatus.every(s => + s.details.statusId === TrainStatus.UpToDate || + s.details.statusId === TrainStatus.Success)) { + clearInterval(intervalId); + resolve(); + } + }, WaitIntervalInMs); }); }