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 d790266

Browse files
authored
Improve remote sketchbook explorer (#459)
* Refactor remote sketchbook explorer * sketches sorting
1 parent 4da5d57 commit d790266

16 files changed

+593
-614
lines changed

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

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

Lines changed: 60 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
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';
7+
import { Create, CreateError } from './typings';
68

79
export interface ResponseResultProvider {
810
(response: Response): Promise<any>;
@@ -15,10 +17,11 @@ export namespace ResponseResultProvider {
1517

1618
type ResourceType = 'f' | 'd';
1719

18-
export let sketchCache: Create.Sketch[] = [];
19-
2020
@injectable()
2121
export class CreateApi {
22+
@inject(SketchCache)
23+
protected sketchCache: SketchCache;
24+
2225
protected authenticationService: AuthenticationClientService;
2326
protected arduinoPreferences: ArduinoPreferences;
2427

@@ -32,48 +35,20 @@ export class CreateApi {
3235
return this;
3336
}
3437

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 });
51-
}
52-
5338
getSketchSecretStat(sketch: Create.Sketch): Create.Resource {
5439
return {
5540
href: `${sketch.href}${posix.sep}${Create.arduino_secrets_file}`,
5641
modified_at: sketch.modified_at,
42+
created_at: sketch.created_at,
5743
name: `${Create.arduino_secrets_file}`,
5844
path: `${sketch.path}${posix.sep}${Create.arduino_secrets_file}`,
5945
mimetype: 'text/x-c++src; charset=utf-8',
6046
type: 'file',
61-
sketchId: sketch.id,
6247
};
6348
}
6449

65-
async sketch(opt: {
66-
id?: string;
67-
path?: string;
68-
}): Promise<Create.Sketch | undefined> {
69-
let url;
70-
if (opt.id) {
71-
url = new URL(`${this.domain()}/sketches/byID/${opt.id}`);
72-
} else if (opt.path) {
73-
url = new URL(`${this.domain()}/sketches/byPath${opt.path}`);
74-
} else {
75-
return;
76-
}
50+
async sketch(id: string): Promise<Create.Sketch> {
51+
const url = new URL(`${this.domain()}/sketches/byID/${id}`);
7752

7853
url.searchParams.set('user_id', 'me');
7954
const headers = await this.headers();
@@ -92,7 +67,7 @@ export class CreateApi {
9267
method: 'GET',
9368
headers,
9469
});
95-
sketchCache=result.sketches;
70+
result.sketches.forEach((sketch)=>this.sketchCache.addSketch(sketch));
9671
return result.sketches;
9772
}
9873

@@ -118,7 +93,7 @@ export class CreateApi {
11893

11994
async readDirectory(
12095
posixPath: string,
121-
options: { recursive?: boolean; match?: string;secrets?: boolean } = {}
96+
options: { recursive?: boolean; match?: string } = {}
12297
): Promise<Create.Resource[]> {
12398
const url = new URL(
12499
`${this.domain()}/files/d/$HOME/sketches_v2${posixPath}`
@@ -131,58 +106,21 @@ export class CreateApi {
131106
}
132107
const headers = await this.headers();
133108

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-
}
109+
return this.run<Create.RawResource[]>(url, {
110+
method: 'GET',
111+
headers,
112+
})
113+
.then(async (result) => {
114+
// add arduino_secrets.h to the results, when reading a sketch main folder
115+
if (posixPath.length && posixPath !== posix.sep) {
116+
const sketch = this.sketchCache.getSketch(posixPath);
117+
118+
if (sketch && sketch.secrets && sketch.secrets.length > 0) {
119+
result.push(this.getSketchSecretStat(sketch));
169120
}
170121
}
171-
const sketchesMap: Record<string, Create.Sketch> = sketches.reduce(
172-
(prev, curr) => {
173-
return { ...prev, [curr.path]: curr };
174-
},
175-
{}
176-
);
177122

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-
});
123+
return result;
186124
})
187125
.catch((reason) => {
188126
if (reason?.status === 404) return [] as Create.Resource[];
@@ -214,18 +152,16 @@ export class CreateApi {
214152

215153
let resources;
216154
if (basename === Create.arduino_secrets_file) {
217-
const sketch = await this.findSketchInCache(
218-
this.sketchCompareByPath(parentPosixPath)
219-
);
155+
const sketch = this.sketchCache.getSketch(parentPosixPath);
220156
resources = sketch ? [this.getSketchSecretStat(sketch)] : [];
221157
} else {
222158
resources = await this.readDirectory(parentPosixPath, {
223159
match: basename,
224160
});
225161
}
226-
227-
resources.sort((left,right) => left.path.length-right.path.length);
228-
constresource=resources.find(({ name })=>name===basename);
162+
constresource=resources.find(
163+
({ path }) => createPaths.splitSketchPath(path)[1]===posixPath
164+
);
229165
if (!resource) {
230166
throw new CreateError(`Not found: ${posixPath}.`, 404);
231167
}
@@ -248,10 +184,7 @@ export class CreateApi {
248184
return data;
249185
}
250186

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

256189
if (
257190
sketch &&
@@ -273,14 +206,25 @@ export class CreateApi {
273206

274207
if (basename === Create.arduino_secrets_file) {
275208
const parentPosixPath = createPaths.parentPosix(posixPath);
276-
const sketch = await this.findSketchInCache(
277-
this.sketchCompareByPath(parentPosixPath),
278-
false
279-
);
209+
210+
//retrieve the sketch id from the cache
211+
const cacheSketch = this.sketchCache.getSketch(parentPosixPath);
212+
if (!cacheSketch) {
213+
throw new Error(`Unable to find sketch ${parentPosixPath} in cache`);
214+
}
215+
216+
// get a fresh copy of the sketch in order to guarantee fresh secrets
217+
const sketch = await this.sketch(cacheSketch.id);
218+
if (!sketch) {
219+
throw new Error(
220+
`Unable to get a fresh copy of the sketch ${cacheSketch.id}`
221+
);
222+
}
223+
this.sketchCache.addSketch(sketch);
280224

281225
let file = '';
282226
if (sketch && sketch.secrets) {
283-
for (const item of sketch?.secrets) {
227+
for (const item of sketch.secrets) {
284228
file += `#define ${item.name} "${item.value}"\r\n`;
285229
}
286230
}
@@ -310,9 +254,9 @@ export class CreateApi {
310254

311255
if (basename === Create.arduino_secrets_file) {
312256
const parentPosixPath = createPaths.parentPosix(posixPath);
313-
constsketch=awaitthis.findSketchInCache(
314-
this.sketchCompareByPath(parentPosixPath)
315-
);
257+
258+
constsketch=this.sketchCache.getSketch(parentPosixPath);
259+
316260
if (sketch) {
317261
const url = new URL(`${this.domain()}/sketches/${sketch.id}`);
318262
const headers = await this.headers();
@@ -356,9 +300,10 @@ export class CreateApi {
356300
secrets: { data: secrets },
357301
};
358302

359-
// 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+
// replace the sketch in the cache with the one we are pushing
304+
// TODO: we should do a get after the POST, in order to be sure the cache
305+
// is updated the most recent metadata
306+
this.sketchCache.addSketch(sketch);
362307

363308
const init = {
364309
method: 'POST',
@@ -370,6 +315,14 @@ export class CreateApi {
370315
return;
371316
}
372317

318+
// do not upload "do_not_sync" files/directoris and their descendants
319+
const segments = posixPath.split(posix.sep) || [];
320+
if (
321+
segments.some((segment) => Create.do_not_sync_files.includes(segment))
322+
) {
323+
return;
324+
}
325+
373326
const url = new URL(
374327
`${this.domain()}/files/f/$HOME/sketches_v2${posixPath}`
375328
);
@@ -512,75 +465,3 @@ void loop() {
512465
513466
`;
514467
}
515-
516-
export namespace Create {
517-
export interface Sketch {
518-
readonly name: string;
519-
readonly path: string;
520-
readonly modified_at: string;
521-
readonly created_at: string;
522-
523-
readonly secrets?: { name: string; value: string }[];
524-
525-
readonly id: string;
526-
readonly is_public: boolean;
527-
// readonly board_fqbn: '',
528-
// readonly board_name: '',
529-
// readonly board_type: 'serial' | 'network' | 'cloud' | '',
530-
readonly href?: string;
531-
readonly libraries: string[];
532-
// readonly tutorials: string[] | null;
533-
// readonly types: string[] | null;
534-
// readonly user_id: string;
535-
}
536-
537-
export type ResourceType = 'sketch' | 'folder' | 'file';
538-
export const arduino_secrets_file = 'arduino_secrets.h';
539-
export interface Resource {
540-
readonly name: string;
541-
/**
542-
* Note: this path is **not** the POSIX path we use. It has the leading segments with the `user_id`.
543-
*/
544-
readonly path: string;
545-
readonly type: ResourceType;
546-
readonly sketchId: string;
547-
readonly modified_at: string; // As an ISO-8601 formatted string: `YYYY-MM-DDTHH:mm:ss.sssZ`
548-
readonly children?: number; // For 'sketch' and 'folder' types.
549-
readonly size?: number; // For 'sketch' type only.
550-
readonly isPublic?: boolean; // For 'sketch' type only.
551-
552-
readonly mimetype?: string; // For 'file' type.
553-
readonly href?: string;
554-
}
555-
export namespace Resource {
556-
export function is(arg: any): arg is Resource {
557-
return (
558-
!!arg &&
559-
'name' in arg &&
560-
typeof arg['name'] === 'string' &&
561-
'path' in arg &&
562-
typeof arg['path'] === 'string' &&
563-
'type' in arg &&
564-
typeof arg['type'] === 'string' &&
565-
'modified_at' in arg &&
566-
typeof arg['modified_at'] === 'string' &&
567-
(arg['type'] === 'sketch' ||
568-
arg['type'] === 'folder' ||
569-
arg['type'] === 'file')
570-
);
571-
}
572-
}
573-
574-
export type RawResource = Omit<Resource, 'sketchId' | 'isPublic'>;
575-
}
576-
577-
export class CreateError extends Error {
578-
constructor(
579-
message: string,
580-
readonly status: number,
581-
readonly details?: string
582-
) {
583-
super(message);
584-
Object.setPrototypeOf(this, CreateError.prototype);
585-
}
586-
}

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ import {
2424
FileServiceContribution,
2525
} from '@theia/filesystem/lib/browser/file-service';
2626
import { AuthenticationClientService } from '../auth/authentication-client-service';
27-
import { Create,CreateApi } from './create-api';
27+
import { CreateApi } from './create-api';
2828
import { CreateUri } from './create-uri';
2929
import { SketchesService } from '../../common/protocol';
3030
import { ArduinoPreferences } from '../arduino-preferences';
31+
import { Create } from './typings';
3132

3233
export const REMOTE_ONLY_FILES = ['sketch.json'];
3334

@@ -106,10 +107,7 @@ export class CreateFsProvider
106107

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

0 commit comments

Comments
(0)

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