Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 7c851ca

Browse files
author
Akos Kitta
committed
fix: check for updates dialog
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent 8521103 commit 7c851ca

File tree

5 files changed

+142
-103
lines changed

5 files changed

+142
-103
lines changed

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ import { IDEUpdaterClientImpl } from './ide-updater/ide-updater-client-impl';
262262
import {
263263
IDEUpdaterDialog,
264264
IDEUpdaterDialogProps,
265-
IDEUpdaterDialogWidget,
266265
} from './dialogs/ide-updater/ide-updater-dialog';
267266
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
268267
import { MonitorModel } from './monitor-model';
@@ -910,7 +909,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
910909
title: 'UploadCertificate',
911910
});
912911

913-
bind(IDEUpdaterDialogWidget).toSelf().inSingletonScope();
914912
bind(IDEUpdaterDialog).toSelf().inSingletonScope();
915913
bind(IDEUpdaterDialogProps).toConstantValue({
916914
title: 'IDEUpdater',

‎arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-component.tsx‎

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { nls } from '@theia/core/lib/common';
2-
import { shell } from 'electron';
2+
import { shell } from '@theia/core/electron-shared/@electron/remote';
33
import * as React from '@theia/core/shared/react';
4-
import { createRoot } from '@theia/core/shared/react-dom/client';
54
import ReactMarkdown from 'react-markdown';
65
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
76
import ProgressBar from '../../components/ProgressBar';
@@ -28,32 +27,19 @@ export const IDEUpdaterComponent = ({
2827
},
2928
}: IDEUpdaterComponentProps): React.ReactElement => {
3029
const { version, releaseNotes } = updateInfo;
31-
const changelogDivRef =
32-
React.useRef() as React.MutableRefObject<HTMLDivElement>;
33-
const changelogRoot = createRoot(changelogDivRef.current);
30+
const [changelog, setChangelog] = React.useState<string>('');
3431
React.useEffect(() => {
35-
if (!!releaseNotes && changelogDivRef.current) {
36-
let changelog: string;
37-
if (typeof releaseNotes === 'string') changelog = releaseNotes;
38-
else
39-
changelog = releaseNotes.reduce((acc, item) => {
40-
return item.note ? (acc += `${item.note}\n\n`) : acc;
41-
}, '');
42-
changelogRoot.render(
43-
<ReactMarkdown
44-
components={{
45-
a: ({ href, children, ...props }) => (
46-
<a onClick={() => href && shell.openExternal(href)} {...props}>
47-
{children}
48-
</a>
49-
),
50-
}}
51-
>
52-
{changelog}
53-
</ReactMarkdown>
32+
if (releaseNotes) {
33+
setChangelog(
34+
typeof releaseNotes === 'string'
35+
? releaseNotes
36+
: releaseNotes.reduce(
37+
(acc, item) => (item.note ? (acc += `${item.note}\n\n`) : acc),
38+
''
39+
)
5440
);
5541
}
56-
}, [updateInfo]);
42+
}, [releaseNotes,changelog]);
5743

5844
const DownloadCompleted: () => React.ReactElement = () => (
5945
<div className="ide-updater-dialog--downloaded">
@@ -106,9 +92,24 @@ export const IDEUpdaterComponent = ({
10692
version
10793
)}
10894
</div>
109-
{releaseNotes && (
95+
{changelog && (
11096
<div className="dialogRow changelog-container">
111-
<div className="changelog" ref={changelogDivRef} />
97+
<div className="changelog">
98+
<ReactMarkdown
99+
components={{
100+
a: ({ href, children, ...props }) => (
101+
<a
102+
onClick={() => href && shell.openExternal(href)}
103+
{...props}
104+
>
105+
{children}
106+
</a>
107+
),
108+
}}
109+
>
110+
{changelog}
111+
</ReactMarkdown>
112+
</div>
112113
</div>
113114
)}
114115
</div>

‎arduino-ide-extension/src/browser/dialogs/ide-updater/ide-updater-dialog.tsx‎

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@ import {
55
postConstruct,
66
} from '@theia/core/shared/inversify';
77
import { DialogProps } from '@theia/core/lib/browser/dialogs';
8-
import { AbstractDialog } from '../../theia/dialogs/dialogs';
9-
import { Widget } from '@theia/core/shared/@phosphor/widgets';
108
import { Message } from '@theia/core/shared/@phosphor/messaging';
11-
import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget';
9+
import { ReactDialog } from '../../theia/dialogs/dialogs';
1210
import { nls } from '@theia/core';
1311
import { IDEUpdaterComponent, UpdateProgress } from './ide-updater-component';
1412
import {
@@ -22,47 +20,11 @@ import { WindowService } from '@theia/core/lib/browser/window/window-service';
2220

2321
const DOWNLOAD_PAGE_URL = 'https://www.arduino.cc/en/software';
2422

25-
@injectable()
26-
export class IDEUpdaterDialogWidget extends ReactWidget {
27-
private _updateInfo: UpdateInfo;
28-
private _updateProgress: UpdateProgress = {};
29-
30-
setUpdateInfo(updateInfo: UpdateInfo): void {
31-
this._updateInfo = updateInfo;
32-
this.update();
33-
}
34-
35-
mergeUpdateProgress(updateProgress: UpdateProgress): void {
36-
this._updateProgress = { ...this._updateProgress, ...updateProgress };
37-
this.update();
38-
}
39-
40-
get updateInfo(): UpdateInfo {
41-
return this._updateInfo;
42-
}
43-
44-
get updateProgress(): UpdateProgress {
45-
return this._updateProgress;
46-
}
47-
48-
protected render(): React.ReactNode {
49-
return !!this._updateInfo ? (
50-
<IDEUpdaterComponent
51-
updateInfo={this._updateInfo}
52-
updateProgress={this._updateProgress}
53-
/>
54-
) : null;
55-
}
56-
}
57-
5823
@injectable()
5924
export class IDEUpdaterDialogProps extends DialogProps {}
6025

6126
@injectable()
62-
export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
63-
@inject(IDEUpdaterDialogWidget)
64-
private readonly widget: IDEUpdaterDialogWidget;
65-
27+
export class IDEUpdaterDialog extends ReactDialog<UpdateInfo | undefined> {
6628
@inject(IDEUpdater)
6729
private readonly updater: IDEUpdater;
6830

@@ -75,6 +37,9 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
7537
@inject(WindowService)
7638
private readonly windowService: WindowService;
7739

40+
private _updateInfo: UpdateInfo | undefined;
41+
private _updateProgress: UpdateProgress = {};
42+
7843
constructor(
7944
@inject(IDEUpdaterDialogProps)
8045
protected override readonly props: IDEUpdaterDialogProps
@@ -94,26 +59,34 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
9459
protected init(): void {
9560
this.updaterClient.onUpdaterDidFail((error) => {
9661
this.appendErrorButtons();
97-
this.widget.mergeUpdateProgress({ error });
62+
this.mergeUpdateProgress({ error });
9863
});
9964
this.updaterClient.onDownloadProgressDidChange((progressInfo) => {
100-
this.widget.mergeUpdateProgress({ progressInfo });
65+
this.mergeUpdateProgress({ progressInfo });
10166
});
10267
this.updaterClient.onDownloadDidFinish(() => {
10368
this.appendInstallButtons();
104-
this.widget.mergeUpdateProgress({ downloadFinished: true });
69+
this.mergeUpdateProgress({ downloadFinished: true });
10570
});
10671
}
10772

108-
get value(): UpdateInfo {
109-
return this.widget.updateInfo;
73+
protected render(): React.ReactNode {
74+
return (
75+
this.updateInfo && (
76+
<IDEUpdaterComponent
77+
updateInfo={this.updateInfo}
78+
updateProgress={this.updateProgress}
79+
/>
80+
)
81+
);
82+
}
83+
84+
get value(): UpdateInfo | undefined {
85+
return this.updateInfo;
11086
}
11187

11288
protected override onAfterAttach(msg: Message): void {
113-
if (this.widget.isAttached) {
114-
Widget.detach(this.widget);
115-
}
116-
Widget.attach(this.widget, this.contentNode);
89+
this.update();
11790
this.appendInitialButtons();
11891
super.onAfterAttach(msg);
11992
}
@@ -196,15 +169,19 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
196169
}
197170

198171
private skipVersion(): void {
172+
if (!this.updateInfo) {
173+
console.warn(`Nothing to skip. No update info is available`);
174+
return;
175+
}
199176
this.localStorageService.setData<string>(
200177
SKIP_IDE_VERSION,
201-
this.widget.updateInfo.version
178+
this.updateInfo.version
202179
);
203180
this.close();
204181
}
205182

206183
private startDownload(): void {
207-
this.widget.mergeUpdateProgress({
184+
this.mergeUpdateProgress({
208185
downloadStarted: true,
209186
});
210187
this.clearButtons();
@@ -216,31 +193,48 @@ export class IDEUpdaterDialog extends AbstractDialog<UpdateInfo> {
216193
this.close();
217194
}
218195

196+
private set updateInfo(updateInfo: UpdateInfo | undefined) {
197+
this._updateInfo = updateInfo;
198+
this.update();
199+
}
200+
201+
private get updateInfo(): UpdateInfo | undefined {
202+
return this._updateInfo;
203+
}
204+
205+
private get updateProgress(): UpdateProgress {
206+
return this._updateProgress;
207+
}
208+
209+
private mergeUpdateProgress(updateProgress: UpdateProgress): void {
210+
this._updateProgress = { ...this._updateProgress, ...updateProgress };
211+
this.update();
212+
}
213+
219214
override async open(
220215
data: UpdateInfo | undefined = undefined
221216
): Promise<UpdateInfo | undefined> {
222217
if (data && data.version) {
223-
this.widget.mergeUpdateProgress({
218+
this.mergeUpdateProgress({
224219
progressInfo: undefined,
225220
downloadStarted: false,
226221
downloadFinished: false,
227222
error: undefined,
228223
});
229-
this.widget.setUpdateInfo(data);
224+
this.updateInfo=data;
230225
return super.open();
231226
}
232227
}
233228

234229
protected override onActivateRequest(msg: Message): void {
235230
super.onActivateRequest(msg);
236-
this.widget.activate();
231+
this.update();
237232
}
238233

239234
override close(): void {
240-
this.widget.dispose();
241235
if (
242-
this.widget.updateProgress?.downloadStarted &&
243-
!this.widget.updateProgress?.downloadFinished
236+
this.updateProgress?.downloadStarted &&
237+
!this.updateProgress?.downloadFinished
244238
) {
245239
this.updater.stopDownload();
246240
}

‎arduino-ide-extension/src/browser/theia/dialogs/dialogs.ts‎

Lines changed: 0 additions & 17 deletions
This file was deleted.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {
2+
AbstractDialog as TheiaAbstractDialog,
3+
DialogProps,
4+
} from '@theia/core/lib/browser/dialogs';
5+
import { ReactDialog as TheiaReactDialog } from '@theia/core/lib/browser/dialogs/react-dialog';
6+
import { codiconArray, Message } from '@theia/core/lib/browser/widgets/widget';
7+
import {
8+
Disposable,
9+
DisposableCollection,
10+
} from '@theia/core/lib/common/disposable';
11+
import { inject, injectable } from '@theia/core/shared/inversify';
12+
import * as React from '@theia/core/shared/react';
13+
import { createRoot } from '@theia/core/shared/react-dom/client';
14+
15+
@injectable()
16+
export abstract class AbstractDialog<T> extends TheiaAbstractDialog<T> {
17+
constructor(
18+
@inject(DialogProps) protected override readonly props: DialogProps
19+
) {
20+
super(props);
21+
22+
this.closeCrossNode.classList.remove(...codiconArray('close'));
23+
this.closeCrossNode.classList.add('fa', 'fa-close');
24+
}
25+
}
26+
27+
@injectable()
28+
export abstract class ReactDialog<T> extends TheiaReactDialog<T> {
29+
protected override onUpdateRequest(msg: Message): void {
30+
// This is tricky to bypass the default Theia code.
31+
// Otherwise, there is a warning when opening the dialog for the second time.
32+
// You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.
33+
const disposables = new DisposableCollection();
34+
if (!this.isMounted) {
35+
// toggle the `isMounted` logic for the time being of the super call so that the `createRoot` does not run
36+
this.isMounted = true;
37+
disposables.push(Disposable.create(() => (this.isMounted = false)));
38+
}
39+
40+
// Always unset the `contentNodeRoot` so there is no double update when calling super.
41+
const restoreContentNodeRoot = this.contentNodeRoot;
42+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
43+
(this.contentNodeRoot as any) = undefined;
44+
disposables.push(
45+
Disposable.create(() => (this.contentNodeRoot = restoreContentNodeRoot))
46+
);
47+
48+
try {
49+
super.onUpdateRequest(msg);
50+
} finally {
51+
disposables.dispose();
52+
}
53+
54+
// Use the patched rendering.
55+
if (!this.isMounted) {
56+
this.contentNodeRoot = createRoot(this.contentNode);
57+
// Resetting the prop is missing from the Theia code.
58+
// https://github.com/eclipse-theia/theia/blob/v1.31.1/packages/core/src/browser/dialogs/react-dialog.tsx#L41-L47
59+
this.isMounted = true;
60+
}
61+
this.contentNodeRoot?.render(<>{this.render()}</>);
62+
}
63+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /