Skip to content

Commit ff6f8a9

Browse files
committed
Refactor remote sketchbook explorer
1 parent 3ba08e4 commit ff6f8a9

14 files changed

+461
-527
lines changed

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

+3
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ import { CloudSketchbookCompositeWidget } from './widgets/cloud-sketchbook/cloud
236236
import { SketchbookWidget } from './widgets/sketchbook/sketchbook-widget';
237237
import { SketchbookTreeWidget } from './widgets/sketchbook/sketchbook-tree-widget';
238238
import { createSketchbookTreeWidget } from './widgets/sketchbook/sketchbook-tree-container';
239+
import { SketchCache } from './widgets/cloud-sketchbook/cloud-sketch-cache';
239240

240241
const ElementQueries = require('css-element-queries/src/ElementQueries');
241242

@@ -686,6 +687,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
686687
createCloudSketchbookTreeWidget(container)
687688
);
688689
bind(CreateApi).toSelf().inSingletonScope();
690+
bind(SketchCache).toSelf().inSingletonScope();
691+
689692
bind(ShareSketchDialog).toSelf().inSingletonScope();
690693
bind(AuthenticationClientService).toSelf().inSingletonScope();
691694
bind(CommandContribution).toService(AuthenticationClientService);

arduino-ide-extension/src/browser/create/create-api.ts

+37-94
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { injectable } from 'inversify';
1+
import { injectable, inject } from 'inversify';
22
import * as createPaths from './create-paths';
3-
import { posix, splitSketchPath } from './create-paths';
3+
import { posix } from './create-paths';
44
import { AuthenticationClientService } from '../auth/authentication-client-service';
55
import { ArduinoPreferences } from '../arduino-preferences';
6+
import { SketchCache } from '../widgets/cloud-sketchbook/cloud-sketch-cache';
67

