Skip to content

Commit 512dad5

Browse files
author
Akos Kitta
committed
feat: copy sketch to the cloud
Closes #1876 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
1 parent eca7922 commit 512dad5

File tree

12 files changed

+306
-89
lines changed

12 files changed

+306
-89
lines changed

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

Lines changed: 2 additions & 0 deletions
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
Lines changed: 117 additions & 0 deletions
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+
}

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

Lines changed: 51 additions & 10 deletions
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
}
@@ -152,7 +193,7 @@ export class NewCloudSketch extends CloudSketchContribution {
152193
): Promise<void> {
153194
return this.commandService.executeCommand(
154195
SketchbookCommands.OPEN_NEW_WINDOW.id,
155-
{ node }
196+
{ node, treeWidgetId: 'cloud-sketchbook-composite-widget' }
156197
);
157198
}
158199
}

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

Lines changed: 1 addition & 1 deletion
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy