diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 651d77e9a..d0bdd278a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,9 @@ jobs: if [ $IS_FORK = true ]; then echo "Skipping the app signing: building from a fork." else + export BUILD_SUFFIX="linux"; if [ "${{ runner.OS }}" = "macOS" ]; then + export BUILD_SUFFIX="mac"; export CSC_LINK="${{ runner.temp }}/signing_certificate.p12" # APPLE_SIGNING_CERTIFICATE_P12 secret was produced by following the procedure from: # https://www.kencochrane.com/2020/08/01/build-and-sign-golang-binaries-for-macos-with-github-actions/#exporting-the-developer-certificate @@ -69,6 +71,7 @@ jobs: export CSC_KEY_PASSWORD="${{ secrets.KEYCHAIN_PASSWORD }}" elif [ "${{ runner.OS }}" = "Windows" ]; then + export BUILD_SUFFIX=""; export CSC_LINK="${{ runner.temp }}/signing_certificate.pfx" npm config set msvs_version 2017 --global echo "${{ secrets.WINDOWS_SIGNING_CERTIFICATE_PFX }}" | base64 --decode > "$CSC_LINK" @@ -78,7 +81,15 @@ jobs: fi yarn --cwd ./electron/packager/ - yarn --cwd ./electron/packager/ package + yarn --cwd ./electron/packager/ package + + export BUILD_PREFIX="stable" + if [ "$IS_NIGHTLY" = true ]; then + export BUILD_PREFIX="nightly" + fi + + mv electron/build/dist/latest-$BUILD_SUFFIX.yml electron/build/dist/$BUILD_PREFIX-$BUILD_SUFFIX.yml + rm electron/build/dist/alpha* electron/build/dist/beta* - name: Upload [GitHub Actions] uses: actions/upload-artifact@v2 @@ -98,7 +109,9 @@ jobs: - path: '*Linux_64bit.zip' name: Linux_X86-64 - path: '*macOS_64bit.dmg' - name: macOS + name: macOS_dmg + - path: '*macOS_64bit.zip' + name: macOS_zip - path: '*Windows_64bit.exe' name: Windows_X86-64_interactive_installer - path: '*Windows_64bit.msi' diff --git a/arduino-ide-extension/package.json b/arduino-ide-extension/package.json index 2d6ff2b63..3f21181c1 100644 --- a/arduino-ide-extension/package.json +++ b/arduino-ide-extension/package.json @@ -65,6 +65,7 @@ "css-element-queries": "^1.2.0", "dateformat": "^3.0.3", "deepmerge": "2.0.1", + "electron-updater": "^4.6.5", "fuzzy": "^0.1.3", "glob": "^7.1.6", "google-protobuf": "^3.11.4", @@ -82,6 +83,7 @@ "ps-tree": "^1.2.0", "query-string": "^7.0.1", "react-disable": "^0.1.0", + "react-markdown": "^8.0.0", "react-select": "^3.0.4", "react-tabs": "^3.1.2", "react-window": "^1.8.6", diff --git a/arduino-ide-extension/scripts/compose-changelog.js b/arduino-ide-extension/scripts/compose-changelog.js index 36b5c94c0..aba9dae46 100755 --- a/arduino-ide-extension/scripts/compose-changelog.js +++ b/arduino-ide-extension/scripts/compose-changelog.js @@ -1,36 +1,42 @@ // @ts-check - (async () => { const { Octokit } = require('@octokit/rest'); - const fs = require("fs"); - const path = require("path"); + const fs = require('fs'); + const path = require('path'); const octokit = new Octokit({ userAgent: 'Arduino IDE compose-changelog.js', }); - const response = await octokit.rest.repos.listReleases({ - owner: 'arduino', - repo: 'arduino-ide', - }).catch(err => { - console.error(err); - process.exit(1); - }) + const response = await octokit.rest.repos + .listReleases({ + owner: 'arduino', + repo: 'arduino-ide', + }) + .catch((err) => { + console.error(err); + process.exit(1); + }); const releases = response.data; - let fullChangelog = releases.reduce((acc, item) => { + let fullChangelog = releases.reduce((acc, item, index) => { // Process each line separately - const body = item.body.split('\n').map(processLine).join('\n') + const body = item.body.split('\n').map(processLine).join('\n'); // item.name is the name of the release changelog - return acc + `# ${item.name}\n\n${body}\n\n---\n\n`; + return ( + acc + + `## ${item.name}\n\n${body}${ + index !== releases.length - 1 ? '\n\n---\n\n' : '\n' + }` + ); }, ''); - const args = process.argv.slice(2) + const args = process.argv.slice(2); if (args.length == 0) { - console.error("Missing argument to destination file") - process.exit(1) + console.error('Missing argument to destination file'); + process.exit(1); } const changelogFile = path.resolve(args[0]); @@ -38,19 +44,18 @@ changelogFile, fullChangelog, { - flag: "w+", + flag: 'w+', }, - err => { + (err) => { if (err) { console.error(err); process.exit(1); } - console.log("Changelog written to", changelogFile); + console.log('Changelog written to', changelogFile); } - ) + ); })(); - // processLine applies different substitutions to line string. // We're assuming that there are no more than one substitution // per line to be applied. @@ -61,7 +66,8 @@ const processLine = (line) => { // * [#123](https://github.com/arduino/arduino-ide/pull/123/) // * [#123](https://github.com/arduino/arduino-ide/issues/123/) // If it does return the line as is. - let r = /(\(|\[)#\d+(\)|\])(\(|\[)https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?(\)|\])/gm; + let r = + /(\(|\[)#\d+(\)|\])(\(|\[)https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?(\)|\])/gm; if (r.test(line)) { return line; } @@ -70,9 +76,12 @@ const processLine = (line) => { // * #123 // If it does it's changed to: // * [#123](https://github.com/arduino/arduino-ide/pull/123) - r = /#(\d+)/gm; + r = /(? { // * [#123](https://github.com/arduino/arduino-ide/issues/123) // * [#123](https://github.com/arduino/arduino-ide/pull/123/) // * [#123](https://github.com/arduino/arduino-ide/issues/123/) - r = /(https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?)/gm; + r = + /(https:\/\/github\.com\/arduino\/arduino-ide\/(pull|issues)\/(\d+)\/?)/gm; if (r.test(line)) { return line.replace(r, `[#$3]($1)`); } @@ -95,11 +105,12 @@ const processLine = (line) => { // * https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3/ // If it does it's changed to: // * [`2.0.0-rc2...2.0.0-rc3`](https://github.com/arduino/arduino-ide/compare/2.0.0-rc2...2.0.0-rc3) - r = /(https:\/\/github\.com\/arduino\/arduino-ide\/compare\/([^\/]*))\/?\s?/gm; + r = + /(https:\/\/github\.com\/arduino\/arduino-ide\/compare\/([^\/]*))\/?\s?/gm; if (r.test(line)) { - return line.replace(r, '[`$2`]($1)');; + return line.replace(r, '[`$2`]($1)'); } // If nothing matches just return the line as is return line; -} +}; diff --git a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx index b493bd3bf..7b0184bdd 100644 --- a/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx +++ b/arduino-ide-extension/src/browser/arduino-frontend-contribution.tsx @@ -68,8 +68,12 @@ import { ArduinoPreferences } from './arduino-preferences'; import { SketchesServiceClientImpl } from '../common/protocol/sketches-service-client-impl'; import { SaveAsSketch } from './contributions/save-as-sketch'; import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution'; +import { IDEUpdaterCommands } from './ide-updater/ide-updater-commands'; +import { IDEUpdaterDialog } from './dialogs/ide-updater/ide-updater-dialog'; +import { IDEUpdater } from '../common/protocol/ide-updater'; const INIT_LIBS_AND_PACKAGES = 'initializedLibsAndPackages'; +export const SKIP_IDE_VERSION = 'skipIDEVersion'; @injectable() export class ArduinoFrontendContribution @@ -78,8 +82,7 @@ export class ArduinoFrontendContribution TabBarToolbarContribution, CommandContribution, MenuContribution, - ColorContribution -{ + ColorContribution { @inject(ILogger) protected logger: ILogger; @@ -157,6 +160,15 @@ export class ArduinoFrontendContribution @inject(LocalStorageService) protected readonly localStorageService: LocalStorageService; + @inject(IDEUpdaterCommands) + protected readonly updater: IDEUpdaterCommands; + + @inject(IDEUpdaterDialog) + protected readonly updaterDialog: IDEUpdaterDialog; + + @inject(IDEUpdater) + protected readonly updaterService: IDEUpdater; + protected invalidConfigPopup: | Promise | undefined; @@ -251,7 +263,7 @@ export class ArduinoFrontendContribution }); } - onStart(app: FrontendApplication): void { + async onStart(app: FrontendApplication): Promise { // Initialize all `pro-mode` widgets. This is a NOOP if in normal mode. for (const viewContribution of [ this.fileNavigatorContributions, @@ -266,6 +278,19 @@ export class ArduinoFrontendContribution viewContribution.initializeLayout(app); } } + + this.updaterService.init( + this.arduinoPreferences.get('arduino.ide.updateChannel') + ); + this.updater.checkForUpdates().then(async (updateInfo) => { + if (!updateInfo) return; + const versionToSkip = await this.localStorageService.getData( + SKIP_IDE_VERSION + ); + if (versionToSkip === updateInfo.version) return; + this.updaterDialog.open(updateInfo); + }); + const start = async ({ selectedBoard }: BoardsConfig.Config) => { if (selectedBoard) { const { name, fqbn } = selectedBoard; 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 700012bbd..5ceae9179 100644 --- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts +++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts @@ -262,6 +262,19 @@ import { UserFieldsDialogWidget, } from './dialogs/user-fields/user-fields-dialog'; import { nls } from '@theia/core/lib/common'; +import { IDEUpdaterCommands } from './ide-updater/ide-updater-commands'; +import { + IDEUpdater, + IDEUpdaterClient, + IDEUpdaterPath, +} from '../common/protocol/ide-updater'; +import { IDEUpdaterClientImpl } from './ide-updater/ide-updater-client-impl'; +import { + IDEUpdaterDialog, + IDEUpdaterDialogProps, + IDEUpdaterDialogWidget, +} from './dialogs/ide-updater/ide-updater-dialog'; +import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider'; const ElementQueries = require('css-element-queries/src/ElementQueries'); @@ -407,8 +420,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(SerialService) .toDynamicValue((context) => { const connection = context.container.get(WebSocketConnectionProvider); - const client = - context.container.get(SerialServiceClient); + const client = context.container.get( + SerialServiceClient + ); return connection.createProxy(SerialServicePath, client); }) .inSingletonScope(); @@ -472,12 +486,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { .inSingletonScope(); rebind(TheiaEditorWidgetFactory).to(EditorWidgetFactory).inSingletonScope(); rebind(TabBarToolbarFactory).toFactory( - ({ container: parentContainer }) => - () => { - const container = parentContainer.createChild(); - container.bind(TabBarToolbar).toSelf().inSingletonScope(); - return container.get(TabBarToolbar); - } + ({ container: parentContainer }) => () => { + const container = parentContainer.createChild(); + container.bind(TabBarToolbar).toSelf().inSingletonScope(); + return container.get(TabBarToolbar); + } ); bind(OutputWidget).toSelf().inSingletonScope(); rebind(TheiaOutputWidget).toService(OutputWidget); @@ -642,13 +655,15 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { // Enable the dirty indicator on uncloseable widgets. rebind(TabBarRendererFactory).toFactory((context) => () => { - const contextMenuRenderer = - context.container.get(ContextMenuRenderer); + const contextMenuRenderer = context.container.get( + ContextMenuRenderer + ); const decoratorService = context.container.get( TabBarDecoratorService ); - const iconThemeService = - context.container.get(IconThemeService); + const iconThemeService = context.container.get( + IconThemeService + ); return new TabBarRenderer( contextMenuRenderer, decoratorService, @@ -756,9 +771,32 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { title: 'UploadCertificate', }); + bind(IDEUpdaterDialogWidget).toSelf().inSingletonScope(); + bind(IDEUpdaterDialog).toSelf().inSingletonScope(); + bind(IDEUpdaterDialogProps).toConstantValue({ + title: 'IDEUpdater', + }); + bind(UserFieldsDialogWidget).toSelf().inSingletonScope(); bind(UserFieldsDialog).toSelf().inSingletonScope(); bind(UserFieldsDialogProps).toConstantValue({ title: 'UserFields', }); + + bind(IDEUpdaterCommands).toSelf().inSingletonScope(); + bind(CommandContribution).toService(IDEUpdaterCommands); + + // Frontend binding for the IDE Updater service + bind(IDEUpdaterClientImpl).toSelf().inSingletonScope(); + bind(IDEUpdaterClient).toService(IDEUpdaterClientImpl); + bind(IDEUpdater) + .toDynamicValue((context) => { + const client = context.container.get(IDEUpdaterClientImpl); + return ElectronIpcConnectionProvider.createProxy( + context.container, + IDEUpdaterPath, + client + ); + }) + .inSingletonScope(); }); diff --git a/arduino-ide-extension/src/browser/arduino-preferences.ts b/arduino-ide-extension/src/browser/arduino-preferences.ts index 5e1013a1d..910d724bc 100644 --- a/arduino-ide-extension/src/browser/arduino-preferences.ts +++ b/arduino-ide-extension/src/browser/arduino-preferences.ts @@ -9,6 +9,11 @@ import { import { nls } from '@theia/core/lib/common'; import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol'; +export enum UpdateChannel { + Stable = 'stable', + Nightly = 'nightly', +} + export const ArduinoConfigSchema: PreferenceSchema = { type: 'object', properties: { @@ -64,13 +69,14 @@ export const ArduinoConfigSchema: PreferenceSchema = { ), default: 0, }, - 'arduino.ide.autoUpdate': { - type: 'boolean', + 'arduino.ide.updateChannel': { + type: 'string', + enum: Object.values(UpdateChannel) as UpdateChannel[], + default: UpdateChannel.Stable, description: nls.localize( - 'arduino/preferences/ide.autoUpdate', - 'True to enable automatic update checks. The IDE will check for updates automatically and periodically.' + 'arduino/preferences/ide.updateChannel', + "Release channel to get updated from. 'stable' is the stable release, 'nightly' is the latest development build." ), - default: true, }, 'arduino.board.certificates': { type: 'string', @@ -171,7 +177,7 @@ export interface ArduinoConfiguration { 'arduino.upload.verify': boolean; 'arduino.window.autoScale': boolean; 'arduino.window.zoomLevel': number; - 'arduino.ide.autoUpdate': boolean; + 'arduino.ide.updateChannel': UpdateChannel; 'arduino.board.certificates': string; 'arduino.sketchbook.showAllFiles': boolean; 'arduino.cloud.enabled': boolean; @@ -188,16 +194,10 @@ export interface ArduinoConfiguration { export const ArduinoPreferences = Symbol('ArduinoPreferences'); export type ArduinoPreferences = PreferenceProxy; -export function createArduinoPreferences( - preferences: PreferenceService -): ArduinoPreferences { - return createPreferenceProxy(preferences, ArduinoConfigSchema); -} - export function bindArduinoPreferences(bind: interfaces.Bind): void { bind(ArduinoPreferences).toDynamicValue((ctx) => { const preferences = ctx.container.get(PreferenceService); - return createArduinoPreferences(preferences); + return createPreferenceProxy(preferences, ArduinoConfigSchema); }); bind(PreferenceContribution).toConstantValue({ schema: ArduinoConfigSchema, diff --git a/arduino-ide-extension/src/browser/components/ProgressBar.tsx b/arduino-ide-extension/src/browser/components/ProgressBar.tsx new file mode 100644 index 000000000..f91c9f991 --- /dev/null +++ b/arduino-ide-extension/src/browser/components/ProgressBar.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; + +export type ProgressBarProps = { + percent?: number; + showPercentage?: boolean; +}; + +export default function ProgressBar({ + percent = 0, + showPercentage = false, +}: ProgressBarProps): React.ReactElement { + const roundedPercent = Math.round(percent); + return ( +
+
+
+
+ {showPercentage && ( +
+
{roundedPercent}%
+
+ )} +
+ ); +} diff --git a/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx new file mode 100644 index 000000000..3c1c4911e --- /dev/null +++ b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx @@ -0,0 +1,159 @@ +import { nls } from '@theia/core/lib/common'; +import { shell } from 'electron'; +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import ReactMarkdown from 'react-markdown'; +import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater'; +import ProgressBar from '../../components/ProgressBar'; + +export type IDEUpdaterComponentProps = { + updateInfo: UpdateInfo; + downloadFinished?: boolean; + downloadStarted?: boolean; + progress?: ProgressInfo; + error?: Error; + onDownload: () => void; + onClose: () => void; + onSkipVersion: () => void; + onCloseAndInstall: () => void; +}; + +export const IDEUpdaterComponent = ({ + updateInfo: { version, releaseNotes }, + downloadStarted = false, + downloadFinished = false, + progress, + error, + onDownload, + onClose, + onSkipVersion, + onCloseAndInstall, +}: IDEUpdaterComponentProps): React.ReactElement => { + const changelogDivRef = React.useRef() as React.MutableRefObject< + HTMLDivElement + >; + React.useEffect(() => { + if (!!releaseNotes) { + let changelog: string; + if (typeof releaseNotes === 'string') changelog = releaseNotes; + else + changelog = releaseNotes.reduce((acc, item) => { + return item.note ? (acc += `${item.note}\n\n`) : acc; + }, ''); + ReactDOM.render( + ( + href && shell.openExternal(href)} {...props}> + {children} + + ), + }} + > + {changelog} + , + changelogDivRef.current + ); + } + }, [releaseNotes]); + const closeButton = ( + + ); + + return ( +
+ {downloadFinished ? ( +
+
+ {nls.localize( + 'arduino/ide-updater/versionDownloaded', + 'Arduino IDE {0} has been downloaded.', + version + )} +
+
+ {nls.localize( + 'arduino/ide-updater/closeToInstallNotice', + 'Close the software and install the update on your machine.' + )} +
+
+ {closeButton} + +
+
+ ) : downloadStarted ? ( +
+
+ {nls.localize( + 'arduino/ide-updater/downloadingNotice', + 'Downloading the latest version of the Arduino IDE.' + )} +
+ +
+ ) : ( +
+
+
+
+
+
+
+ {nls.localize( + 'arduino/ide-updater/updateAvailable', + 'Update Available' + )} +
+
+
+ {nls.localize( + 'arduino/ide-updater/newVersionAvailable', + 'A new version of Arduino IDE ({0}) is available for download.', + version + )} +
+ {releaseNotes && ( +
+
+
+ )} +
+ +
+ {closeButton} + +
+
+
+ )} + {!!error &&
{error}
} +
+ ); +}; diff --git a/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx new file mode 100644 index 000000000..56b8c84f5 --- /dev/null +++ b/arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx @@ -0,0 +1,166 @@ +import * as React from 'react'; +import { inject, injectable } from 'inversify'; +import { DialogProps } from '@theia/core/lib/browser/dialogs'; +import { AbstractDialog } from '../../theia/dialogs/dialogs'; +import { Widget } from '@phosphor/widgets'; +import { Message } from '@phosphor/messaging'; +import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; +import { nls } from '@theia/core'; +import { IDEUpdaterComponent } from './ide-updater-component'; +import { IDEUpdaterCommands } from '../../ide-updater/ide-updater-commands'; +import { + IDEUpdaterClient, + ProgressInfo, + UpdateInfo, +} from '../../../common/protocol/ide-updater'; +import { LocalStorageService } from '@theia/core/lib/browser'; +import { SKIP_IDE_VERSION } from '../../arduino-frontend-contribution'; + +@injectable() +export class IDEUpdaterDialogWidget extends ReactWidget { + protected isOpen = new Object(); + updateInfo: UpdateInfo; + progressInfo: ProgressInfo | undefined; + error: Error | undefined; + downloadFinished: boolean; + downloadStarted: boolean; + onClose: () => void; + + @inject(IDEUpdaterCommands) + protected readonly updater: IDEUpdaterCommands; + + @inject(IDEUpdaterClient) + protected readonly updaterClient: IDEUpdaterClient; + + @inject(LocalStorageService) + protected readonly localStorageService: LocalStorageService; + + init(updateInfo: UpdateInfo, onClose: () => void): void { + this.updateInfo = updateInfo; + this.progressInfo = undefined; + this.error = undefined; + this.downloadStarted = false; + this.downloadFinished = false; + this.onClose = onClose; + + this.updaterClient.onError((e) => { + this.error = e; + this.update(); + }); + this.updaterClient.onDownloadProgressChanged((e) => { + this.progressInfo = e; + this.update(); + }); + this.updaterClient.onDownloadFinished((e) => { + this.downloadFinished = true; + this.update(); + }); + } + + async onSkipVersion(): Promise { + this.localStorageService.setData( + SKIP_IDE_VERSION, + this.updateInfo.version + ); + this.close(); + } + + close(): void { + super.close(); + this.onClose(); + } + + onDispose(): void { + if (this.downloadStarted && !this.downloadFinished) + this.updater.stopDownload(); + } + + async onDownload(): Promise { + this.progressInfo = undefined; + this.downloadStarted = true; + this.error = undefined; + this.updater.downloadUpdate(); + this.update(); + } + + onCloseAndInstall(): void { + this.updater.quitAndInstall(); + } + + protected render(): React.ReactNode { + return !!this.updateInfo ? ( +
+ + + ) : null; + } +} + +@injectable() +export class IDEUpdaterDialogProps extends DialogProps {} + +@injectable() +export class IDEUpdaterDialog extends AbstractDialog { + @inject(IDEUpdaterDialogWidget) + protected readonly widget: IDEUpdaterDialogWidget; + + constructor( + @inject(IDEUpdaterDialogProps) + protected readonly props: IDEUpdaterDialogProps + ) { + super({ + title: nls.localize( + 'arduino/updater/ideUpdaterDialog', + 'Software Update' + ), + }); + this.contentNode.classList.add('ide-updater-dialog'); + this.acceptButton = undefined; + } + + get value(): UpdateInfo { + return this.widget.updateInfo; + } + + protected onAfterAttach(msg: Message): void { + if (this.widget.isAttached) { + Widget.detach(this.widget); + } + Widget.attach(this.widget, this.contentNode); + super.onAfterAttach(msg); + this.update(); + } + + async open( + data: UpdateInfo | undefined = undefined + ): Promise { + if (data && data.version) { + this.widget.init(data, this.close.bind(this)); + return super.open(); + } + } + + protected onUpdateRequest(msg: Message): void { + super.onUpdateRequest(msg); + this.widget.update(); + } + + protected onActivateRequest(msg: Message): void { + super.onActivateRequest(msg); + this.widget.activate(); + } + + close(): void { + this.widget.dispose(); + super.close(); + } +} diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx index 62c166c5e..e7fa7a060 100644 --- a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx +++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx @@ -260,18 +260,6 @@ export class SettingsComponent extends React.Component< 'Verify code after upload' )} -