From 874c3efa2c7b6a83ed569338d7e3feab87d4e636 Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Tue, 3 Nov 2020 16:08:19 +0100 Subject: [PATCH] ATL-663: Indicate alpha status. Updated the About dialog. Signed-off-by: Akos Kitta --- .../browser/arduino-ide-frontend-module.ts | 10 +-- .../src/browser/contributions/about.ts | 74 +++++++++++++++++++ .../src/browser/menu/arduino-menus.ts | 13 +++- .../src/browser/theia/core/about-dialog.ts | 25 ------- .../core/common-frontend-contribution.ts | 3 +- .../theia/workspace/workspace-service.ts | 4 +- .../src/common/protocol/config-service.ts | 2 +- .../theia/core/electron-main-menu-factory.ts | 8 +- .../src/node/arduino-daemon-impl.ts | 15 +++- .../src/node/config-service-impl.ts | 2 +- browser-app/package.json | 1 + electron-app/package.json | 1 + electron/build/template-package.json | 5 +- 13 files changed, 114 insertions(+), 49 deletions(-) create mode 100644 arduino-ide-extension/src/browser/contributions/about.ts delete mode 100644 arduino-ide-extension/src/browser/theia/core/about-dialog.ts diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts index 241b3b45..f131b1e2 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -57,8 +57,6 @@ import { TabBarDecoratorService } from './theia/core/tab-bar-decorator'; import { ProblemManager as TheiaProblemManager } from '@theia/markers/lib/browser'; import { ProblemManager } from './theia/markers/problem-manager'; import { BoardsAutoInstaller } from './boards/boards-auto-installer'; -import { AboutDialog as TheiaAboutDialog } from '@theia/core/lib/browser/about-dialog'; -import { AboutDialog } from './theia/core/about-dialog'; import { ShellLayoutRestorer } from './theia/core/shell-layout-restorer'; import { EditorMode } from './editor-mode'; import { ListItemRenderer } from './widgets/component-list/list-item-renderer'; @@ -121,11 +119,12 @@ import { OutputServiceImpl } from './output-service-impl'; import { OutputServicePath, OutputService } from '../common/protocol/output-service'; import { NotificationCenter } from './notification-center'; import { NotificationServicePath, NotificationServiceServer } from '../common/protocol'; +import { About } from './contributions/about'; const ElementQueries = require('css-element-queries/src/ElementQueries'); MonacoThemingService.register({ - id: 'arduinoTheme', + id: 'arduino-theme', label: 'Light (Arduino)', uiTheme: 'vs', json: require('../../src/browser/data/arduino.color-theme.json') @@ -289,10 +288,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(ProblemManager).toSelf().inSingletonScope(); rebind(TheiaProblemManager).toService(ProblemManager); - // About dialog to show the CLI version - bind(AboutDialog).toSelf().inSingletonScope(); - rebind(TheiaAboutDialog).toService(AboutDialog); - // Customized layout restorer that can restore the state in async way: https://github.com/eclipse-theia/theia/issues/6579 bind(ShellLayoutRestorer).toSelf().inSingletonScope(); rebind(TheiaShellLayoutRestorer).toService(ShellLayoutRestorer); @@ -324,6 +319,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { Contribution.configure(bind, BuiltInExamples); Contribution.configure(bind, LibraryExamples); Contribution.configure(bind, IncludeLibrary); + Contribution.configure(bind, About); bind(OutputServiceImpl).toSelf().inSingletonScope().onActivation(({ container }, outputService) => { WebSocketConnectionProvider.createProxy(container, OutputServicePath, outputService); diff --git a/arduino-ide-extension/src/browser/contributions/about.ts b/arduino-ide-extension/src/browser/contributions/about.ts new file mode 100644 index 00000000..77af7f39 --- /dev/null +++ b/arduino-ide-extension/src/browser/contributions/about.ts @@ -0,0 +1,74 @@ +import { inject, injectable } from 'inversify'; +import { remote } from 'electron'; +import { isOSX, isWindows } from '@theia/core/lib/common/os'; +import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; +import { Contribution, Command, MenuModelRegistry, CommandRegistry } from './contribution'; +import { ArduinoMenus } from '../menu/arduino-menus'; +import { ConfigService } from '../../common/protocol'; + +@injectable() +export class About extends Contribution { + + @inject(ClipboardService) + protected readonly clipboardService: ClipboardService; + + @inject(ConfigService) + protected readonly configService: ConfigService; + + registerCommands(registry: CommandRegistry): void { + registry.registerCommand(About.Commands.ABOUT_APP, { + execute: () => this.showAbout() + }); + } + + registerMenus(registry: MenuModelRegistry): void { + // On macOS we will get the `Quit ${YOUR_APP_NAME}` menu item natively, no need to duplicate it. + registry.registerMenuAction(ArduinoMenus.HELP__ABOUT_GROUP, { + commandId: About.Commands.ABOUT_APP.id, + label: `About${isOSX ? ` ${this.applicationName}` : ''}`, + order: '0' + }); + } + + async showAbout(): Promise { + const ideStatus = FrontendApplicationConfigProvider.get()['status']; + const { version, commit, status: cliStatus } = await this.configService.getVersion(); + const detail = ` +Version: ${remote.app.getVersion()} +CLI Version: ${version}${cliStatus ? ` ${cliStatus}` : ''} [${commit}] + +Copyright © ${new Date().getFullYear()} Arduino SA +`; + const ok = 'OK'; + const copy = 'Copy'; + const buttons = !isWindows && !isOSX ? [copy, ok] : [ok, copy]; + const { response } = await remote.dialog.showMessageBox(remote.getCurrentWindow(), { + message: `${this.applicationName}${ideStatus ? ` – ${ideStatus}` : ''}`, + title: `${this.applicationName}${ideStatus ? ` – ${ideStatus}` : ''}`, + type: 'info', + detail, + buttons, + noLink: true, + defaultId: buttons.indexOf(ok), + cancelId: buttons.indexOf(ok) + }); + + if (buttons[response] === copy) { + await this.clipboardService.writeText(detail); + } + } + + protected get applicationName(): string { + return FrontendApplicationConfigProvider.get().applicationName; + } + +} + +export namespace About { + export namespace Commands { + export const ABOUT_APP: Command = { + id: 'arduino-about' + }; + } +} diff --git a/arduino-ide-extension/src/browser/menu/arduino-menus.ts b/arduino-ide-extension/src/browser/menu/arduino-menus.ts index ea596910..1ed14cf8 100644 --- a/arduino-ide-extension/src/browser/menu/arduino-menus.ts +++ b/arduino-ide-extension/src/browser/menu/arduino-menus.ts @@ -1,6 +1,6 @@ -import { MAIN_MENU_BAR } from '@theia/core/lib/common/menu'; +import { isOSX } from '@theia/core/lib/common/os'; import { CommonMenus } from '@theia/core/lib/browser/common-frontend-contribution'; -import { isOSX } from '@theia/core'; +import { MAIN_MENU_BAR } from '@theia/core/lib/common/menu'; export namespace ArduinoMenus { @@ -40,7 +40,14 @@ export namespace ArduinoMenus { // Core settings, such as `Processor` and `Programmers` for the board and `Burn Bootloader` export const TOOLS__BOARD_SETTINGS_GROUP = [...TOOLS, '1_board_settings']; - // Context menu + // -- Help + // `About` group + // XXX: on macOS, the about group is not under `Help` + export const HELP__ABOUT_GROUP = [...(isOSX ? MAIN_MENU_BAR : CommonMenus.HELP), '999_about']; + + // ------------ + + // Context menus // -- Open export const OPEN_SKETCH__CONTEXT = ['arduino-open-sketch--context']; export const OPEN_SKETCH__CONTEXT__OPEN_GROUP = [...OPEN_SKETCH__CONTEXT, '0_open']; diff --git a/arduino-ide-extension/src/browser/theia/core/about-dialog.ts b/arduino-ide-extension/src/browser/theia/core/about-dialog.ts deleted file mode 100644 index e4ec9962..00000000 --- a/arduino-ide-extension/src/browser/theia/core/about-dialog.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { injectable, inject, postConstruct } from 'inversify'; -import { AboutDialog as TheiaAboutDialog, ABOUT_CONTENT_CLASS } from '@theia/core/lib/browser/about-dialog'; -import { ConfigService } from '../../../common/protocol/config-service'; - -@injectable() -export class AboutDialog extends TheiaAboutDialog { - - @inject(ConfigService) - protected readonly configService: ConfigService; - - @postConstruct() - protected async init(): Promise { - const [, version] = await Promise.all([super.init(), this.configService.getVersion()]); - if (version) { - const { firstChild } = this.contentNode; - if (firstChild instanceof HTMLElement && firstChild.classList.contains(ABOUT_CONTENT_CLASS)) { - const cliVersion = document.createElement('div'); - cliVersion.textContent = version; - firstChild.appendChild(cliVersion); - // TODO: anchor to the commit in the `arduino-cli` repository. - } - } - } - -} diff --git a/arduino-ide-extension/src/browser/theia/core/common-frontend-contribution.ts b/arduino-ide-extension/src/browser/theia/core/common-frontend-contribution.ts index 3517f6df..a7560613 100644 --- a/arduino-ide-extension/src/browser/theia/core/common-frontend-contribution.ts +++ b/arduino-ide-extension/src/browser/theia/core/common-frontend-contribution.ts @@ -19,7 +19,8 @@ export class CommonFrontendContribution extends TheiaCommonFrontendContribution CommonCommands.AUTO_SAVE, CommonCommands.OPEN_PREFERENCES, CommonCommands.SELECT_ICON_THEME, - CommonCommands.SELECT_COLOR_THEME + CommonCommands.SELECT_COLOR_THEME, + CommonCommands.ABOUT_COMMAND ]) { registry.unregisterMenuAction(command); } diff --git a/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts b/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts index 4f3b7668..9960eec6 100644 --- a/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts +++ b/arduino-ide-extension/src/browser/theia/workspace/workspace-service.ts @@ -7,6 +7,7 @@ import { ApplicationServer } from '@theia/core/lib/common/application-protocol'; import { FrontendApplication } from '@theia/core/lib/browser/frontend-application'; import { FocusTracker, Widget } from '@theia/core/lib/browser'; import { WorkspaceService as TheiaWorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; import { ConfigService } from '../../../common/protocol/config-service'; import { SketchesService } from '../../../common/protocol/sketches-service'; import { ArduinoWorkspaceRootResolver } from '../../arduino-workspace-resolver'; @@ -97,8 +98,9 @@ export class WorkspaceService extends TheiaWorkspaceService { } protected formatTitle(title?: string): string { + const status = FrontendApplicationConfigProvider.get()['status']; const version = this.version ? ` ${this.version}` : ''; - const name = `${this.applicationName} ${version}`; + const name = `${this.applicationName}${status ? ` – ${status}` : ''} ${version}`; return title ? `${title} | ${name}` : name; } diff --git a/arduino-ide-extension/src/common/protocol/config-service.ts b/arduino-ide-extension/src/common/protocol/config-service.ts index 0776eacd..82c22456 100644 --- a/arduino-ide-extension/src/common/protocol/config-service.ts +++ b/arduino-ide-extension/src/common/protocol/config-service.ts @@ -1,7 +1,7 @@ export const ConfigServicePath = '/services/config-service'; export const ConfigService = Symbol('ConfigService'); export interface ConfigService { - getVersion(): Promise; + getVersion(): Promise>; getConfiguration(): Promise; getCliConfigFileUri(): Promise; getConfigurationFileSchemaUri(): Promise; diff --git a/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts b/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts index 8d0ad110..caf783e7 100644 --- a/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts +++ b/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts @@ -20,13 +20,13 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory { const { submenu } = super.createOSXMenu(); const label = 'Arduino Pro IDE'; if (!!submenu && !(submenu instanceof remote.Menu)) { - const [about, , ...rest] = submenu; - const menuModel = this.menuProvider.getMenu(ArduinoMenus.FILE__SETTINGS_GROUP); - const settings = this.fillMenuTemplate([], menuModel); + const [/* about */, /* settings */, ...rest] = submenu; + const about = this.fillMenuTemplate([], this.menuProvider.getMenu(ArduinoMenus.HELP__ABOUT_GROUP)); + const settings = this.fillMenuTemplate([], this.menuProvider.getMenu(ArduinoMenus.FILE__SETTINGS_GROUP)); return { label, submenu: [ - about, // TODO: we have two about dialogs! one from electron the other from Theia. + ...about, { type: 'separator' }, ...settings, { type: 'separator' }, diff --git a/arduino-ide-extension/src/node/arduino-daemon-impl.ts b/arduino-ide-extension/src/node/arduino-daemon-impl.ts index d7a4d881..7ca18638 100644 --- a/arduino-ide-extension/src/node/arduino-daemon-impl.ts +++ b/arduino-ide-extension/src/node/arduino-daemon-impl.ts @@ -105,9 +105,20 @@ export class ArduinoDaemonImpl implements ArduinoDaemon, BackendApplicationContr return this._execPath; } - async getVersion(): Promise { + async getVersion(): Promise> { const execPath = await this.getExecPath(); - return spawnCommand(`"${execPath}"`, ['version'], this.onError.bind(this)); + const raw = await spawnCommand(`"${execPath}"`, ['version', '--format', 'json'], this.onError.bind(this)); + try { + // The CLI `Info` object: https://github.com/arduino/arduino-cli/blob/17d24eb901b1fdaa5a4ec7da3417e9e460f84007/version/version.go#L31-L34 + const { VersionString, Commit, Status } = JSON.parse(raw); + return { + version: VersionString, + commit: Commit, + status: Status + }; + } catch { + return { version: raw, commit: raw }; + } } protected async getSpawnArgs(): Promise { diff --git a/arduino-ide-extension/src/node/config-service-impl.ts b/arduino-ide-extension/src/node/config-service-impl.ts index a78f7d7f..abe7c221 100644 --- a/arduino-ide-extension/src/node/config-service-impl.ts +++ b/arduino-ide-extension/src/node/config-service-impl.ts @@ -84,7 +84,7 @@ export class ConfigServiceImpl implements BackendApplicationContribution, Config return this.configChangeEmitter.event; } - async getVersion(): Promise { + async getVersion(): Promise> { return this.daemon.getVersion(); } diff --git a/browser-app/package.json b/browser-app/package.json index 1e8e7ed5..43e969b7 100644 --- a/browser-app/package.json +++ b/browser-app/package.json @@ -34,6 +34,7 @@ "config": { "applicationName": "Arduino Pro IDE", "defaultTheme": "arduino-theme", + "status": "Alpha", "preferences": { "editor.autoSave": "on", "editor.minimap.enabled": false, diff --git a/electron-app/package.json b/electron-app/package.json index c76bf2c6..1675af5c 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -37,6 +37,7 @@ "config": { "applicationName": "Arduino Pro IDE", "defaultTheme": "arduino-theme", + "status": "Alpha", "preferences": { "editor.autoSave": "on", "editor.minimap.enabled": false, diff --git a/electron/build/template-package.json b/electron/build/template-package.json index b465a7fe..44a4f987 100644 --- a/electron/build/template-package.json +++ b/electron/build/template-package.json @@ -38,20 +38,17 @@ "target": "electron", "frontend": { "config": { - "applicationName": "Arduino Pro IDE", "disallowReloadKeybinding": true } }, "backend": { "config": { - "singleInstance": true, - "configDirName": ".arduinoProIDE" + "singleInstance": true } } }, "build": { "productName": "Arduino Pro IDE", - "appId": "arduino.ProIDE", "asar": false, "directories": { "buildResources": "resources"