From fa326660476b98658d464975b2f3d32993a3cba2 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 25 May 2018 14:16:50 -0700 Subject: [PATCH] feat(project): rename unlink dialog to change link, allow unlinking (fixes #118) --- package.json | 2 +- .../link-dialog/link-dialog.component.html | 2 +- .../link-dialog/link-dialog.component.ts | 12 ++++++++++-- .../project-dropdown.component.html | 2 +- src/app/editor/project/project.actions.ts | 13 ++++++++++++- src/app/editor/project/project.effects.ts | 15 +++++++++++++++ src/app/editor/project/project.reducer.ts | 8 ++++++++ src/server/datastore.ts | 18 +++++++++++++++--- src/server/electron-server.ts | 7 +++++++ src/server/project-linker.ts | 7 +++++++ src/server/util.ts | 7 +++++++ test/datastore.test.ts | 4 ++++ 12 files changed, 88 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 59f813b..b71c93e 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "@angular/material": "^5.2.4", "@angular/platform-browser": "^5.2.8", "@angular/platform-browser-dynamic": "^5.2.8", - "@mixer/cdk-std": "^0.2.13", + "@mixer/cdk-std": "^0.2.17", "@ngrx/effects": "^5.2.0", "@ngrx/entity": "^5.2.0", "@ngrx/store": "^5.2.0", diff --git a/src/app/editor/project/link-dialog/link-dialog.component.html b/src/app/editor/project/link-dialog/link-dialog.component.html index 91d3ebc..eb117d5 100644 --- a/src/app/editor/project/link-dialog/link-dialog.component.html +++ b/src/app/editor/project/link-dialog/link-dialog.component.html @@ -12,7 +12,7 @@

{{ game.name }}

Updated: {{ game.updatedAt | amTimeAgo }}

- + diff --git a/src/app/editor/project/link-dialog/link-dialog.component.ts b/src/app/editor/project/link-dialog/link-dialog.component.ts index 7e5ae62..19bdb57 100644 --- a/src/app/editor/project/link-dialog/link-dialog.component.ts +++ b/src/app/editor/project/link-dialog/link-dialog.component.ts @@ -3,7 +3,7 @@ import { MatDialogRef } from '@angular/material'; import { Store } from '@ngrx/store'; import * as fromRoot from '../../bedrock.reducers'; -import { IInteractiveGame, SetInteractiveGame } from '../project.actions'; +import { IInteractiveGame, SetInteractiveGame, UnlinkInteractiveGame } from '../project.actions'; import * as fromProject from '../project.reducer'; /** @@ -31,10 +31,18 @@ export class LinkProjectDialogComponent { ) {} /** - * Deletes a snapshot from the store. + * Unlink a game from the project. */ public link(project: IInteractiveGame) { this.store.dispatch(new SetInteractiveGame(project)); this.dialog.close(project); } + + /** + * Unlinks the linked game from the control. + */ + public unlink() { + this.store.dispatch(new UnlinkInteractiveGame()); + this.dialog.close(undefined); + } } diff --git a/src/app/editor/project/project-dropdown/project-dropdown.component.html b/src/app/editor/project/project-dropdown/project-dropdown.component.html index 5f028af..ba2d8ab 100644 --- a/src/app/editor/project/project-dropdown/project-dropdown.component.html +++ b/src/app/editor/project/project-dropdown/project-dropdown.component.html @@ -16,7 +16,7 @@ + text="Change Linked Game"> (ProjectActionTypes.UNSET_GAME_LINK) + .pipe( + withLatestDirectory(this.store), + switchMap(([, directory]) => + this.electron + .call(ProjectMethods.UnlinkGameFromControls, { directory }) + .catch(RpcError, () => undefined), + ), + ); + /** * Updates the name of the project package.json. */ diff --git a/src/app/editor/project/project.reducer.ts b/src/app/editor/project/project.reducer.ts index 8e04c3c..e2c38d1 100644 --- a/src/app/editor/project/project.reducer.ts +++ b/src/app/editor/project/project.reducer.ts @@ -34,6 +34,14 @@ export function projectReducer(state: IProjectState = {}, action: ProjectActions switch (action.type) { case ProjectActionTypes.SET_GAME_LINK: return { ...state, project: { ...state.project, interactiveGame: action.game } }; + case ProjectActionTypes.UNSET_GAME_LINK: + return { + ...state, + project: { + ...state.project, + interactiveGame: null, + }, + }; case ProjectActionTypes.SET_CONFIRM_SCHEMA: return { ...state, project: { ...state.project, confirmSchemaUpload: action.confirm } }; case ProjectActionTypes.RENAME_PROJECT: diff --git a/src/server/datastore.ts b/src/server/datastore.ts index f4260d7..6fa4a57 100644 --- a/src/server/datastore.ts +++ b/src/server/datastore.ts @@ -1,7 +1,7 @@ import { isPlainObject, merge } from 'lodash'; import * as os from 'os'; import * as path from 'path'; -import { appendFile, exists, mkdir, readFile, writeFile } from './util'; +import { appendFile, exists, mkdir, readFile, unlink, writeFile } from './util'; /** * IDataStore is a system-aware store for project files and settings. @@ -74,13 +74,25 @@ export class FileDataStore implements IDataStore { } public async saveGlobal(file: string, value: T): Promise { + const filePath = this.filePath(this.homedir, file); await this.ensureMiixFolderInDir(this.homedir, false); - await writeFile(this.filePath(this.homedir, file), JSON.stringify(value, null, 2)); + + if (value === undefined) { + await unlink(filePath); + } else { + await writeFile(filePath, JSON.stringify(value, null, 2)); + } } public async saveProject(file: string, projectPath: string, value: T): Promise { + const filePath = this.filePath(projectPath, file); await this.ensureMiixFolderInDir(projectPath, true); - await writeFile(this.filePath(projectPath, file), JSON.stringify(value, null, 2)); + + if (value === undefined) { + await unlink(filePath); + } else { + await writeFile(filePath, JSON.stringify(value, null, 2)); + } } private filePath(base: string, file: string) { diff --git a/src/server/electron-server.ts b/src/server/electron-server.ts index f8ac983..269d272 100644 --- a/src/server/electron-server.ts +++ b/src/server/electron-server.ts @@ -228,6 +228,13 @@ const methods: { [methodName: string]: (data: any, server: ElectronServer) => Pr return new ProjectLinker(new Project(options.directory)).linkGame(options.game); }, + /** + * Links the Interactive game to the set of controls. + */ + [forProject.ProjectMethods.UnlinkGameFromControls]: async (options: { directory: string }) => { + return new ProjectLinker(new Project(options.directory)).unlinkGame(); + }, + /** * Links the Interactive game to the set of controls. */ diff --git a/src/server/project-linker.ts b/src/server/project-linker.ts index 3b323af..6a368ca 100644 --- a/src/server/project-linker.ts +++ b/src/server/project-linker.ts @@ -51,6 +51,13 @@ export class ProjectLinker { }); } + /** + * Gets rid of any previous game link. + */ + public async unlinkGame() { + await this.project.saveSetting(storageKey, undefined); + } + /** * Returns the linked Interactive game if any. */ diff --git a/src/server/util.ts b/src/server/util.ts index 9a9e8e9..6624299 100644 --- a/src/server/util.ts +++ b/src/server/util.ts @@ -11,6 +11,13 @@ export async function readFile(file: string): Promise { return promiseCallback(callback => fs.readFile(file, 'utf8', callback)); } +/** + * Promisified fs.unlink + */ +export async function unlink(file: string): Promise { + return promiseCallback(callback => fs.unlink(file, callback)); +} + /** * Promisified fs.writeFile */ diff --git a/test/datastore.test.ts b/test/datastore.test.ts index 4750b64..a6d0883 100644 --- a/test/datastore.test.ts +++ b/test/datastore.test.ts @@ -42,6 +42,8 @@ describe('FileDataStore', () => { expect(await exists(path.join(homedir(), '.miix', 'foo.json'))).to.be.false; await store.saveGlobal('foo', true); expect(await readFile(path.join(homedir(), '.miix', 'foo.json'))).to.equal('true'); + await store.saveGlobal('foo', undefined); + expect(await exists(path.join(homedir(), '.miix', 'foo.json'))).to.be.false; }); it('saves project data correctly', async () => { @@ -50,6 +52,8 @@ describe('FileDataStore', () => { await store.saveProject('foo', projdir(), true); expect(await readFile(path.join(projdir(), '.miix', 'foo.json'))).to.equal('true'); expect(await readFile(path.join(projdir(), '.gitignore'))).to.equal('\n/.miix\n'); + await store.saveProject('foo', projdir(), undefined); + expect(await exists(path.join(projdir(), '.miix', 'foo.json'))).to.be.false; }); it('saves project data correctly', async () => {