Skip to content

Commit

Permalink
feat: copy sketch to the cloud
Browse files Browse the repository at this point in the history
Closes #1876

Signed-off-by: Akos Kitta <[email protected]>
  • Loading branch information
Akos Kitta committed Feb 24, 2023
1 parent eca7922 commit f840f7f
Show file tree
Hide file tree
Showing 11 changed files with 293 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ import { CreateFeatures } from './create/create-features';
import { Account } from './contributions/account';
import { SidebarBottomMenuWidget } from './theia/core/sidebar-bottom-menu-widget';
import { SidebarBottomMenuWidget as TheiaSidebarBottomMenuWidget } from '@theia/core/lib/browser/shell/sidebar-bottom-menu-widget';
import { CreateCloudCopy } from './contributions/create-cloud-copy';

export default new ContainerModule((bind, unbind, isBound, rebind) => {
// Commands and toolbar items
Expand Down Expand Up @@ -741,6 +742,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
Contribution.configure(bind, RenameCloudSketch);
Contribution.configure(bind, Account);
Contribution.configure(bind, CloudSketchbookContribution);
Contribution.configure(bind, CreateCloudCopy);

bindContributionProvider(bind, StartupTaskProvider);
bind(StartupTaskProvider).toService(BoardsServiceProvider); // to reuse the boards config in another window
Expand Down
117 changes: 117 additions & 0 deletions arduino-ide-extension/src/browser/contributions/create-cloud-copy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { ApplicationShell } from '@theia/core/lib/browser/shell';
import type { Command, CommandRegistry } from '@theia/core/lib/common/command';
import { Progress } from '@theia/core/lib/common/message-service-protocol';
import { nls } from '@theia/core/lib/common/nls';
import { inject, injectable } from '@theia/core/shared/inversify';
import { Create } from '../create/typings';
import { ApplicationConnectionStatusContribution } from '../theia/core/connection-status-service';
import { CloudSketchbookTree } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree';
import { SketchbookTree } from '../widgets/sketchbook/sketchbook-tree';
import { SketchbookTreeModel } from '../widgets/sketchbook/sketchbook-tree-model';
import { CloudSketchContribution, pushingSketch } from './cloud-contribution';
import {
CreateNewCloudSketchCallback,
NewCloudSketch,
NewCloudSketchParams,
} from './new-cloud-sketch';
import { saveOntoCopiedSketch } from './save-as-sketch';

interface CreateCloudCopyParams {
readonly model: SketchbookTreeModel;
readonly node: SketchbookTree.SketchDirNode;
}
function isCreateCloudCopyParams(arg: unknown): arg is CreateCloudCopyParams {
return (
(<CreateCloudCopyParams>arg).model !== undefined &&
(<CreateCloudCopyParams>arg).model instanceof SketchbookTreeModel &&
(<CreateCloudCopyParams>arg).node !== undefined &&
SketchbookTree.SketchDirNode.is((<CreateCloudCopyParams>arg).node)
);
}

@injectable()
export class CreateCloudCopy extends CloudSketchContribution {
@inject(ApplicationConnectionStatusContribution)
private readonly connectionStatus: ApplicationConnectionStatusContribution;

private shell: ApplicationShell;

override onStart(app: FrontendApplication): void {
this.shell = app.shell;
}

override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(CreateCloudCopy.Commands.CREATE_CLOUD_COPY, {
execute: (args: CreateCloudCopyParams) => this.createCloudCopy(args),
isEnabled: (args: unknown) =>
Boolean(this.createFeatures.session) && isCreateCloudCopyParams(args),
isVisible: (args: unknown) =>
Boolean(this.createFeatures.enabled) &&
Boolean(this.createFeatures.session) &&
this.connectionStatus.offlineStatus !== 'internet' &&
isCreateCloudCopyParams(args),
});
}

/**
* - creates new cloud sketch with the name of the params sketch,
* - pulls the cloud sketch,
* - copies files from params sketch to pulled cloud sketch in the cache folder,
* - pushes the cloud sketch, and
* - opens in new window.
*/
private async createCloudCopy(params: CreateCloudCopyParams): Promise<void> {
const sketch = await this.sketchesService.loadSketch(
params.node.fileStat.resource.toString()
);
const callback: CreateNewCloudSketchCallback = async (
newSketch: Create.Sketch,
newNode: CloudSketchbookTree.CloudSketchDirNode,
progress: Progress
) => {
const treeModel = await this.treeModel();
if (!treeModel) {
throw new Error('Could not retrieve the cloud sketchbook tree model.');
}

progress.report({
message: nls.localize(
'arduino/createCloudCopy/copyingSketchFilesMessage',
'Copying local sketch files...'
),
});
const localCacheFolderUri = newNode.uri.toString();
await this.sketchesService.copy(sketch, {
destinationUri: localCacheFolderUri,
onlySketchFiles: true,
});
await saveOntoCopiedSketch(
sketch,
localCacheFolderUri,
this.shell,
this.editorManager
);

progress.report({ message: pushingSketch(newSketch.name) });
await treeModel.sketchbookTree().push(newNode);
};
return this.commandService.executeCommand(
NewCloudSketch.Commands.NEW_CLOUD_SKETCH.id,
<NewCloudSketchParams>{
initialValue: params.node.fileStat.name,
callback,
skipShowErrorMessageOnOpen: false,
}
);
}
}

export namespace CreateCloudCopy {
export namespace Commands {
export const CREATE_CLOUD_COPY: Command = {
id: 'arduino-create-cloud-copy',
iconClass: 'fa fa-arduino-cloud-upload',
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Progress } from '@theia/core/lib/common/message-service-protocol';
import { nls } from '@theia/core/lib/common/nls';
import { injectable } from '@theia/core/shared/inversify';
import { CreateUri } from '../create/create-uri';
import { isConflict } from '../create/typings';
import { Create, isConflict } from '../create/typings';
import { ArduinoMenus } from '../menu/arduino-menus';
import {
TaskFactoryImpl,
Expand All @@ -15,13 +15,36 @@ import {
import { CloudSketchbookTree } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree';
import { CloudSketchbookTreeModel } from '../widgets/cloud-sketchbook/cloud-sketchbook-tree-model';
import { SketchbookCommands } from '../widgets/sketchbook/sketchbook-commands';
import { Command, CommandRegistry, Sketch } from './contribution';
import {
CloudSketchContribution,
pullingSketch,
sketchAlreadyExists,
synchronizingSketchbook,
} from './cloud-contribution';
import { Command, CommandRegistry, Sketch } from './contribution';

export interface CreateNewCloudSketchCallback {
(
newSketch: Create.Sketch,
newNode: CloudSketchbookTree.CloudSketchDirNode,
progress: Progress
): Promise<void>;
}

export interface NewCloudSketchParams {
/**
* Value to populate the dialog `<input>` when it opens.
*/
readonly initialValue?: string | undefined;
/**
* Additional callback to call when the new cloud sketch has been created.
*/
readonly callback?: CreateNewCloudSketchCallback;
/**
* If `true`, the validation error message will not be visible in the input dialog, but the `OK` button will be disabled. Defaults to `true`.
*/
readonly skipShowErrorMessageOnOpen?: boolean;
}

@injectable()
export class NewCloudSketch extends CloudSketchContribution {
Expand All @@ -43,7 +66,14 @@ export class NewCloudSketch extends CloudSketchContribution {

override registerCommands(registry: CommandRegistry): void {
registry.registerCommand(NewCloudSketch.Commands.NEW_CLOUD_SKETCH, {
execute: () => this.createNewSketch(true),
execute: (params: NewCloudSketchParams) =>
this.createNewSketch(
typeof params.skipShowErrorMessageOnOpen === 'boolean'
? params.skipShowErrorMessageOnOpen
: true,
params.initialValue,
params.callback
),
isEnabled: () => Boolean(this.createFeatures.session),
isVisible: () => this.createFeatures.enabled,
});
Expand All @@ -66,7 +96,8 @@ export class NewCloudSketch extends CloudSketchContribution {

private async createNewSketch(
skipShowErrorMessageOnOpen: boolean,
initialValue?: string | undefined
initialValue?: string | undefined,
callback?: CreateNewCloudSketchCallback
): Promise<void> {
const treeModel = await this.treeModel();
if (treeModel) {
Expand All @@ -75,7 +106,8 @@ export class NewCloudSketch extends CloudSketchContribution {
rootNode,
treeModel,
skipShowErrorMessageOnOpen,
initialValue
initialValue,
callback
);
}
}
Expand All @@ -84,13 +116,14 @@ export class NewCloudSketch extends CloudSketchContribution {
rootNode: CompositeTreeNode,
treeModel: CloudSketchbookTreeModel,
skipShowErrorMessageOnOpen: boolean,
initialValue?: string | undefined
initialValue?: string | undefined,
callback?: CreateNewCloudSketchCallback
): Promise<void> {
const existingNames = rootNode.children
.filter(CloudSketchbookTree.CloudSketchDirNode.is)
.map(({ fileStat }) => fileStat.name);
const taskFactory = new TaskFactoryImpl((value) =>
this.createNewSketchWithProgress(treeModel, value)
this.createNewSketchWithProgress(treeModel, value, callback)
);
try {
const dialog = new WorkspaceInputDialogWithProgress(
Expand Down Expand Up @@ -118,15 +151,20 @@ export class NewCloudSketch extends CloudSketchContribution {
} catch (err) {
if (isConflict(err)) {
await treeModel.refresh();
return this.createNewSketch(false, taskFactory.value ?? initialValue);
return this.createNewSketch(
false,
taskFactory.value ?? initialValue,
callback
);
}
throw err;
}
}

private createNewSketchWithProgress(
treeModel: CloudSketchbookTreeModel,
value: string
value: string,
callback?: CreateNewCloudSketchCallback
): (
progress: Progress
) => Promise<CloudSketchbookTree.CloudSketchDirNode | undefined> {
Expand All @@ -143,6 +181,9 @@ export class NewCloudSketch extends CloudSketchContribution {
await treeModel.refresh();
progress.report({ message: pullingSketch(sketch.name) });
const node = await this.pull(sketch);
if (callback && node) {
await callback(sketch, node, progress);
}
return node;
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export class RenameCloudSketch extends CloudSketchContribution {
const toPosixPath = params.cloudUri.parent.resolve(value).path.toString();
// push
progress.report({ message: pushingSketch(params.sketch.name) });
await treeModel.sketchbookTree().push(node);
await treeModel.sketchbookTree().push(node, true);

// rename
progress.report({
Expand Down
Loading

0 comments on commit f840f7f

Please sign in to comment.