Skip to content

Commit f3e5de9

Browse files
author
Alberto Iannaccone
committed
Integrate remote sketchbook extension
1 parent e21867b commit f3e5de9

39 files changed

+4429
-140
lines changed

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

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,21 @@ import { NotificationManager as TheiaNotificationManager } from '@theia/messages
218218
import { NotificationsRenderer as TheiaNotificationsRenderer } from '@theia/messages/lib/browser/notifications-renderer';
219219
import { NotificationsRenderer } from './theia/messages/notifications-renderer';
220220
import { SketchbookWidgetContribution } from './widgets/sketchbook/sketchbook-widget-contribution';
221+
import { LocalCacheFsProvider } from './local-cache/local-cache-fs-provider';
222+
import { CloudSketchbookWidget } from './widgets/cloud-sketchbook/cloud-sketchbook-widget';
223+
import { CloudSketchbookTreeWidget } from './widgets/cloud-sketchbook/cloud-sketchbook-tree-widget';
224+
import { createCloudSketchbookTreeWidget } from './widgets/cloud-sketchbook/cloud-sketchbook-tree-container';
225+
import { CreateApi } from './create/create-api';
226+
import { ShareSketchDialog } from './dialogs.ts/cloud-share-sketch-dialog';
227+
import { AuthenticationClientService } from './auth/authentication-client-service';
228+
import {
229+
AuthenticationService,
230+
AuthenticationServicePath,
231+
} from '../common/protocol/authentication-service';
232+
import { CreateFsProvider } from './create/create-fs-provider';
233+
import { FileServiceContribution } from '@theia/filesystem/lib/browser/file-service';
234+
import { CloudSketchbookContribution } from './widgets/cloud-sketchbook/cloud-sketchbook-contributions';
235+
import { CloudSketchbookCompositeWidget } from './widgets/cloud-sketchbook/cloud-sketchbook-composite-widget';
221236
import { SketchbookWidget } from './widgets/sketchbook/sketchbook-widget';
222237
import { SketchbookTreeWidget } from './widgets/sketchbook/sketchbook-tree-widget';
223238
import { createSketchbookTreeWidget } from './widgets/sketchbook/sketchbook-tree-container';
@@ -660,11 +675,48 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
660675

661676
// UI for the Sketchbook
662677
bind(SketchbookWidget).toSelf();
678+
bind(SketchbookTreeWidget).toDynamicValue(({ container }) =>
679+
createSketchbookTreeWidget(container)
680+
);
663681
bindViewContribution(bind, SketchbookWidgetContribution);
664-
bind(FrontendApplicationContribution).toService(SketchbookWidgetContribution);
682+
bind(FrontendApplicationContribution).toService(
683+
SketchbookWidgetContribution
684+
);
665685
bind(WidgetFactory).toDynamicValue(({ container }) => ({
666686
id: 'arduino-sketchbook-widget',
667-
createWidget: () => container.get(SketchbookWidget)
687+
createWidget: () => container.get(SketchbookWidget),
688+
}));
689+
690+
bind(CloudSketchbookWidget).toSelf();
691+
rebind(SketchbookWidget).toService(CloudSketchbookWidget);
692+
bind(CloudSketchbookTreeWidget).toDynamicValue(({ container }) =>
693+
createCloudSketchbookTreeWidget(container)
694+
);
695+
bind(CreateApi).toSelf().inSingletonScope();
696+
bind(ShareSketchDialog).toSelf().inSingletonScope();
697+
bind(AuthenticationClientService).toSelf().inSingletonScope();
698+
bind(CommandContribution).toService(AuthenticationClientService);
699+
bind(FrontendApplicationContribution).toService(
700+
AuthenticationClientService
701+
);
702+
bind(AuthenticationService)
703+
.toDynamicValue((context) =>
704+
WebSocketConnectionProvider.createProxy(
705+
context.container,
706+
AuthenticationServicePath
707+
)
708+
)
709+
.inSingletonScope();
710+
bind(CreateFsProvider).toSelf().inSingletonScope();
711+
bind(FrontendApplicationContribution).toService(CreateFsProvider);
712+
bind(FileServiceContribution).toService(CreateFsProvider);
713+
bind(CloudSketchbookContribution).toSelf().inSingletonScope();
714+
bind(CommandContribution).toService(CloudSketchbookContribution);
715+
bind(LocalCacheFsProvider).toSelf().inSingletonScope();
716+
bind(FileServiceContribution).toService(LocalCacheFsProvider);
717+
bind(CloudSketchbookCompositeWidget).toSelf();
718+
bind<WidgetFactory>(WidgetFactory).toDynamicValue((ctx) => ({
719+
id: 'cloud-sketchbook-composite-widget',
720+
createWidget: () => ctx.container.get(CloudSketchbookCompositeWidget),
668721
}));
669-
bind(SketchbookTreeWidget).toDynamicValue(({ container }) => createSketchbookTreeWidget(container));
670722
});
Lines changed: 111 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,117 @@
11
import { interfaces } from 'inversify';
2-
import { createPreferenceProxy, PreferenceProxy, PreferenceService, PreferenceContribution, PreferenceSchema } from '@theia/core/lib/browser/preferences';
2+
import {
3+
createPreferenceProxy,
4+
PreferenceProxy,
5+
PreferenceService,
6+
PreferenceContribution,
7+
PreferenceSchema,
8+
} from '@theia/core/lib/browser/preferences';
39
import { CompilerWarningLiterals, CompilerWarnings } from '../common/protocol';
410

511
export const ArduinoConfigSchema: PreferenceSchema = {
6-
'type': 'object',
7-
'properties': {
12+
type: 'object',
13+
properties: {
814
'arduino.language.log': {
9-
'type': 'boolean',
10-
'description': "True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
11-
'default': false
15+
type: 'boolean',
16+
description:
17+
"True if the Arduino Language Server should generate log files into the sketch folder. Otherwise, false. It's false by default.",
18+
default: false,
1219
},
1320
'arduino.compile.verbose': {
14-
'type': 'boolean',
15-
'description': 'True for verbose compile output. False by default',
16-
'default': false
21+
type: 'boolean',
22+
description: 'True for verbose compile output. False by default',
23+
default: false,
1724
},
1825
'arduino.compile.warnings': {
19-
'enum': [...CompilerWarningLiterals],
20-
'description': "Tells gcc which warning level to use. It's 'None' by default",
21-
'default': 'None'
26+
enum: [...CompilerWarningLiterals],
27+
description:
28+
"Tells gcc which warning level to use. It's 'None' by default",
29+
default: 'None',
2230
},
2331
'arduino.upload.verbose': {
24-
'type': 'boolean',
25-
'description': 'True for verbose upload output. False by default.',
26-
'default': false
32+
type: 'boolean',
33+
description: 'True for verbose upload output. False by default.',
34+
default: false,
2735
},
2836
'arduino.upload.verify': {
29-
'type': 'boolean',
30-
'default': false
37+
type: 'boolean',
38+
default: false,
3139
},
3240
'arduino.window.autoScale': {
33-
'type': 'boolean',
34-
'description': 'True if the user interface automatically scales with the font size.',
35-
'default': true
41+
type: 'boolean',
42+
description:
43+
'True if the user interface automatically scales with the font size.',
44+
default: true,
3645
},
3746
'arduino.window.zoomLevel': {
38-
'type': 'number',
39-
'description': 'Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.',
40-
'default': 0
47+
type: 'number',
48+
description:
49+
'Adjust the zoom level of the window. The original size is 0 and each increment above (e.g. 1) or below (e.g. -1) represents zooming 20% larger or smaller. You can also enter decimals to adjust the zoom level with a finer granularity.',
50+
default: 0,
4151
},
4252
'arduino.ide.autoUpdate': {
43-
'type': 'boolean',
44-
'description': 'True to enable automatic update checks. The IDE will check for updates automatically and periodically.',
45-
'default': true
53+
type: 'boolean',
54+
description:
55+
'True to enable automatic update checks. The IDE will check for updates automatically and periodically.',
56+
default: true,
4657
},
4758
'arduino.sketchbook.showAllFiles': {
48-
'type': 'boolean',
49-
'description': 'True to show all sketch files inside the sketch. It is false by default.',
50-
'default': false
51-
}
52-
}
59+
type: 'boolean',
60+
description:
61+
'True to show all sketch files inside the sketch. It is false by default.',
62+
default: false,
63+
},
64+
'arduino.cloud.enabled': {
65+
type: 'boolean',
66+
description:
67+
'True if the sketch sync functions are enabled. Defaults to true.',
68+
default: true,
69+
},
70+
'arduino.cloud.pull.warn': {
71+
type: 'boolean',
72+
description:
73+
'True if users should be warned before pulling a cloud sketch. Defaults to true.',
74+
default: true,
75+
},
76+
'arduino.cloud.push.warn': {
77+
type: 'boolean',
78+
description:
79+
'True if users should be warned before pushing a cloud sketch. Defaults to true.',
80+
default: true,
81+
},
82+
'arduino.cloud.pushpublic.warn': {
83+
type: 'boolean',
84+
description:
85+
'True if users should be warned before pushing a public sketch to the cloud. Defaults to true.',
86+
default: true,
87+
},
88+
'arduino.cloud.sketchSyncEnpoint': {
89+
type: 'string',
90+
description:
91+
'The endpoint used to push and pull sketches from a backend. By default it points to Arduino Cloud API.',
92+
default: 'https://api2.arduino.cc/create',
93+
},
94+
'arduino.auth.clientID': {
95+
type: 'string',
96+
description: 'The OAuth2 client ID.',
97+
default: 'C34Ya6ex77jTNxyKWj01lCe1vAHIaPIo',
98+
},
99+
'arduino.auth.domain': {
100+
type: 'string',
101+
description: 'The OAuth2 domain.',
102+
default: 'login.arduino.cc',
103+
},
104+
'arduino.auth.audience': {
105+
type: 'string',
106+
description: 'The 0Auth2 audience.',
107+
default: 'https://api.arduino.cc',
108+
},
109+
'arduino.auth.registerUri': {
110+
type: 'string',
111+
description: 'The URI used to register a new user.',
112+
default: 'https://auth.arduino.cc/login#/register',
113+
},
114+
},
53115
};
54116

55117
export interface ArduinoConfiguration {
@@ -62,19 +124,33 @@ export interface ArduinoConfiguration {
62124
'arduino.window.zoomLevel': number;
63125
'arduino.ide.autoUpdate': boolean;
64126
'arduino.sketchbook.showAllFiles': boolean;
127+
'arduino.cloud.enabled': boolean;
128+
'arduino.cloud.pull.warn': boolean;
129+
'arduino.cloud.push.warn': boolean;
130+
'arduino.cloud.pushpublic.warn': boolean;
131+
'arduino.cloud.sketchSyncEnpoint': string;
132+
'arduino.auth.clientID': string;
133+
'arduino.auth.domain': string;
134+
'arduino.auth.audience': string;
135+
'arduino.auth.registerUri': string;
65136
}
66137

67138
export const ArduinoPreferences = Symbol('ArduinoPreferences');
68139
export type ArduinoPreferences = PreferenceProxy<ArduinoConfiguration>;
69140

70-
export function createArduinoPreferences(preferences: PreferenceService): ArduinoPreferences {
141+
export function createArduinoPreferences(
142+
preferences: PreferenceService
143+
): ArduinoPreferences {
71144
return createPreferenceProxy(preferences, ArduinoConfigSchema);
72145
}
73146

74147
export function bindArduinoPreferences(bind: interfaces.Bind): void {
75-
bind(ArduinoPreferences).toDynamicValue(ctx => {
76-
const preferences = ctx.container.get<PreferenceService>(PreferenceService);
148+
bind(ArduinoPreferences).toDynamicValue((ctx) => {
149+
const preferences =
150+
ctx.container.get<PreferenceService>(PreferenceService);
77151
return createArduinoPreferences(preferences);
78152
});
79-
bind(PreferenceContribution).toConstantValue({ schema: ArduinoConfigSchema });
153+
bind(PreferenceContribution).toConstantValue({
154+
schema: ArduinoConfigSchema,
155+
});
80156
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { inject, injectable } from 'inversify';
2+
import { Emitter } from '@theia/core/lib/common/event';
3+
import { JsonRpcProxy } from '@theia/core/lib/common/messaging/proxy-factory';
4+
import { WindowService } from '@theia/core/lib/browser/window/window-service';
5+
import { DisposableCollection } from '@theia/core/lib/common/disposable';
6+
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
7+
import {
8+
CommandRegistry,
9+
CommandContribution,
10+
} from '@theia/core/lib/common/command';
11+
import {
12+
AuthenticationService,
13+
AuthenticationServiceClient,
14+
AuthenticationSession,
15+
} from '../../common/protocol/authentication-service';
16+
import { CloudUserCommands } from './cloud-user-commands';
17+
import { serverPort } from '../../node/auth/authentication-server';
18+
import { AuthOptions } from '../../node/auth/types';
19+
import { ArduinoPreferences } from '../arduino-preferences';
20+
21+
@injectable()
22+
export class AuthenticationClientService
23+
implements
24+
FrontendApplicationContribution,
25+
CommandContribution,
26+
AuthenticationServiceClient
27+
{
28+
@inject(AuthenticationService)
29+
protected readonly service: JsonRpcProxy<AuthenticationService>;
30+
31+
@inject(WindowService)
32+
protected readonly windowService: WindowService;
33+
34+
@inject(ArduinoPreferences)
35+
protected readonly arduinoPreferences: ArduinoPreferences;
36+
37+
protected authOptions: AuthOptions;
38+
protected _session: AuthenticationSession | undefined;
39+
protected readonly toDispose = new DisposableCollection();
40+
protected readonly onSessionDidChangeEmitter = new Emitter<
41+
AuthenticationSession | undefined
42+
>();
43+
44+
readonly onSessionDidChange = this.onSessionDidChangeEmitter.event;
45+
46+
onStart(): void {
47+
this.toDispose.push(this.onSessionDidChangeEmitter);
48+
this.service.setClient(this);
49+
this.service
50+
.session()
51+
.then((session) => this.notifySessionDidChange(session));
52+
this.setOptions();
53+
this.arduinoPreferences.onPreferenceChanged((event) => {
54+
if (event.preferenceName.startsWith('arduino.auth.')) {
55+
this.setOptions();
56+
}
57+
});
58+
}
59+
60+
setOptions(): void {
61+
this.service.setOptions({
62+
redirectUri: `http://localhost:${serverPort}/callback`,
63+
responseType: 'code',
64+
clientID: this.arduinoPreferences['arduino.auth.clientID'],
65+
domain: this.arduinoPreferences['arduino.auth.domain'],
66+
audience: this.arduinoPreferences['arduino.auth.audience'],
67+
registerUri: this.arduinoPreferences['arduino.auth.registerUri'],
68+
scopes: ['openid', 'profile', 'email', 'offline_access'],
69+
});
70+
}
71+
72+
protected updateSession(session?: AuthenticationSession | undefined) {
73+
this._session = session;
74+
this.onSessionDidChangeEmitter.fire(this._session);
75+
}
76+
77+
get session(): AuthenticationSession | undefined {
78+
return this._session;
79+
}
80+
81+
registerCommands(registry: CommandRegistry): void {
82+
registry.registerCommand(CloudUserCommands.LOGIN, {
83+
execute: () => this.service.login(),
84+
});
85+
registry.registerCommand(CloudUserCommands.LOGOUT, {
86+
execute: () => this.service.logout(),
87+
});
88+
registry.registerCommand(CloudUserCommands.REGISTER, {
89+
execute: () =>
90+
this.windowService.openNewWindow(
91+
this.arduinoPreferences['arduino.auth.registerUri'],
92+
{ external: true }
93+
),
94+
});
95+
}
96+
97+
notifySessionDidChange(session: AuthenticationSession | undefined): void {
98+
this.updateSession(session);
99+
}
100+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Command } from '@theia/core/lib/common/command';
2+
3+
export namespace CloudUserCommands {
4+
export const LOGIN: Command = {
5+
id: 'arduino-cloud--login',
6+
label: 'Sign in',
7+
};
8+
9+
export const LOGOUT: Command = {
10+
id: 'arduino-cloud--logout',
11+
label: 'Sign Out',
12+
};
13+
14+
export const REGISTER: Command = {
15+
id: 'arduino-cloud--register',
16+
label: 'Register',
17+
};
18+
19+
export const OPEN_PROFILE_CONTEXT_MENU: Command = {
20+
id: 'arduino-cloud-sketchbook--open-profile-menu',
21+
label: 'Contextual menu',
22+
};
23+
}

0 commit comments

Comments
 (0)