Skip to content

Commit feb39eb

Browse files
author
Akos Kitta
committed
feat: introduced cloud state in sketchbook view
Closes #1879 Closes #1899 Signed-off-by: Akos Kitta <[email protected]>
1 parent 24dc0bb commit feb39eb

37 files changed

+1073
-390
lines changed

Diff for: arduino-ide-extension/arduino-icons.json

+1-1
Large diffs are not rendered by default.

Diff for: arduino-ide-extension/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
"glob": "^7.1.6",
7878
"google-protobuf": "^3.20.1",
7979
"hash.js": "^1.1.7",
80+
"is-online": "^9.0.1",
8081
"js-yaml": "^3.13.1",
8182
"just-diff": "^5.1.1",
8283
"jwt-decode": "^3.1.2",

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ import { EditorCommandContribution as TheiaEditorCommandContribution } from '@th
9090
import {
9191
FrontendConnectionStatusService,
9292
ApplicationConnectionStatusContribution,
93+
DaemonPort,
94+
IsOnline,
9395
} from './theia/core/connection-status-service';
9496
import {
9597
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
@@ -738,6 +740,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
738740
Contribution.configure(bind, ValidateSketch);
739741
Contribution.configure(bind, RenameCloudSketch);
740742
Contribution.configure(bind, Account);
743+
Contribution.configure(bind, CloudSketchbookContribution);
741744

742745
bindContributionProvider(bind, StartupTaskProvider);
743746
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
@@ -916,8 +919,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
916919
bind(CreateFsProvider).toSelf().inSingletonScope();
917920
bind(FrontendApplicationContribution).toService(CreateFsProvider);
918921
bind(FileServiceContribution).toService(CreateFsProvider);
919-
bind(CloudSketchbookContribution).toSelf().inSingletonScope();
920-
bind(CommandContribution).toService(CloudSketchbookContribution);
921922
bind(LocalCacheFsProvider).toSelf().inSingletonScope();
922923
bind(FileServiceContribution).toService(LocalCacheFsProvider);
923924
bind(CloudSketchbookCompositeWidget).toSelf();
@@ -1021,4 +1022,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
10211022

10221023
bind(SidebarBottomMenuWidget).toSelf();
10231024
rebind(TheiaSidebarBottomMenuWidget).toService(SidebarBottomMenuWidget);
1025+
bind(DaemonPort).toSelf().inSingletonScope();
1026+
bind(FrontendApplicationContribution).toService(DaemonPort);
1027+
bind(IsOnline).toSelf().inSingletonScope();
1028+
bind(FrontendApplicationContribution).toService(IsOnline);
10241029
});

Diff for: arduino-ide-extension/src/browser/contributions/account.ts

