Skip to content

Commit 19bf879

Browse files
feat: introduce VersionWelcomeDialog
Show donate dialog after the first time a first IDE version is loaded
1 parent 4fab7f4 commit 19bf879

File tree

7 files changed

+186
-2
lines changed

7 files changed

+186
-2
lines changed

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

Lines changed: 9 additions & 0 deletions
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-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

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@ import { LocalStorageService } from '@theia/core/lib/browser/storage-service';
33
import { inject, injectable } from '@theia/core/shared/inversify';
44
import {
55
IDEUpdater,
6+
LAST_USED_IDE_VERSION,
67
SKIP_IDE_VERSION,
78
} from '../../common/protocol/ide-updater';
89
import { IDEUpdaterDialog } from '../dialogs/ide-updater/ide-updater-dialog';
910
import { Contribution } from './contribution';
11+
import { VersionWelcomeDialog } from '../dialogs/version-welcome-dialog';
12+
import { AppService } from '../app-service';
13+
import { SemVer } from 'semver';
1014

1115
@injectable()
1216
export class CheckForIDEUpdates extends Contribution {
@@ -16,9 +20,15 @@ export class CheckForIDEUpdates extends Contribution {
1620
@inject(IDEUpdaterDialog)
1721
private readonly updaterDialog: IDEUpdaterDialog;
1822

23+
@inject(VersionWelcomeDialog)
24+
private readonly versionWelcomeDialog: VersionWelcomeDialog;
25+
1926
@inject(LocalStorageService)
2027
private readonly localStorage: LocalStorageService;
2128

29+
@inject(AppService)
30+
private readonly appService: AppService;
31+
2232
override onStart(): void {
2333
this.preferences.onPreferenceChanged(
2434
({ preferenceName, newValue, oldValue }) => {
@@ -36,7 +46,7 @@ export class CheckForIDEUpdates extends Contribution {
3646
);
3747
}
3848

39-
override onReady(): void {
49+
override async onReady(): Promise<void> {
4050
this.updater
4151
.init(
4252
this.preferences.get('arduino.ide.updateChannel'),
@@ -49,7 +59,13 @@ export class CheckForIDEUpdates extends Contribution {
4959
return this.updater.checkForUpdates(true);
5060
})
5161
.then(async (updateInfo) => {
52-
if (!updateInfo) return;
62+
if (!updateInfo) {
63+
const isNewVersion = await this.isNewStableVersion();
64+
if (isNewVersion) {
65+
this.versionWelcomeDialog.open();
66+
}
67+
return;
68+
}
5369
const versionToSkip = await this.localStorage.getData<string>(
5470
SKIP_IDE_VERSION
5571
);
@@ -64,6 +80,44 @@ export class CheckForIDEUpdates extends Contribution {
6480
e.message
6581
)
6682
);
83+
})
84+
.finally(() => {
85+
this.setCurrentIDEVersion();
6786
});
6887
}
88+
89+
private async setCurrentIDEVersion(): Promise<void> {
90+
try {
91+
const { appVersion } = await this.appService.info();
92+
const currSemVer = new SemVer(appVersion ?? '');
93+
this.localStorage.setData(LAST_USED_IDE_VERSION, currSemVer.format());
94+
} catch {
95+
// ignore invalid versions
96+
}
97+
}
98+
99+
/**
100+
* Check if user is running a new IDE version for the first time.
101+
* @returns true if the current IDE version is greater than the last used version
102+
* and both are non-prerelease versions.
103+
*/
104+
private async isNewStableVersion(): Promise<boolean> {
105+
try {
106+
const { appVersion } = await this.appService.info();
107+
const prevVersion = await this.localStorage.getData<string>(
108+
LAST_USED_IDE_VERSION
109+
);
110+
111+
const prevSemVer = new SemVer(prevVersion ?? '');
112+
const currSemVer = new SemVer(appVersion ?? '');
113+
114+
if (prevSemVer.prerelease.length || currSemVer.prerelease.length) {
115+
return false;
116+
}
117+
118+
return currSemVer.compare(prevSemVer) === 1;
119+
} catch (e) {
120+
return false;
121+
}
122+
}
69123
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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(AppService)
16+
private readonly appService: AppService;
17+
18+
@inject(WindowService)
19+
private readonly windowService: WindowService;
20+
21+
constructor(
22+
@inject(VersionWelcomeDialogProps)
23+
protected override readonly props: VersionWelcomeDialogProps
24+
) {
25+
super({
26+
title: nls.localize(
27+
'arduino/versionWelcome/title',
28+
'Welcome to a new version of the Arduino IDE!'
29+
),
30+
});
31+
this.node.id = 'version-welcome-dialog-container';
32+
this.contentNode.classList.add('version-welcome-dialog');
33+
}
34+
35+
protected render(): React.ReactNode {
36+
return (
37+
<div>
38+
<p>
39+
{nls.localize(
40+
'arduino/versionWelcome/donateMessage',
41+
'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.'
42+
)}
43+
</p>
44+
<p className="bold">
45+
{nls.localize(
46+
'arduino/versionWelcome/donateMessage2',
47+
'Please consider supporting our work on the free open source Arduino IDE.'
48+
)}
49+
</p>
50+
</div>
51+
);
52+
}
53+
54+
override get value(): void {
55+
return;
56+
}
57+
58+
private appendButtons(): void {
59+
const cancelButton = this.createButton(
60+
nls.localize('arduino/versionWelcome/cancelButton', 'Maybe later')
61+
);
62+
cancelButton.classList.add('secondary');
63+
cancelButton.classList.add('cancel-button');
64+
this.addAction(cancelButton, this.close.bind(this), 'click');
65+
this.controlPanel.appendChild(cancelButton);
66+
67+
const donateButton = this.createButton(
68+
nls.localize('arduino/versionWelcome/donateButton', 'Donate now')
69+
);
70+
this.addAction(donateButton, this.onDonateButtonClick.bind(this), 'click');
71+
this.controlPanel.appendChild(donateButton);
72+
donateButton.focus();
73+
}
74+
75+
private onDonateButtonClick(): void {
76+
this.openDonationPage();
77+
this.close();
78+
}
79+
80+
private readonly openDonationPage = () => {
81+
const url = 'https://www.arduino.cc/en/donate';
82+
this.windowService.openNewWindow(url, { external: true });
83+
};
84+
85+
private async updateTitleVersion(): Promise<void> {
86+
const appInfo = await this.appService.info();
87+
const { appVersion } = appInfo;
88+
89+
if (appVersion) {
90+
this.titleNode.innerHTML = nls.localize(
91+
'arduino/versionWelcome/titleWithVersion',
92+
'Welcome to the new Arduino IDE {0}!',
93+
appVersion
94+
);
95+
}
96+
}
97+
98+
protected override onAfterAttach(msg: Message): void {
99+
this.update();
100+
this.appendButtons();
101+
this.updateTitleVersion();
102+
super.onAfterAttach(msg);
103+
}
104+
}

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

Lines changed: 1 addition & 0 deletions
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";
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#version-welcome-dialog-container > .dialogBlock {
2+
width: 546px;
3+
4+
.bold {
5+
font-weight: bold;
6+
}
7+
}

arduino-ide-extension/src/common/protocol/ide-updater.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,4 @@ export interface IDEUpdaterClient {
7171
}
7272

7373
export const SKIP_IDE_VERSION = 'skipIDEVersion';
74+
export const LAST_USED_IDE_VERSION = 'lastUsedIDEVersion';

i18n/en.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,14 @@
528528
"renameSketchFolderMessage": "The sketch '{0}' cannot be used. {1} To get rid of this message, rename the sketch. Do you want to rename the sketch now?",
529529
"renameSketchFolderTitle": "Invalid sketch name"
530530
},
531+
"versionWelcome": {
532+
"cancelButton": "Maybe later",
533+
"donateButton": "Donate now",
534+
"donateMessage": "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.",
535+
"donateMessage2": "Please consider supporting our work on the free open source Arduino IDE.",
536+
"title": "Welcome to a new version of the Arduino IDE!",
537+
"titleWithVersion": "Welcome to the new Arduino IDE {0}!"
538+
},
531539
"workspace": {
532540
"alreadyExists": "'{0}' already exists."
533541
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy