Skip to content

Commit

Permalink
feat: introduce version welcome dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
giacomocusinato committed Nov 26, 2024
1 parent 51c40fa commit d19f588
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,10 @@ import {
import { TreeViewDecoratorService } from '@theia/plugin-ext/lib/main/browser/view/tree-view-decorator-service';
import { PLUGIN_VIEW_DATA_FACTORY_ID } from '@theia/plugin-ext/lib/main/browser/view/plugin-view-registry';
import { TreeViewWidget } from './theia/plugin-ext/tree-view-widget';
import {
VersionWelcomeDialog,
VersionWelcomeDialogProps,
} from './dialogs/version-welcome/version-welcome-dialog';

// Hack to fix copy/cut/paste issue after electron version update in Theia.
// https://github.com/eclipse-theia/theia/issues/12487
Expand Down Expand Up @@ -1014,6 +1018,11 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
title: 'IDEUpdater',
});

bind(VersionWelcomeDialog).toSelf().inSingletonScope();
bind(VersionWelcomeDialogProps).toConstantValue({
title: 'VersionWelcomeDialog',
});

bind(UserFieldsDialog).toSelf().inSingletonScope();
bind(UserFieldsDialogProps).toConstantValue({
title: 'UserFields',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '../../common/protocol/ide-updater';
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';
import { Contribution } from './contribution';
import { VersionWelcomeDialog } from '../dialogs/version-welcome/version-welcome-dialog';

@injectable()
export class CheckForIDEUpdates extends Contribution {
Expand All @@ -16,6 +17,9 @@ export class CheckForIDEUpdates extends Contribution {
@inject(IDEUpdaterDialog)
private readonly updaterDialog: IDEUpdaterDialog;

@inject(VersionWelcomeDialog)
private readonly welcomeDialog: VersionWelcomeDialog;

@inject(LocalStorageService)
private readonly localStorage: LocalStorageService;

Expand All @@ -37,6 +41,8 @@ export class CheckForIDEUpdates extends Contribution {
}

override onReady(): void {
this.welcomeDialog.open();

this.updater
.init(
this.preferences.get('arduino.ide.updateChannel'),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { nls } from '@theia/core/lib/common/nls';
import React from '@theia/core/shared/react';
// @ts-expect-error see https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
import type { Options } from 'react-markdown';
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
import ProgressBar from '../../components/ProgressBar';

const ReactMarkdown = React.lazy<React.ComponentType<Options>>(
// @ts-expect-error see above
() => import('react-markdown')
);

export interface UpdateProgress {
progressInfo?: ProgressInfo | undefined;
downloadFinished?: boolean;
downloadStarted?: boolean;
error?: Error;
}

export interface IDEUpdaterComponentProps {
updateInfo: UpdateInfo;
updateProgress: UpdateProgress;
openExternal: (url: string) => undefined;
}

export const IDEUpdaterComponent = ({
updateInfo,
updateProgress: {
downloadStarted = false,
downloadFinished = false,
progressInfo,
error,
},
openExternal,
}: IDEUpdaterComponentProps): React.ReactElement => {
const { version, releaseNotes } = updateInfo;
const [changelog, setChangelog] = React.useState<string>('');
React.useEffect(() => {
if (releaseNotes) {
setChangelog(
typeof releaseNotes === 'string'
? releaseNotes
: releaseNotes.reduce(
(acc, item) => (item.note ? (acc += `${item.note}\n\n`) : acc),
''
)
);
}
}, [releaseNotes, changelog]);

const DownloadCompleted: () => React.ReactElement = () => (
<div className="ide-updater-dialog--downloaded">
<div>
{nls.localize(
'arduino/ide-updater/versionDownloaded',
'Arduino IDE {0} has been downloaded.',
version
)}
</div>
<div>
{nls.localize(
'arduino/ide-updater/closeToInstallNotice',
'Close the software and install the update on your machine.'
)}
</div>
</div>
);

const DownloadStarted: () => React.ReactElement = () => (
<div className="ide-updater-dialog--downloading">
<div>
{nls.localize(
'arduino/ide-updater/downloadingNotice',
'Downloading the latest version of the Arduino IDE.'
)}
</div>
<ProgressBar percent={progressInfo?.percent} showPercentage />
</div>
);

const PreDownload: () => React.ReactElement = () => (
<div className="ide-updater-dialog--pre-download">
<div className="ide-updater-dialog--logo-container">
<div className="ide-updater-dialog--logo"></div>
</div>
<div className="ide-updater-dialog--new-version-text dialogSection">
<div className="dialogRow">
<div className="bold">
{nls.localize(
'arduino/ide-updater/updateAvailable',
'Update Available'
)}
</div>
</div>
<div className="dialogRow">
{nls.localize(
'arduino/ide-updater/newVersionAvailable',
'A new version of Arduino IDE ({0}) is available for download.',
version
)}
</div>
{changelog && (
<div className="dialogRow changelog-container">
<div className="changelog">
<React.Suspense
fallback={
<div className="fallback">
<div className="spinner" />
</div>
}
>
<ReactMarkdown
components={{
a: ({ href, children, ...props }) => (
<a onClick={() => href && openExternal(href)} {...props}>
{children}
</a>
),
}}
>
{changelog}
</ReactMarkdown>
</React.Suspense>
</div>
</div>
)}
</div>
</div>
);

const GoToDownloadPage: () => React.ReactElement = () => (
<div className="ide-updater-dialog--go-to-download-page">
<div>
{nls.localize(
'arduino/ide-updater/goToDownloadPage',
"An update for the Arduino IDE is available, but we're not able to download and install it automatically. Please go to the download page and download the latest version from there."
)}
</div>
</div>
);

return (
<div className="ide-updater-dialog--content">
{!!error ? (
<GoToDownloadPage />
) : downloadFinished ? (
<DownloadCompleted />
) : downloadStarted ? (
<DownloadStarted />
) : (
<PreDownload />
)}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React from '@theia/core/shared/react';
import { inject, injectable } from '@theia/core/shared/inversify';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import { ReactDialog } from '../../theia/dialogs/dialogs';
import { nls } from '@theia/core';
import { DialogProps } from '@theia/core/lib/browser';
import { WindowService } from '@theia/core/lib/browser/window/window-service';
import { AppService } from '../../app-service';

@injectable()
export class VersionWelcomeDialogProps extends DialogProps {}

@injectable()
export class VersionWelcomeDialog extends ReactDialog<void> {
// @inject(LocalStorageService)
// private readonly localStorageService: LocalStorageService;
// @inject(AppService)
// private readonly appService: AppService;
@inject(AppService)
private readonly appService: AppService;

@inject(WindowService)
private readonly windowService: WindowService;

constructor(
@inject(VersionWelcomeDialogProps)
protected override readonly props: VersionWelcomeDialogProps
) {
super({
title: nls.localize(
'arduino/ide-updater/ideUpdaterDialog',
'Welcome to the new Arduino IDE!'
),
});

this.node.id = 'version-welcome-dialog-container';
this.contentNode.classList.add('version-welcome-dialog');
}

protected render(): React.ReactNode {
return (
<div>
<p>
Arduino is committed to keeping software free and open-source for
everyone. Your donation helps us develop new features, improve
libraries, and support millions of users worldwide.
</p>
<p className="bold">
Please consider supporting our efforts to keep Arduino IDE free.
</p>
</div>
);
}

override get value(): void {
return;
}

private appendButtons(): void {
const cancelButton = this.createButton(
nls.localize('arduino/ide-updater/skipVersionButton', 'Skip Version')
);
cancelButton.classList.add('secondary');
cancelButton.classList.add('cancel-button');
this.addAction(cancelButton, this.close.bind(this), 'click');
this.controlPanel.appendChild(cancelButton);

const donateButton = this.createButton(
nls.localize('arduino/ide-updater/donateButton', 'Download')
);
this.addAction(donateButton, this.openDonationPage.bind(this), 'click');
this.controlPanel.appendChild(donateButton);
donateButton.focus();
}

private readonly openDonationPage = () => {
const url = 'https://www.arduino.cc/en/donate';
this.windowService.openNewWindow(url, { external: true });
};

private async updateTitleVersion(): Promise<void> {
debugger;
const appInfo = await this.appService.info();
const { appVersion } = appInfo;
const title = nls.localize(
'arduino/ide-updater/ideUpdaterDialog',
`Welcome to the new Arduino IDE ${appVersion}!`,
appVersion
);
console.log('title', this.titleNode);
this.titleNode.innerHTML = title;
}

protected override onAfterAttach(msg: Message): void {
this.update();
this.appendButtons();
this.updateTitleVersion();
super.onAfterAttach(msg);
}
}
1 change: 1 addition & 0 deletions arduino-ide-extension/src/browser/style/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
@import "./settings-dialog.css";
@import "./firmware-uploader-dialog.css";
@import "./ide-updater-dialog.css";
@import "./version-welcome-dialog.css";
@import "./certificate-uploader-dialog.css";
@import "./user-fields-dialog.css";
@import "./debug.css";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#version-welcome-dialog-container > .dialogBlock {
width: 546px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export class IDEUpdaterImpl implements IDEUpdater {
await this.updater.checkForUpdates();

this.cancellationToken = cancellationToken;
// replace with true to test
if (this.updater.currentVersion.compare(updateInfo.version) === -1) {
/*
'latest.txt' points to the latest changelog that has been generated by the CI,
Expand Down

0 comments on commit d19f588

Please sign in to comment.