78
export interface ResponseResultProvider {
89
(response: Response): Promise<any>;
@@ -15,10 +16,11 @@ export namespace ResponseResultProvider {
1516

1617
type ResourceType = 'f' | 'd';
1718

18-
export let sketchCache: Create.Sketch[] = [];
19-
2019
@injectable()
2120
export class CreateApi {
21+
@inject(SketchCache)
22+
protected readonly sketchCache: SketchCache;
23+
2224
protected authenticationService: AuthenticationClientService;
2325
protected arduinoPreferences: ArduinoPreferences;
2426

@@ -32,33 +34,19 @@ export class CreateApi {
3234
return this;
3335
}
3436

35-
public sketchCompareByPath = (param: string) => {
36-
return (sketch: Create.Sketch) => {
37-
const [, spath] = splitSketchPath(sketch.path);
38-
return param === spath;
39-
};
40-
};
41-
42-
async findSketchInCache(
43-
compareFn: (sketch: Create.Sketch) => boolean,
44-
trustCache = true
45-
): Promise<Create.Sketch | undefined> {
46-
const sketch = sketchCache.find((sketch) => compareFn(sketch));
47-
if (trustCache) {
48-
return Promise.resolve(sketch);
49-
}
50-
return await this.sketch({ id: sketch?.id });
37+
public wipeCache(): void {
38+
this.sketchCache.init();
5139
}
5240

5341
getSketchSecretStat(sketch: Create.Sketch): Create.Resource {
5442
return {
5543
href: `${sketch.href}${posix.sep}${Create.arduino_secrets_file}`,
5644
modified_at: sketch.modified_at,
45+
created_at: sketch.created_at,
5746
name: `${Create.arduino_secrets_file}`,
5847
path: `${sketch.path}${posix.sep}${Create.arduino_secrets_file}`,
5948
mimetype: 'text/x-c++src; charset=utf-8',
6049
type: 'file',
61-
sketchId: sketch.id,
6250
};
6351
}
6452

@@ -92,7 +80,7 @@ export class CreateApi {
9280
method: 'GET',
9381
headers,
9482
});
95-
sketchCache = result.sketches;
83+
result.sketches.forEach((sketch) => this.sketchCache.addSketch(sketch));
9684
return result.sketches;
9785
}
9886

@@ -118,7 +106,7 @@ export class CreateApi {
118106

119107
async readDirectory(
120108
posixPath: string,
121-
options: { recursive?: boolean; match?: string; secrets?: boolean } = {}
109+
options: { recursive?: boolean; match?: string } = {}
122110
): Promise<Create.Resource[]> {
123111
const url = new URL(
124112
`${this.domain()}/files/d/$HOME/sketches_v2${posixPath}`
@@ -131,58 +119,21 @@ export class CreateApi {
131119
}
132120
const headers = await this.headers();
133121

134-
const sketchProm = options.secrets
135-
? this.sketches()
136-
: Promise.resolve(sketchCache);
137-
138-
return Promise.all([
139-
this.run<Create.RawResource[]>(url, {
140-
method: 'GET',
141-
headers,
142-
}),
143-
sketchProm,
144-
])
145-
.then(async ([result, sketches]) => {
146-
if (options.secrets) {
147-
// for every sketch with secrets, create a fake arduino_secrets.h
148-
result.forEach(async (res) => {
149-
if (res.type !== 'sketch') {
150-
return;
151-
}
152-
153-
const [, spath] = createPaths.splitSketchPath(res.path);
154-
const sketch = await this.findSketchInCache(
155-
this.sketchCompareByPath(spath)
156-
);
157-
if (sketch && sketch.secrets && sketch.secrets.length > 0) {
158-
result.push(this.getSketchSecretStat(sketch));
159-
}
160-
});
161-
162-
if (posixPath !== posix.sep) {
163-
const sketch = await this.findSketchInCache(
164-
this.sketchCompareByPath(posixPath)
165-
);
166-
if (sketch && sketch.secrets && sketch.secrets.length > 0) {
167-
result.push(this.getSketchSecretStat(sketch));
168-
}
122+
return this.run<Create.RawResource[]>(url, {
123+
method: 'GET',
124+
headers,
125+
})
126+
.then(async (result) => {
127+
// add arduino_secrets.h to the results, when reading a sketch main folder
128+
if (posixPath.length && posixPath !== posix.sep) {
129+
const sketch = this.sketchCache.getSketch(posixPath);
130+
131+
if (sketch && sketch.secrets && sketch.secrets.length > 0) {
132+
result.push(this.getSketchSecretStat(sketch));
169133
}
170134
}
171-
const sketchesMap: Record<string, Create.Sketch> = sketches.reduce(
172-
(prev, curr) => {
173-
return { ...prev, [curr.path]: curr };
174-
},
175-
{}
176-
);
177-
178-
// add the sketch id and isPublic to the resource
179-
return result.map((resource) => {
180-
return {
181-
...resource,
182-
sketchId: sketchesMap[resource.path]?.id || '',
183-
isPublic: sketchesMap[resource.path]?.is_public || false,
184-
};
185-
});
135+
136+
return result;
186137
})
187138
.catch((reason) => {
188139
if (reason?.status === 404) return [] as Create.Resource[];
@@ -214,18 +165,16 @@ export class CreateApi {
214165

215166
let resources;
216167
if (basename === Create.arduino_secrets_file) {
217-
const sketch = await this.findSketchInCache(
218-
this.sketchCompareByPath(parentPosixPath)
219-
);
168+
const sketch = this.sketchCache.getSketch(parentPosixPath);
220169
resources = sketch ? [this.getSketchSecretStat(sketch)] : [];
221170
} else {
222171
resources = await this.readDirectory(parentPosixPath, {
223172
match: basename,
224173
});
225174
}
226-
227-
resources.sort((left, right) => left.path.length - right.path.length);
228-
const resource = resources.find(({ name }) => name === basename);
175+
const resource = resources.find(
176+
({ path }) => createPaths.splitSketchPath(path)[1] === posixPath
177+
);
229178
if (!resource) {
230179
throw new CreateError(`Not found: ${posixPath}.`, 404);
231180
}
@@ -248,10 +197,7 @@ export class CreateApi {
248197
return data;
249198
}
250199

251-
const sketch = await this.findSketchInCache((sketch) => {
252-
const [, spath] = splitSketchPath(sketch.path);
253-
return spath === createPaths.parentPosix(path);
254-
}, true);
200+
const sketch = this.sketchCache.getSketch(createPaths.parentPosix(path));
255201

256202
if (
257203
sketch &&
@@ -273,14 +219,11 @@ export class CreateApi {
273219

274220
if (basename === Create.arduino_secrets_file) {
275221
const parentPosixPath = createPaths.parentPosix(posixPath);
276-
const sketch = await this.findSketchInCache(
277-
this.sketchCompareByPath(parentPosixPath),
278-
false
279-
);
222+
const sketch = this.sketchCache.getSketch(parentPosixPath);
280223

281224
let file = '';
282225
if (sketch && sketch.secrets) {
283-
for (const item of sketch?.secrets) {
226+
for (const item of sketch.secrets) {
284227
file += `#define ${item.name} "${item.value}"\r\n`;
285228
}
286229
}
@@ -310,9 +253,9 @@ export class CreateApi {
310253

311254
if (basename === Create.arduino_secrets_file) {
312255
const parentPosixPath = createPaths.parentPosix(posixPath);
313-
const sketch = await this.findSketchInCache(
314-
this.sketchCompareByPath(parentPosixPath)
315-
);
256+
257+
const sketch = this.sketchCache.getSketch(parentPosixPath);
258+
316259
if (sketch) {
317260
const url = new URL(`${this.domain()}/sketches/${sketch.id}`);
318261
const headers = await this.headers();
@@ -357,8 +300,7 @@ export class CreateApi {
357300
};
358301

359302
// replace the sketch in the cache, so other calls will not overwrite each other
360-
sketchCache = sketchCache.filter((skt) => skt.id !== sketch.id);
361-
sketchCache.push({ ...sketch, secrets });
303+
this.sketchCache.addSketch(sketch);
362304

363305
const init = {
364306
method: 'POST',
@@ -543,8 +485,9 @@ export namespace Create {
543485
*/
544486
readonly path: string;
545487
readonly type: ResourceType;
546-
readonly sketchId: string;
488+
readonly sketchId?: string;
547489
readonly modified_at: string; // As an ISO-8601 formatted string: `YYYY-MM-DDTHH:mm:ss.sssZ`
490+
readonly created_at: string; // As an ISO-8601 formatted string: `YYYY-MM-DDTHH:mm:ss.sssZ`
548491
readonly children?: number; // For 'sketch' and 'folder' types.
549492
readonly size?: number; // For 'sketch' type only.
550493
readonly isPublic?: boolean; // For 'sketch' type only.

arduino-ide-extension/src/browser/create/create-fs-provider.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,7 @@ export class CreateFsProvider
106106

107107
async readdir(uri: URI): Promise<[string, FileType][]> {
108108
const resources = await this.getCreateApi.readDirectory(
109-
uri.path.toString(),
110-
{
111-
secrets: true,
112-
}
109+
uri.path.toString()
113110
);
114111
return resources
115112
.filter((res) => !REMOTE_ONLY_FILES.includes(res.name))

arduino-ide-extension/src/browser/local-cache/local-cache-fs-provider.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class LocalCacheFsProvider
103103
});
104104
}
105105

106-
private get currentUserUri(): URI {
106+
public get currentUserUri(): URI {
107107
const { session } = this.authenticationService;
108108
if (!session) {
109109
throw new FileSystemProviderError(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { FileStat } from '@theia/filesystem/lib/common/files';
2+
import { injectable } from 'inversify';
3+
import { Create } from '../../create/create-api';
4+
import { toPosixPath } from '../../create/create-paths';
5+
6+
@injectable()
7+
export class SketchCache {
8+
sketches: Record<string, Create.Sketch> = {};
9+
filestats: Record<string, FileStat> = {};
10+
11+
init(): void {
12+
// reset the data
13+
this.sketches = {};
14+
this.filestats = {};
15+
}
16+
17+
addItem(item: FileStat): void {
18+
this.filestats[item.resource.path.toString()] = item;
19+
}
20+
21+
getItem(path: string): FileStat | null {
22+
return this.filestats[path] || null;
23+
}
24+
25+
addSketch(sketch: Create.Sketch): void {
26+
const { path } = sketch;
27+
const posixPath = toPosixPath(path);
28+
this.sketches[posixPath] = sketch;
29+
}
30+
31+
getSketch(path: string): Create.Sketch | null {
32+
return this.sketches[path] || null;
33+
}
34+
}

arduino-ide-extension/src/browser/widgets/cloud-sketchbook/cloud-sketchbook-composite-widget.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export class CloudSketchbookCompositeWidget extends BaseWidget {
3535
this.id = 'cloud-sketchbook-composite-widget';
3636
}
3737

38+
public getTreeWidget(): CloudSketchbookTreeWidget {
39+
return this.cloudSketchbookTreeWidget;
40+
}
41+
3842
protected onAfterAttach(message: Message): void {
3943
super.onAfterAttach(message);
4044
Widget.attach(this.cloudSketchbookTreeWidget, this.compositeNode);

arduino-ide-extension/src/browser/widgets/cloud-sketchbook/cloud-sketchbook-contributions.ts

+4-13
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,11 @@ export class CloudSketchbookContribution extends Contribution {
166166
isEnabled: (arg) =>
167167
CloudSketchbookCommands.Arg.is(arg) &&
168168
CloudSketchbookTree.CloudSketchDirNode.is(arg.node) &&
169-
!!arg.node.synced,
169+
CloudSketchbookTree.CloudSketchTreeNode.isSynced(arg.node),
170170
isVisible: (arg) =>
171171
CloudSketchbookCommands.Arg.is(arg) &&
172172
CloudSketchbookTree.CloudSketchDirNode.is(arg.node) &&
173-
!!arg.node.synced,
173+
CloudSketchbookTree.CloudSketchTreeNode.isSynced(arg.node),
174174
});
175175

176176
registry.registerCommand(CloudSketchbookCommands.OPEN_IN_CLOUD_EDITOR, {
@@ -257,18 +257,10 @@ export class CloudSketchbookContribution extends Contribution {
257257

258258
const currentSketch = await this.sketchServiceClient.currentSketch();
259259

260-
const localUri = await arg.model.cloudSketchbookTree.localUri(
261-
arg.node
262-
);
263-
let underlying = null;
264-
if (arg.node && localUri) {
265-
underlying = await this.fileService.toUnderlyingResource(localUri);
266-
}
267-
268260
// disable the "open sketch" command for the current sketch and for those not in sync
269261
if (
270-
!underlying ||
271-
(currentSketch && currentSketch.uri === underlying.toString())
262+
!CloudSketchbookTree.CloudSketchTreeNode.isSynced(arg.node) ||
263+
(currentSketch && currentSketch.uri === arg.node.uri.toString())
272264
) {
273265
const placeholder = new PlaceholderMenuNode(
274266
SKETCHBOOKSYNC__CONTEXT__MAIN_GROUP,
@@ -284,7 +276,6 @@ export class CloudSketchbookContribution extends Contribution {
284276
)
285277
);
286278
} else {
287-
arg.node.uri = localUri;
288279
this.menuRegistry.registerMenuAction(
289280
SKETCHBOOKSYNC__CONTEXT__MAIN_GROUP,
290281
{

0 commit comments

Comments
 (0)