+14-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { inject, injectable } from '@theia/core/shared/inversify';
88
import { CloudUserCommands, LEARN_MORE_URL } from '../auth/cloud-user-commands';
99
import { CreateFeatures } from '../create/create-features';
1010
import { ArduinoMenus } from '../menu/arduino-menus';
11+
import { ApplicationConnectionStatusContribution } from '../theia/core/connection-status-service';
1112
import {
1213
Command,
1314
CommandRegistry,
@@ -29,6 +30,8 @@ export class Account extends Contribution {
2930
private readonly windowService: WindowService;
3031
@inject(CreateFeatures)
3132
private readonly createFeatures: CreateFeatures;
33+
@inject(ApplicationConnectionStatusContribution)
34+
private readonly connectionStatus: ApplicationConnectionStatusContribution;
3235

3336
private readonly toDispose = new DisposableCollection();
3437
private app: FrontendApplication;
@@ -50,21 +53,28 @@ export class Account extends Contribution {
5053
override registerCommands(registry: CommandRegistry): void {
5154
const openExternal = (url: string) =>
5255
this.windowService.openNewWindow(url, { external: true });
56+
const loggedIn = () => Boolean(this.createFeatures.session);
57+
const loggedInWithInternetConnection = () =>
58+
loggedIn() && this.connectionStatus.offlineStatus !== 'internet';
5359
registry.registerCommand(Account.Commands.LEARN_MORE, {
5460
execute: () => openExternal(LEARN_MORE_URL),
55-
isEnabled: () => !Boolean(this.createFeatures.session),
61+
isEnabled: () => !loggedIn(),
62+
isVisible: () => !loggedIn(),
5663
});
5764
registry.registerCommand(Account.Commands.GO_TO_PROFILE, {
5865
execute: () => openExternal('https://id.arduino.cc/'),
59-
isEnabled: () => Boolean(this.createFeatures.session),
66+
isEnabled: () => loggedInWithInternetConnection(),
67+
isVisible: () => loggedIn(),
6068
});
6169
registry.registerCommand(Account.Commands.GO_TO_CLOUD_EDITOR, {
6270
execute: () => openExternal('https://create.arduino.cc/editor'),
63-
isEnabled: () => Boolean(this.createFeatures.session),
71+
isEnabled: () => loggedInWithInternetConnection(),
72+
isVisible: () => loggedIn(),
6473
});
6574
registry.registerCommand(Account.Commands.GO_TO_IOT_CLOUD, {
6675
execute: () => openExternal('https://create.arduino.cc/iot/'),
67-
isEnabled: () => Boolean(this.createFeatures.session),
76+
isEnabled: () => loggedInWithInternetConnection(),
77+
isVisible: () => loggedIn(),
6878
});
6979
}
7080

Diff for: arduino-ide-extension/src/browser/contributions/cloud-contribution.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export abstract class CloudSketchContribution extends SketchContribution {
9393
);
9494
}
9595
try {
96-
await treeModel.sketchbookTree().pull({ node });
96+
await treeModel.sketchbookTree().pull({ node }, true);
9797
return node;
9898
} catch (err) {
9999
if (isNotFound(err)) {

Diff for: arduino-ide-extension/src/browser/contributions/contribution.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
1414
import { MessageService } from '@theia/core/lib/common/message-service';
1515
import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
1616
import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
17-
1817
import {
1918
MenuModelRegistry,
2019
MenuContribution,
@@ -58,7 +57,7 @@ import { ClipboardService } from '@theia/core/lib/browser/clipboard-service';
5857
import { ExecuteWithProgress } from '../../common/protocol/progressible';
5958
import { BoardsServiceProvider } from '../boards/boards-service-provider';
6059
import { BoardsDataStore } from '../boards/boards-data-store';
61-
import { NotificationManager } from '../theia/messages/notifications-manager';
60+
import { NotificationManager } from '@theia/messages/lib/browser/notifications-manager';
6261
import { MessageType } from '@theia/core/lib/common/message-service-protocol';
6362
import { WorkspaceService } from '../theia/workspace/workspace-service';
6463
import { MainMenuManager } from '../../common/main-menu-manager';
@@ -295,7 +294,7 @@ export abstract class CoreServiceContribution extends SketchContribution {
295294
}
296295

297296
private notificationId(message: string, ...actions: string[]): string {
298-
return this.notificationManager.getMessageId({
297+
return this.notificationManager['getMessageId']({
299298
text: message,
300299
actions,
301300
type: MessageType.Error,

Diff for: arduino-ide-extension/src/browser/create/create-api.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ import { MaybePromise } from '@theia/core/lib/common/types';
22
import { inject, injectable } from '@theia/core/shared/inversify';
33
import { fetch } from 'cross-fetch';
44
import { SketchesService } from '../../common/protocol';
5-
import { unit8ArrayToString } from '../../common/utils';
5+
import { uint8ArrayToString } from '../../common/utils';
66
import { ArduinoPreferences } from '../arduino-preferences';
77
import { AuthenticationClientService } from '../auth/authentication-client-service';
88
import { SketchCache } from '../widgets/cloud-sketchbook/cloud-sketch-cache';
99
import * as createPaths from './create-paths';
1010
import { posix } from './create-paths';
1111
import { Create, CreateError } from './typings';
1212

13-
export interface ResponseResultProvider {
13+
interface ResponseResultProvider {
1414
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1515
(response: Response): Promise<any>;
1616
}
17-
export namespace ResponseResultProvider {
17+
namespace ResponseResultProvider {
1818
export const NOOP: ResponseResultProvider = async () => undefined;
1919
export const TEXT: ResponseResultProvider = (response) => response.text();
2020
export const JSON: ResponseResultProvider = (response) => response.json();
@@ -288,10 +288,9 @@ export class CreateApi {
288288
if (sketch) {
289289
const url = new URL(`${this.domain()}/sketches/${sketch.id}`);
290290
const headers = await this.headers();
291-
292291
// parse the secret file
293292
const secrets = (
294-
typeof content === 'string' ? content : unit8ArrayToString(content)
293+
typeof content === 'string' ? content : uint8ArrayToString(content)
295294
)
296295
.split(/\r?\n/)
297296
.reduce((prev, curr) => {
@@ -355,7 +354,7 @@ export class CreateApi {
355354
const headers = await this.headers();
356355

357356
let data: string =
358-
typeof content === 'string' ? content : unit8ArrayToString(content);
357+
typeof content === 'string' ? content : uint8ArrayToString(content);
359358
data = await this.toggleSecretsInclude(posixPath, data, 'remove');
360359

361360
const payload = { data: btoa(data) };

Diff for: arduino-ide-extension/src/browser/create/create-features.ts

+60-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import { AuthenticationSession } from '../../node/auth/types';
88
import { ArduinoPreferences } from '../arduino-preferences';
99
import { AuthenticationClientService } from '../auth/authentication-client-service';
1010
import { LocalCacheFsProvider } from '../local-cache/local-cache-fs-provider';
11+
import { CreateUri } from './create-uri';
12+
13+
export type CloudSketchState = 'push' | 'pull';
1114

1215
@injectable()
1316
export class CreateFeatures implements FrontendApplicationContribution {
@@ -18,13 +21,22 @@ export class CreateFeatures implements FrontendApplicationContribution {
1821
@inject(LocalCacheFsProvider)
1922
private readonly localCacheFsProvider: LocalCacheFsProvider;
2023

24+
/**
25+
* The keys are the Create URI of the sketches.
26+
*/
27+
private readonly _cloudSketchStates = new Map<string, CloudSketchState>();
2128
private readonly onDidChangeSessionEmitter = new Emitter<
2229
AuthenticationSession | undefined
2330
>();
2431
private readonly onDidChangeEnabledEmitter = new Emitter<boolean>();
32+
private readonly onDidChangeCloudSketchStateEmitter = new Emitter<{
33+
uri: URI;
34+
state: CloudSketchState | undefined;
35+
}>();
2536
private readonly toDispose = new DisposableCollection(
2637
this.onDidChangeSessionEmitter,
27-
this.onDidChangeEnabledEmitter
38+
this.onDidChangeEnabledEmitter,
39+
this.onDidChangeCloudSketchStateEmitter
2840
);
2941
private _enabled: boolean;
3042
private _session: AuthenticationSession | undefined;
@@ -64,14 +76,55 @@ export class CreateFeatures implements FrontendApplicationContribution {
6476
return this.onDidChangeEnabledEmitter.event;
6577
}
6678

67-
get enabled(): boolean {
68-
return this._enabled;
79+
get onDidChangeCloudSketchState(): Event<{
80+
uri: URI;
81+
state: CloudSketchState | undefined;
82+
}> {
83+
return this.onDidChangeCloudSketchStateEmitter.event;
6984
}
7085

7186
get session(): AuthenticationSession | undefined {
7287
return this._session;
7388
}
7489

90+
get enabled(): boolean {
91+
return this._enabled;
92+
}
93+
94+
get cloudSketchStates(): {
95+
uri: URI;
96+
state: CloudSketchState | undefined;
97+
}[] {
98+
return Array.from(this._cloudSketchStates.entries()).map(
99+
([uri, state]) => ({ uri: new URI(uri), state })
100+
);
101+
}
102+
103+
cloudSketchState(uri: URI): CloudSketchState | undefined {
104+
return this._cloudSketchStates.get(uri.toString());
105+
}
106+
107+
setCloudSketchState(uri: URI, state: CloudSketchState | undefined): void {
108+
if (uri.scheme !== CreateUri.scheme) {
109+
throw new Error(
110+
`Expected a URI with '${uri.scheme}' scheme. Got: ${uri.toString()}`
111+
);
112+
}
113+
const key = uri.toString();
114+
if (!state) {
115+
if (!this._cloudSketchStates.delete(key)) {
116+
console.warn(
117+
`Could not reset the cloud sketch state of ${key}. No state existed for the the cloud sketch.`
118+
);
119+
} else {
120+
this.onDidChangeCloudSketchStateEmitter.fire({ uri, state: undefined });
121+
}
122+
} else {
123+
this._cloudSketchStates.set(key, state);
124+
this.onDidChangeCloudSketchStateEmitter.fire({ uri, state });
125+
}
126+
}
127+
75128
/**
76129
* `true` if the sketch is under `directories.data/RemoteSketchbook`. Otherwise, `false`.
77130
* Returns with `undefined` if `dataDirUri` is `undefined`.
@@ -83,7 +136,10 @@ export class CreateFeatures implements FrontendApplicationContribution {
83136
);
84137
return undefined;
85138
}
86-
return dataDirUri.isEqualOrParent(new URI(sketch.uri));
139+
return dataDirUri
140+
.resolve('RemoteSketchbook')
141+
.resolve('ArduinoCloud')
142+
.isEqualOrParent(new URI(sketch.uri));
87143
}
88144

89145
cloudUri(sketch: Sketch): URI | undefined {
Loading
Loading
Loading
Loading

Diff for: arduino-ide-extension/src/browser/style/account-icon.svg

-1
This file was deleted.

0 commit comments

Comments
 (0)