Skip to content

Commit d19f588

Browse files
feat: introduce version welcome dialog
1 parent 51c40fa commit d19f588

File tree

7 files changed

+275
-0
lines changed

7 files changed

+275
-0
lines changed

arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+9
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,10 @@ import {
387387
import { TreeViewDecoratorService } from '@theia/plugin-ext/lib/main/browser/view/tree-view-decorator-service';
388388
import { PLUGIN_VIEW_DATA_FACTORY_ID } from '@theia/plugin-ext/lib/main/browser/view/plugin-view-registry';
389389
import { TreeViewWidget } from './theia/plugin-ext/tree-view-widget';
390+
import {
391+
VersionWelcomeDialog,
392+
VersionWelcomeDialogProps,
393+
} from './dialogs/version-welcome/version-welcome-dialog';
390394

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

1021+
bind(VersionWelcomeDialog).toSelf().inSingletonScope();
1022+
bind(VersionWelcomeDialogProps).toConstantValue({
1023+
title: 'VersionWelcomeDialog',
1024+
});
1025+
10171026
bind(UserFieldsDialog).toSelf().inSingletonScope();
10181027
bind(UserFieldsDialogProps).toConstantValue({
10191028
title: 'UserFields',

arduino-ide-extension/src/browser/contributions/check-for-ide-updates.ts

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from '../../common/protocol/ide-updater';
88
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';
99
import { Contribution } from './contribution';
10+
import { VersionWelcomeDialog } from '../dialogs/version-welcome/version-welcome-dialog';
1011

1112
@injectable()
1213
export class CheckForIDEUpdates extends Contribution {
@@ -16,6 +17,9 @@ export class CheckForIDEUpdates extends Contribution {
1617
@inject(IDEUpdaterDialog)
1718
private readonly updaterDialog: IDEUpdaterDialog;
1819

20+
@inject(VersionWelcomeDialog)
21+
private readonly welcomeDialog: VersionWelcomeDialog;
22+
1923
@inject(LocalStorageService)
2024
private readonly localStorage: LocalStorageService;
2125

@@ -37,6 +41,8 @@ export class CheckForIDEUpdates extends Contribution {
3741
}
3842

3943
override onReady(): void {
44+
this.welcomeDialog.open();
45+
4046
this.updater
4147
.init(
4248
this.preferences.get('arduino.ide.updateChannel'),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { nls } from '@theia/core/lib/common/nls';
2+
import React from '@theia/core/shared/react';
3+
// @ts-expect-error see https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
4+
import type { Options } from 'react-markdown';
5+
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
6+
import ProgressBar from '../../components/ProgressBar';
7+
8+
const ReactMarkdown = React.lazy<React.ComponentType<Options>>(
9+
// @ts-expect-error see above
10+
() => import('react-markdown')
11+
);
12+
13+
export interface UpdateProgress {
14+
progressInfo?: ProgressInfo | undefined;
15+
downloadFinished?: boolean;
16+
downloadStarted?: boolean;
17+
error?: Error;
18+
}
19+
20+
export interface IDEUpdaterComponentProps {
21+
updateInfo: UpdateInfo;
22+
updateProgress: UpdateProgress;
23+
openExternal: (url: string) => undefined;
24+
}
25+
26+
export const IDEUpdaterComponent = ({
27+
updateInfo,
28+
updateProgress: {
29+
downloadStarted = false,
30+
downloadFinished = false,
31+
progressInfo,
32+
error,
33+
},
34+
openExternal,
35+
}: IDEUpdaterComponentProps): React.ReactElement => {
36+
const { version, releaseNotes } = updateInfo;
37+
const [changelog, setChangelog] = React.useState<string>('');
38+
React.useEffect(() => {
39+
if (releaseNotes) {
40+
setChangelog(
41+
typeof releaseNotes === 'string'
42+
? releaseNotes
43+
: releaseNotes.reduce(
44+
(acc, item) => (item.note ? (acc += `${item.note}\n\n`) : acc),
45+
''
46+
)
47+
);
48+
}
49+
}, [releaseNotes, changelog]);
50+
51+
const DownloadCompleted: () => React.ReactElement = () => (
52+
<div className="ide-updater-dialog--downloaded">
53+
<div>
54+
{nls.localize(
55+
'arduino/ide-updater/versionDownloaded',
56+
'Arduino IDE {0} has been downloaded.',
57+
version
58+
)}
59+
</div>
60+
<div>
61+
{nls.localize(
62+
'arduino/ide-updater/closeToInstallNotice',
63+
'Close the software and install the update on your machine.'
64+
)}
65+
</div>
66+
</div>
67+
);
68+
69+
const DownloadStarted: () => React.ReactElement = () => (
70+
<div className="ide-updater-dialog--downloading">
71+
<div>
72+
{nls.localize(
73+
'arduino/ide-updater/downloadingNotice',
74+
'Downloading the latest version of the Arduino IDE.'
75+
)}
76+
</div>
77+
<ProgressBar percent={progressInfo?.percent} showPercentage />
78+
</div>
79+
);
80+
81+
const PreDownload: () => React.ReactElement = () => (
82+
<div className="ide-updater-dialog--pre-download">
83+
<div className="ide-updater-dialog--logo-container">
84+
<div className="ide-updater-dialog--logo"></div>
85+
</div>
86+
<div className="ide-updater-dialog--new-version-text dialogSection">
87+
<div className="dialogRow">
88+
<div className="bold">
89+
{nls.localize(
90+
'arduino/ide-updater/updateAvailable',
91+
'Update Available'
92+
)}
93+
</div>
94+
</div>
95+
<div className="dialogRow">
96+
{nls.localize(
97+
'arduino/ide-updater/newVersionAvailable',
98+
'A new version of Arduino IDE ({0}) is available for download.',
99+
version
100+
)}
101+
</div>
102+
{changelog && (
103+
<div className="dialogRow changelog-container">
104+
<div className="changelog">
105+
<React.Suspense
106+
fallback={
107+
<div className="fallback">
108+
<div className="spinner" />
109+
</div>
110+
}
111+
>
112+
<ReactMarkdown
113+
components={{
114+
a: ({ href, children, ...props }) => (
115+
<a onClick={() => href && openExternal(href)} {...props}>
116+
{children}
117+
</a>
118+
),
119+
}}
120+
>
121+
{changelog}
122+
</ReactMarkdown>
123+
</React.Suspense>
124+
</div>
125+
</div>
126+
)}
127+
</div>
128+
</div>
129+
);
130+
131+
const GoToDownloadPage: () => React.ReactElement = () => (
132+
<div className="ide-updater-dialog--go-to-download-page">
133+
<div>
134+
{nls.localize(
135+
'arduino/ide-updater/goToDownloadPage',
136+
"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."
137+
)}
138+
</div>
139+
</div>
140+
);
141+
142+
return (
143+
<div className="ide-updater-dialog--content">
144+
{!!error ? (
145+
<GoToDownloadPage />
146+
) : downloadFinished ? (
147+
<DownloadCompleted />
148+
) : downloadStarted ? (
149+
<DownloadStarted />
150+
) : (
151+
<PreDownload />
152+
)}
153+
</div>
154+
);
155+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import React from '@theia/core/shared/react';
2+
import { inject, injectable } from '@theia/core/shared/inversify';
3+
import { Message } from '@theia/core/shared/@phosphor/messaging';
4+
import { ReactDialog } from '../../theia/dialogs/dialogs';
5+
import { nls } from '@theia/core';
6+
import { DialogProps } from '@theia/core/lib/browser';
7+
import { WindowService } from '@theia/core/lib/browser/window/window-service';
8+
import { AppService } from '../../app-service';
9+
10+
@injectable()
11+
export class VersionWelcomeDialogProps extends DialogProps {}
12+
13+
@injectable()
14+
export class VersionWelcomeDialog extends ReactDialog<void> {
15+
// @inject(LocalStorageService)
16+
// private readonly localStorageService: LocalStorageService;
17+
// @inject(AppService)
18+
// private readonly appService: AppService;
19+
@inject(AppService)
20+
private readonly appService: AppService;
21+
22+
@inject(WindowService)
23+
private readonly windowService: WindowService;
24+
25+
constructor(
26+
@inject(VersionWelcomeDialogProps)
27+
protected override readonly props: VersionWelcomeDialogProps
28+
) {
29+
super({
30+
title: nls.localize(
31+
'arduino/ide-updater/ideUpdaterDialog',
32+
'Welcome to the new Arduino IDE!'
33+
),
34+
});
35+
36+
this.node.id = 'version-welcome-dialog-container';
37+
this.contentNode.classList.add('version-welcome-dialog');
38+
}
39+
40+
protected render(): React.ReactNode {
41+
return (
42+
<div>
43+
<p>
44+
Arduino is committed to keeping software free and open-source for
45+
everyone. Your donation helps us develop new features, improve
46+
libraries, and support millions of users worldwide.
47+
</p>
48+
<p className="bold">
49+
Please consider supporting our efforts to keep Arduino IDE free.
50+
</p>
51+
</div>
52+
);
53+
}
54+
55+
override get value(): void {
56+
return;
57+
}
58+
59+
private appendButtons(): void {
60+
const cancelButton = this.createButton(
61+
nls.localize('arduino/ide-updater/skipVersionButton', 'Skip Version')
62+
);
63+
cancelButton.classList.add('secondary');
64+
cancelButton.classList.add('cancel-button');
65+
this.addAction(cancelButton, this.close.bind(this), 'click');
66+
this.controlPanel.appendChild(cancelButton);
67+
68+
const donateButton = this.createButton(
69+
nls.localize('arduino/ide-updater/donateButton', 'Download')
70+
);
71+
this.addAction(donateButton, this.openDonationPage.bind(this), 'click');
72+
this.controlPanel.appendChild(donateButton);
73+
donateButton.focus();
74+
}
75+
76+
private readonly openDonationPage = () => {
77+
const url = 'https://www.arduino.cc/en/donate';
78+
this.windowService.openNewWindow(url, { external: true });
79+
};
80+
81+
private async updateTitleVersion(): Promise<void> {
82+
debugger;
83+
const appInfo = await this.appService.info();
84+
const { appVersion } = appInfo;
85+
const title = nls.localize(
86+
'arduino/ide-updater/ideUpdaterDialog',
87+
`Welcome to the new Arduino IDE ${appVersion}!`,
88+
appVersion
89+
);
90+
console.log('title', this.titleNode);
91+
this.titleNode.innerHTML = title;
92+
}
93+
94+
protected override onAfterAttach(msg: Message): void {
95+
this.update();
96+
this.appendButtons();
97+
this.updateTitleVersion();
98+
super.onAfterAttach(msg);
99+
}
100+
}

arduino-ide-extension/src/browser/style/index.css

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
@import "./settings-dialog.css";
1111
@import "./firmware-uploader-dialog.css";
1212
@import "./ide-updater-dialog.css";
13+
@import "./version-welcome-dialog.css";
1314
@import "./certificate-uploader-dialog.css";
1415
@import "./user-fields-dialog.css";
1516
@import "./debug.css";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#version-welcome-dialog-container > .dialogBlock {
2+
width: 546px;
3+
}

arduino-ide-extension/src/electron-main/ide-updater/ide-updater-impl.ts

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export class IDEUpdaterImpl implements IDEUpdater {
6262
await this.updater.checkForUpdates();
6363

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

0 commit comments

Comments
 (0)