Skip to content

Commit 27be212

Browse files
author
Akos Kitta
committed
feat: copy sketch to the cloud
Closes #1876 Signed-off-by: Akos Kitta <[email protected]>
1 parent eca7922 commit 27be212

11 files changed

+293
-82
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ import { CreateFeatures } from './create/create-features';
352352
import { Account } from './contributions/account';
353353
import { SidebarBottomMenuWidget } from './theia/core/sidebar-bottom-menu-widget';
354354
import { SidebarBottomMenuWidget as TheiaSidebarBottomMenuWidget } from '@theia/core/lib/browser/shell/sidebar-bottom-menu-widget';
355+
import { CreateCloudCopy } from './contributions/create-cloud-copy';
355356

356357
export default new ContainerModule((bind, unbind, isBound, rebind) => {
357358
// Commands and toolbar items
@@ -741,6 +742,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
741742
Contribution.configure(bind, RenameCloudSketch);
742743
Contribution.configure(bind, Account);
743744
Contribution.configure(bind, CloudSketchbookContribution);
745+
Contribution.configure(bind, CreateCloudCopy);
744746

745747
bindContributionProvider(bind, StartupTaskProvider);
746748
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
2+
import { ApplicationShell } from '@theia/core/lib/browser/shell';
3+
import type { Command, CommandRegistry } from '@theia/core/lib/common/command';
4+
import { Progress } from '@theia/core/lib/common/message-service-protocol';
5+
import { nls } from '@theia/core/lib/common/nls';
6+
import { inject, injectable } from '@theia/core/shared/inversify';
7+
import { Create } from '../create/typings';
8+
import { ApplicationConnectionStatusContribution } from '../theia/core/connection-status-service';
9+
import { CloudSketchbookTree } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree';
10+
import { SketchbookTree } from '../widgets/sketchbook/sketchbook-tree';
11+
import { SketchbookTreeModel } from '../widgets/sketchbook/sketchbook-tree-model';
12+
import { CloudSketchContribution, pushingSketch } from './cloud-contribution';
13+
import {
14+
CreateNewCloudSketchCallback,
15+
NewCloudSketch,
16+
NewCloudSketchParams,
17+
} from './new-cloud-sketch';
18+
import { saveOntoCopiedSketch } from './save-as-sketch';
19+
20+
interface CreateCloudCopyParams {
21+
readonly model: SketchbookTreeModel;
22+
readonly node: SketchbookTree.SketchDirNode;
23+
}
24+
function isCreateCloudCopyParams(arg: unknown): arg is CreateCloudCopyParams {
25+
return (
26+
(<CreateCloudCopyParams>arg).model !== undefined &&
27+
(<CreateCloudCopyParams>arg).model instanceof SketchbookTreeModel &&
28+
(<CreateCloudCopyParams>arg).node !== undefined &&
29+
SketchbookTree.SketchDirNode.is((<CreateCloudCopyParams>arg).node)
30+
);
31+
}
32+
33+
@injectable()
34+
export class CreateCloudCopy extends CloudSketchContribution {
35+
@inject(ApplicationConnectionStatusContribution)
36+
private readonly connectionStatus: ApplicationConnectionStatusContribution;
37+
38+
private shell: ApplicationShell;
39+
40+
override onStart(app: FrontendApplication): void {
41+
this.shell = app.shell;
42+
}
43+
44+
override registerCommands(registry: CommandRegistry): void {
45+
registry.registerCommand(CreateCloudCopy.Commands.CREATE_CLOUD_COPY, {
46+
execute: (args: CreateCloudCopyParams) => this.createCloudCopy(args),
47+
isEnabled: (args: unknown) =>
48+
Boolean(this.createFeatures.session) && isCreateCloudCopyParams(args),
49+
isVisible: (args: unknown) =>
50+
Boolean(this.createFeatures.enabled) &&
51+
Boolean(this.createFeatures.session) &&
52+
this.connectionStatus.offlineStatus !== 'internet' &&
53+
isCreateCloudCopyParams(args),
54+
});
55+
}
56+
57+
/**
58+
* - creates new cloud sketch with the name of the params sketch,
59+
* - pulls the cloud sketch,
60+
* - copies files from params sketch to pulled cloud sketch in the cache folder,
61+
* - pushes the cloud sketch, and
62+
* - opens in new window.
63+
*/
64+
private async createCloudCopy(params: CreateCloudCopyParams): Promise<void> {
65+
const sketch = await this.sketchesService.loadSketch(
66+
params.node.fileStat.resource.toString()
67+
);
68+
const callback: CreateNewCloudSketchCallback = async (
69+
newSketch: Create.Sketch,
70+
newNode: CloudSketchbookTree.CloudSketchDirNode,
71+
progress: Progress
72+
) => {
73+
const treeModel = await this.treeModel();
74+
if (!treeModel) {
75+
throw new Error('Could not retrieve the cloud sketchbook tree model.');
76+
}
77+
78+
progress.report({
79+
message: nls.localize(
80+
'arduino/createCloudCopy/copyingSketchFilesMessage',
81+
'Copying local sketch files...'
82+
),
83+
});
84+
const localCacheFolderUri = newNode.uri.toString();
85+
await this.sketchesService.copy(sketch, {
86+
destinationUri: localCacheFolderUri,
87+
onlySketchFiles: true,
88+
});
89+
await saveOntoCopiedSketch(
90+
sketch,
91+
localCacheFolderUri,
92+
this.shell,
93+
this.editorManager
94+
);
95+
96+
progress.report({ message: pushingSketch(newSketch.name) });
97+
await treeModel.sketchbookTree().push(newNode, true);
98+
};
99+
return this.commandService.executeCommand(
100+
NewCloudSketch.Commands.NEW_CLOUD_SKETCH.id,
101+
<NewCloudSketchParams>{
102+
initialValue: params.node.fileStat.name,
103+
callback,
104+
skipShowErrorMessageOnOpen: false,
105+
}
106+
);
107+
}
108+
}
109+
110+
export namespace CreateCloudCopy {
111+
export namespace Commands {
112+
export const CREATE_CLOUD_COPY: Command = {
113+
id: 'arduino-create-cloud-copy',
114+
iconClass: 'fa fa-arduino-cloud-upload',
115+
};
116+
}
117+
}

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

+50-9
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Progress } from '@theia/core/lib/common/message-service-protocol';
66
import { nls } from '@theia/core/lib/common/nls';
77
import { injectable } from '@theia/core/shared/inversify';
88
import { CreateUri } from '../create/create-uri';
9-
import { isConflict } from '../create/typings';
9+
import { Create, isConflict } from '../create/typings';
1010
import { ArduinoMenus } from '../menu/arduino-menus';
1111
import {
1212
TaskFactoryImpl,
@@ -15,13 +15,36 @@ import {
1515
import { CloudSketchbookTree } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree';
1616
import { CloudSketchbookTreeModel } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree-model';
1717
import { SketchbookCommands } from '../widgets/sketchbook/sketchbook-commands';
18-
import { Command, CommandRegistry, Sketch } from './contribution';
1918
import {
2019
CloudSketchContribution,
2120
pullingSketch,
2221
sketchAlreadyExists,
2322
synchronizingSketchbook,
2423
} from './cloud-contribution';
24+
import { Command, CommandRegistry, Sketch } from './contribution';
25+
26+
export interface CreateNewCloudSketchCallback {
27+
(
28+
newSketch: Create.Sketch,
29+
newNode: CloudSketchbookTree.CloudSketchDirNode,
30+
progress: Progress
31+
): Promise<void>;
32+
}
33+
34+
export interface NewCloudSketchParams {
35+
/**
36+
* Value to populate the dialog `<input>` when it opens.
37+
*/
38+
readonly initialValue?: string | undefined;
39+
/**
40+
* Additional callback to call when the new cloud sketch has been created.
41+
*/
42+
readonly callback?: CreateNewCloudSketchCallback;
43+
/**
44+
* If `true`, the validation error message will not be visible in the input dialog, but the `OK` button will be disabled. Defaults to `true`.
45+
*/
46+
readonly skipShowErrorMessageOnOpen?: boolean;
47+
}
2548

2649
@injectable()
2750
export class NewCloudSketch extends CloudSketchContribution {
@@ -43,7 +66,14 @@ export class NewCloudSketch extends CloudSketchContribution {
4366

4467
override registerCommands(registry: CommandRegistry): void {
4568
registry.registerCommand(NewCloudSketch.Commands.NEW_CLOUD_SKETCH, {
46-
execute: () => this.createNewSketch(true),
69+
execute: (params: NewCloudSketchParams) =>
70+
this.createNewSketch(
71+
typeof params?.skipShowErrorMessageOnOpen === 'boolean'
72+
? params.skipShowErrorMessageOnOpen
73+
: true,
74+
params?.initialValue,
75+
params?.callback
76+
),
4777
isEnabled: () => Boolean(this.createFeatures.session),
4878
isVisible: () => this.createFeatures.enabled,
4979
});
@@ -66,7 +96,8 @@ export class NewCloudSketch extends CloudSketchContribution {
6696

6797
private async createNewSketch(
6898
skipShowErrorMessageOnOpen: boolean,
69-
initialValue?: string | undefined
99+
initialValue?: string | undefined,
100+
callback?: CreateNewCloudSketchCallback
70101
): Promise<void> {
71102
const treeModel = await this.treeModel();
72103
if (treeModel) {
@@ -75,7 +106,8 @@ export class NewCloudSketch extends CloudSketchContribution {
75106
rootNode,
76107
treeModel,
77108
skipShowErrorMessageOnOpen,
78-
initialValue
109+
initialValue,
110+
callback
79111
);
80112
}
81113
}
@@ -84,13 +116,14 @@ export class NewCloudSketch extends CloudSketchContribution {
84116
rootNode: CompositeTreeNode,
85117
treeModel: CloudSketchbookTreeModel,
86118
skipShowErrorMessageOnOpen: boolean,
87-
initialValue?: string | undefined
119+
initialValue?: string | undefined,
120+
callback?: CreateNewCloudSketchCallback
88121
): Promise<void> {
89122
const existingNames = rootNode.children
90123
.filter(CloudSketchbookTree.CloudSketchDirNode.is)
91124
.map(({ fileStat }) => fileStat.name);
92125
const taskFactory = new TaskFactoryImpl((value) =>
93-
this.createNewSketchWithProgress(treeModel, value)
126+
this.createNewSketchWithProgress(treeModel, value, callback)
94127
);
95128
try {
96129
const dialog = new WorkspaceInputDialogWithProgress(
@@ -118,15 +151,20 @@ export class NewCloudSketch extends CloudSketchContribution {
118151
} catch (err) {
119152
if (isConflict(err)) {
120153
await treeModel.refresh();
121-
return this.createNewSketch(false, taskFactory.value ?? initialValue);
154+
return this.createNewSketch(
155+
false,
156+
taskFactory.value ?? initialValue,
157+
callback
158+
);
122159
}
123160
throw err;
124161
}
125162
}
126163

127164
private createNewSketchWithProgress(
128165
treeModel: CloudSketchbookTreeModel,
129-
value: string
166+
value: string,
167+
callback?: CreateNewCloudSketchCallback
130168
): (
131169
progress: Progress
132170
) => Promise<CloudSketchbookTree.CloudSketchDirNode | undefined> {
@@ -143,6 +181,9 @@ export class NewCloudSketch extends CloudSketchContribution {
143181
await treeModel.refresh();
144182
progress.report({ message: pullingSketch(sketch.name) });
145183
const node = await this.pull(sketch);
184+
if (callback && node) {
185+
await callback(sketch, node, progress);
186+
}
146187
return node;
147188
};
148189
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export class RenameCloudSketch extends CloudSketchContribution {
123123
const toPosixPath = params.cloudUri.parent.resolve(value).path.toString();
124124
// push
125125
progress.report({ message: pushingSketch(params.sketch.name) });
126-
await treeModel.sketchbookTree().push(node);
126+
await treeModel.sketchbookTree().push(node, true);
127127

128128
// rename
129129
progress.report({

0 commit comments

Comments
 (0)