diff --git a/src/browser/app/app.component.ts b/src/browser/app/app.component.ts index 2c48d42f..9f666606 100644 --- a/src/browser/app/app.component.ts +++ b/src/browser/app/app.component.ts @@ -6,6 +6,7 @@ import { NoteFinderComponent } from '../note/note-collection'; import { NoteCollectionService } from '../note/note-collection/note-collection.service'; import { WORKSPACE_DATABASE, WorkspaceDatabase } from '../shared'; import { Themes, ThemeService } from '../ui/style'; +import { VcsManagerComponent } from '../vcs/vcs-view'; import { AppLayoutSidenavOutlet } from './app-layout'; import { AppStateWithFeatures } from './app.state'; @@ -25,6 +26,14 @@ export class AppComponent implements OnInit { description: 'Notes (⌘+1)', outletComponent: NoteFinderComponent, }, + { + id: 'gd-vcs-manager', + name: 'Version Control', + iconName: 'git', + shortcut: '', + description: 'Version Control (⌘+2)', + outletComponent: VcsManagerComponent, + }, ]; readonly noteContentLoaded: Observable = this.store.pipe( diff --git a/src/browser/app/app.module.ts b/src/browser/app/app.module.ts index 86f82253..c52bf07f 100644 --- a/src/browser/app/app.module.ts +++ b/src/browser/app/app.module.ts @@ -6,6 +6,7 @@ import { StoreModule } from '@ngrx/store'; import { StoreDevtoolsModule } from '@ngrx/store-devtools'; import { NoteModule } from '../note'; import { UiModule } from '../ui/ui.module'; +import { VcsModule } from '../vcs'; import { AppLayoutModule } from './app-layout'; import { AppComponent } from './app.component'; import { appReducer } from './app.reducer'; @@ -21,6 +22,7 @@ import { appReducer } from './app.reducer'; EffectsModule.forRoot([]), AppLayoutModule, NoteModule, + VcsModule, ], declarations: [AppComponent], bootstrap: [AppComponent], diff --git a/src/browser/vcs/dummies.ts b/src/browser/vcs/dummies.ts new file mode 100644 index 00000000..2c41e154 --- /dev/null +++ b/src/browser/vcs/dummies.ts @@ -0,0 +1,39 @@ +import * as path from 'path'; +import { Dummy, TextDummy, TypesDummy } from '../../../test/helpers'; +import { VcsFileChange, VcsFileChangeStatusTypes } from '../../core/vcs'; + + +export class VcsFileChangeDummy implements Dummy { + private filePath = new TextDummy('file'); + private status = new TypesDummy([ + VcsFileChangeStatusTypes.REMOVED, + VcsFileChangeStatusTypes.MODIFIED, + VcsFileChangeStatusTypes.RENAMED, + VcsFileChangeStatusTypes.NEW, + ]); + + constructor(public readonly workspaceDir: string = '/test/workspace') { + } + + create(status = this.status.create()): VcsFileChange { + const filePath = this.filePath.create(); + let fileChange = { + filePath, + workingDirectoryPath: this.workspaceDir, + absoluteFilePath: path.resolve(this.workspaceDir, filePath), + status, + } as VcsFileChange; + + if (status === VcsFileChangeStatusTypes.RENAMED) { + fileChange = { + ...fileChange, + headToIndexDiff: { + oldFilePath: 'old-file', + newFilePath: filePath, + }, + }; + } + + return fileChange; + } +} diff --git a/src/browser/vcs/index.ts b/src/browser/vcs/index.ts index d28699d7..2410afec 100644 --- a/src/browser/vcs/index.ts +++ b/src/browser/vcs/index.ts @@ -1,3 +1,6 @@ export * from './vcs.module'; export * from './vcs.service'; export * from './vcs-authentication-database'; +export * from './vcs.state'; +export * from './vcs.actions'; +export * from './vcs.reducer'; diff --git a/src/browser/vcs/vcs-view/index.ts b/src/browser/vcs/vcs-view/index.ts new file mode 100644 index 00000000..f3f482b4 --- /dev/null +++ b/src/browser/vcs/vcs-view/index.ts @@ -0,0 +1,2 @@ +export * from './vcs-view.module'; +export * from './vcs-manager/vcs-manager.component'; diff --git a/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.html b/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.html new file mode 100644 index 00000000..2b8b8036 --- /dev/null +++ b/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.html @@ -0,0 +1,2 @@ +
+
diff --git a/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.scss b/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.scss new file mode 100644 index 00000000..f08112b1 --- /dev/null +++ b/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.scss @@ -0,0 +1,2 @@ +.VcsManager { +} diff --git a/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.ts b/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.ts new file mode 100644 index 00000000..55f6fc81 --- /dev/null +++ b/src/browser/vcs/vcs-view/vcs-manager/vcs-manager.component.ts @@ -0,0 +1,17 @@ +import { Component, OnInit } from '@angular/core'; + + +@Component({ + selector: 'gd-vcs-manager', + templateUrl: './vcs-manager.component.html', + styleUrls: ['./vcs-manager.component.scss'], +}) +export class VcsManagerComponent implements OnInit { + + constructor() { + } + + ngOnInit() { + } + +} diff --git a/src/browser/vcs/vcs-view/vcs-view.module.ts b/src/browser/vcs/vcs-view/vcs-view.module.ts new file mode 100644 index 00000000..92eba8df --- /dev/null +++ b/src/browser/vcs/vcs-view/vcs-view.module.ts @@ -0,0 +1,21 @@ +import { NgModule } from '@angular/core'; +import { UiModule } from '../../ui/ui.module'; +import { VcsManagerComponent } from './vcs-manager/vcs-manager.component'; + + +@NgModule({ + imports: [ + UiModule, + ], + declarations: [ + VcsManagerComponent, + ], + entryComponents: [ + VcsManagerComponent, + ], + exports: [ + VcsManagerComponent, + ], +}) +export class VcsViewModule { +} diff --git a/src/browser/vcs/vcs.actions.ts b/src/browser/vcs/vcs.actions.ts new file mode 100644 index 00000000..5747342c --- /dev/null +++ b/src/browser/vcs/vcs.actions.ts @@ -0,0 +1,19 @@ +import { Action } from '@ngrx/store'; +import { VcsFileChange } from '../../core/vcs'; + + +export enum VcsActionTypes { + UPDATE_FILE_CHANGES = '[Vcs] Update file changes', +} + + +export class UpdateFileChangesAction implements Action { + readonly type = VcsActionTypes.UPDATE_FILE_CHANGES; + + constructor(public readonly payload: { fileChanges: VcsFileChange[] }) { + } +} + + +export type VcsAction = + UpdateFileChangesAction; diff --git a/src/browser/vcs/vcs.module.ts b/src/browser/vcs/vcs.module.ts index 07a4c24e..7b83bc78 100644 --- a/src/browser/vcs/vcs.module.ts +++ b/src/browser/vcs/vcs.module.ts @@ -1,12 +1,17 @@ import { NgModule } from '@angular/core'; +import { StoreModule } from '@ngrx/store'; import { VcsAuthenticationDatabaseProvider } from './vcs-authentication-database'; import { VcsRemoteModule } from './vcs-remote'; +import { VcsViewModule } from './vcs-view'; +import { vcsReducerMap } from './vcs.reducer'; import { VcsService } from './vcs.service'; @NgModule({ imports: [ VcsRemoteModule, + VcsViewModule, + StoreModule.forFeature('vcs', vcsReducerMap), ], providers: [ VcsAuthenticationDatabaseProvider, @@ -14,6 +19,7 @@ import { VcsService } from './vcs.service'; ], exports: [ VcsRemoteModule, + VcsViewModule, ], }) export class VcsModule { diff --git a/src/browser/vcs/vcs.reducer.spec.ts b/src/browser/vcs/vcs.reducer.spec.ts new file mode 100644 index 00000000..4395bd6b --- /dev/null +++ b/src/browser/vcs/vcs.reducer.spec.ts @@ -0,0 +1,20 @@ +import { createDummies } from '../../../test/helpers'; +import { VcsFileChangeDummy } from './dummies'; +import { UpdateFileChangesAction } from './vcs.actions'; +import { vcsReducer } from './vcs.reducer'; + + +describe('browser.vcs.vcsReducer', () => { + const fileChangeDummy = new VcsFileChangeDummy(); + + describe('UPDATE_FILE_CHANGES', () => { + it('should set file changes.', () => { + const fileChanges = createDummies(fileChangeDummy, 10); + const action = new UpdateFileChangesAction({ fileChanges }); + + const result = vcsReducer(undefined, action); + + expect(result.fileChanges).toEqual(fileChanges); + }); + }); +}); diff --git a/src/browser/vcs/vcs.reducer.ts b/src/browser/vcs/vcs.reducer.ts new file mode 100644 index 00000000..873e419d --- /dev/null +++ b/src/browser/vcs/vcs.reducer.ts @@ -0,0 +1,24 @@ +import { ActionReducerMap } from '@ngrx/store'; +import { VcsAction, VcsActionTypes } from './vcs.actions'; +import { createVcsInitialState, VcsState, VcsStateWithRoot } from './vcs.state'; + + +export function vcsReducer( + state: VcsState = createVcsInitialState(), + action: VcsAction, +): VcsState { + switch (action.type) { + case VcsActionTypes.UPDATE_FILE_CHANGES: + return { + ...state, + fileChanges: [...action.payload.fileChanges], + }; + default: + return state; + } +} + + +export const vcsReducerMap: ActionReducerMap = { + vcs: vcsReducer, +}; diff --git a/src/browser/vcs/vcs.state.ts b/src/browser/vcs/vcs.state.ts new file mode 100644 index 00000000..b36b2d56 --- /dev/null +++ b/src/browser/vcs/vcs.state.ts @@ -0,0 +1,18 @@ +import { VcsFileChange } from '../../core/vcs'; + + +export function createVcsInitialState(): VcsState { + return { + fileChanges: [], + }; +} + + +export interface VcsState { + readonly fileChanges: VcsFileChange[]; +} + + +export interface VcsStateWithRoot { + vcs: VcsState; +}