-
-
{{ 'OPEN.REPO' | translate }}
-
-
-
-
{{ 'INIT.TAB_TITLE' | translate }}
-
-
-
-
{{ 'CLONE.TAB_TITLE' | translate }}
-
+
+
+
+
+
{{ 'OPEN.REPO' | translate }}
+
+
+
+
{{ 'INIT.TAB_TITLE' | translate }}
+
+
+
+
{{ 'CLONE.TAB_TITLE' | translate }}
+
+
-
+
+
+
+
+
-
- DIV TEST
-
-
@@ -187,13 +196,42 @@
-
-
- {{ 'LOG_TO_CONTINUE' | translate }}
-
-
-
-
+
+
+ {{ 'LOG_TO_CONTINUE' | translate }}
+ {{ 'WRONG_CRED' | translate }}
+
+
+
+
+
+
+
+
+ {{ 'LOG_TO_CONTINUE' | translate }}
+ {{ 'WRONG_CRED' | translate }}
+
+
+
+
+
+
+
+
+ {{ 'BRANCH.NEW_BRANCH_NAME_PROMPT' | translate }}
+
+
+
+
+
+
+
+ {{ 'LOG_TO_CONTINUE' | translate }}
+ {{ 'WRONG_CRED' | translate }}
+
+
+
+
@@ -202,3 +240,29 @@
+
+
+ {{ 'LOG_TO_CONTINUE' | translate }}
+ {{ 'WRONG_CRED' | translate }}
+
+
+
+
+
+
+
+
+ {{ 'BRANCH.A_LOCAL' | translate }} '{{ localBranch }}' {{ 'BRANCH.ALREADY_EXISTS' | translate }}.
+
+
+
+
+
+
+
+
+ {{ 'BRANCH.NAME' | translate }}
+
+
+
+
\ No newline at end of file
diff --git a/src/app/screens/home/home.component.scss b/src/app/screens/home/home.component.scss
index 48adc2a..cc51150 100644
--- a/src/app/screens/home/home.component.scss
+++ b/src/app/screens/home/home.component.scss
@@ -1,5 +1,9 @@
@import '../../../variables.scss';
+.color-red {
+ color: $light-red;
+}
+
.gh-button-bar {
@include wX-hX(100%, 50px);
diff --git a/src/app/screens/home/home.component.spec.ts b/src/app/screens/home/home.component.spec.ts
deleted file mode 100644
index b438fa7..0000000
--- a/src/app/screens/home/home.component.spec.ts
+++ /dev/null
@@ -1,469 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { HomeComponent } from './home.component';
-import { FormsModule } from '@angular/forms';
-import { TranslateService, TranslateModule, TranslateLoader } from '@ngx-translate/core';
-import { MockTranslateService } from '../../models/MockTranslateService';
-import { ElectronService } from '../../providers/electron.service';
-import { MockElectronService } from '../../models/MockElectronService';
-import { GitService } from '../../providers/git.service';
-import { MockGitService } from '../../models/MockGitService';
-import { ContainerComponent } from '../../components/container/container.component';
-import { InputComponent } from '../../components/input/input.component';
-import { ButtonComponent } from '../../components/button/button.component';
-import { ModalComponent } from '../../components/modal/modal.component';
-import { FooterComponent } from '../../components/footer/footer.component';
-import { IconButtonComponent } from '../../components/icon-button/icon-button.component';
-import { MatTabsModule, TooltipComponent } from '@angular/material';
-import { ResizableModule, ResizeEvent } from 'angular-resizable-element';
-import { LoaderComponent } from '../../components/loader/loader.component';
-import { RouterModule, Router } from '@angular/router';
-import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
-import { RouterTestingModule } from '@angular/router/testing';
-import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { ToastrService, ToastrModule } from 'ngx-toastr';
-import { ThemePreferencesService } from '../../providers/theme-preferences.service';
-import { MockThemePreferencesService } from '../../models/MockThemePreferencesService';
-import { MockTranslateLoader } from '../../models/MockTranslateLoader';
-import { InfoBarComponent } from '../../components/info-bar/info-bar.component';
-import { MockRouter } from '../../models/MockRouter';
-import { MockTerminalManagerService } from '../../models/MockTerminalManagerService';
-import { TerminalManagerService } from '../../providers/terminal-manager.service';
-
-describe('HomeComponent', () => {
- let component: HomeComponent;
- let fixture: ComponentFixture;
- let terminalService: TerminalManagerService;
- let originalTimeout;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [
- HomeComponent,
- ContainerComponent,
- InputComponent,
- ButtonComponent,
- ModalComponent,
- FooterComponent,
- IconButtonComponent,
- LoaderComponent,
- InfoBarComponent
- ],
- imports: [
- FormsModule,
- TranslateModule.forRoot({
- loader: {provide: TranslateLoader, useClass: MockTranslateLoader}
- }),
- MatTabsModule,
- ResizableModule,
- NgbModule,
- RouterTestingModule,
- BrowserAnimationsModule,
- ToastrModule.forRoot()
- ],
- providers: [
- {
- provide: Router,
- useClass: MockRouter
- },
- {
- provide: TranslateService,
- useClass: MockTranslateService
- },
- {
- provide: ThemePreferencesService,
- useClass: MockThemePreferencesService
- },
- {
- provide: ElectronService,
- useClass: MockElectronService
- },
- {
- provide: GitService,
- useClass: MockGitService
- },
- {
- provide: TerminalManagerService,
- useClass: MockTerminalManagerService
- },
- ToastrService
- ]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(HomeComponent);
- component = fixture.componentInstance;
- terminalService = TestBed.get(TerminalManagerService);
- });
-
- it('tests the component creation', () => {
- expect(component).toBeTruthy();
- });
-
- it('tests the pullButtonClicked function', () => {
- expect(component.pullButtonClicked()).toBeTruthy();
- });
-
- it('tests the pushButtonClicked function', () => {
- expect(component.pushButtonClicked()).toBeTruthy();
- });
-
- it('tests the branchButtonClicked function', () => {
- expect(component.branchButtonClicked()).toBeTruthy();
- });
-
- it('tests the openTerminal function with success', (done) => {
- const TerminalName = 'terminator';
- terminalService.terminalName = TerminalName;
- component.openTerminal().then((result) => {
- expect(result).toBeTruthy();
- done();
- });
- });
-
- it('tests the openTerminal function with success', (done) => {
- const TerminalName = 'not-a-terminal';
- terminalService.terminalName = TerminalName;
- component.openTerminal().then((result) => {
- expect(result).toBeFalsy();
- done();
- });
- });
-
- it('tests the openPreferences function', (done) => {
- component.openPreferences().then((result) => {
- expect(result).toBeTruthy();
- done();
- });
- });
-
- it('tests the openProjectModal function', () => {
- const TabSelectedIndex = 0;
- component.openProjectModal(TabSelectedIndex);
- expect(component.projectModalTabSelectedIndex).toBe(TabSelectedIndex);
- expect(component.projectModalVisible).toBeTruthy();
- });
-
- it('tests the displaySearchInputValue function', () => {
- const RepoName = '/repo';
- component.repoName = RepoName;
- expect(component.displaySearchInputValue()).toBeTruthy();
- });
-
- it('tests the validate function with bad status', () => {
- const TestEvent: ResizeEvent =
- {
- edges:
- {
- right: 2
- },
- rectangle:
- {
- top: 50,
- bottom: 750,
- left: 0,
- right: 220,
- height: 700,
- width: 5
- }
- };
- component.dimensions = 20;
- expect(component.validate(TestEvent)).toBeFalsy();
- });
-
- it('tests the validate function with good status', () => {
- const TestEvent: ResizeEvent =
- {
- edges:
- {
- right: 2
- },
- rectangle:
- {
- top: 50,
- bottom: 750,
- left: 0,
- right: 220,
- height: 700,
- width: 220
- }
- };
- component.dimensions = 20;
- expect(component.validate(TestEvent)).toBeTruthy();
- });
-
- it('tests the onResizeEnd function', () => {
- const TestEvent: ResizeEvent =
- {
- edges:
- {
- right: 2
- },
- rectangle:
- {
- top: 50,
- bottom: 800,
- left: 0,
- right: 300,
- height: 500,
- width: 220
- }
- };
- component.onResizeEnd(TestEvent);
- expect(component.style).not.toBeUndefined();
- });
-
- it('tests the updateFullPath function for init with all fields', () => {
- const Path = '/new';
- const RepoName = '/repo';
- component.initLocation = Path;
- component.initName = RepoName;
- component.updateFullPath();
- expect(component.fullPath).toBe('/new/repo');
- });
-
- it('tests the updateFullPath for init without location', () => {
- const Path = '';
- const RepoName = '/repo';
- component.initLocation = Path;
- component.initName = RepoName;
- component.updateFullPath();
- expect(component.fullPath).toBe('');
- });
-
- it('tests the initBrowse function', () => {
- component.initBrowse();
- expect(component.initLocation).toBe('/new');
- });
-
- it('tests the initSubmit function with valid path', (done) => {
- const OldPath = '/old';
- const NewPath = '/new';
- const RepoName = '/repo';
- const BoolModal = true;
- component.initLocation = NewPath;
- component.initName = RepoName;
- component.projectModalVisible = BoolModal;
- component.projectModalLoading = BoolModal;
- component.path = OldPath;
- component.initSubmit().then(() => {
- expect(component.projectModalVisible).toBeFalsy();
- expect(component.projectModalLoading).toBeFalsy();
- expect(component.initLocation).toBe('');
- expect(component.initName).toBe('');
- expect(component.fullPath).toBe('');
- expect(component.path).toBe(NewPath);
- done();
- });
- });
-
- it('tests the openBrowse function', () => {
- const Path = '/new';
- component.openBrowse();
- expect(component.openFolder).toBe(Path);
- });
-
- it('tests the openRepo function with changed and valid path', (done) => {
- const OldPath = '/old';
- const NewPath = '/new';
- const ProjectModalBoolean = true;
- component.path = OldPath;
- component.openFolder = NewPath;
- component.projectModalLoading = ProjectModalBoolean;
- component.projectModalVisible = ProjectModalBoolean;
- component.openRepo().then(() => {
- expect(component.openFolder).toBe('');
- expect(component.projectModalLoading).toBeFalsy();
- expect(component.projectModalVisible).toBeFalsy();
- done();
- });
- });
-
- it('tests the openRepo function with changed and invalid path', (done) => {
- const OldPath = '/old';
- const NewPath = '/invalid';
- const ProjectModalBoolean = true;
- component.path = OldPath;
- component.openFolder = NewPath;
- component.projectModalLoading = ProjectModalBoolean;
- component.projectModalVisible = ProjectModalBoolean;
- component.openRepo().then(() => {
- expect(component.openFolder).toBe('');
- expect(component.projectModalLoading).toBeFalsy();
- expect(component.projectModalVisible).toBeTruthy();
- done();
- });
- });
-
- it('tests the initSubmit function with invalid path', (done) => {
- const OldPath = '/old';
- const NewPath = '/invalidpath';
- const RepoName = '/repo';
- const BoolModal = true;
- component.initLocation = NewPath;
- component.initName = RepoName;
- const FullPath = component.fullPath;
- component.projectModalVisible = BoolModal;
- component.projectModalLoading = BoolModal;
- component.path = OldPath;
- component.initSubmit().then(() => {
- expect(component.projectModalVisible).toBeTruthy();
- expect(component.projectModalLoading).toBeFalsy();
- expect(component.initLocation).toBe(NewPath);
- expect(component.initName).toBe(RepoName);
- expect(component.fullPath).toBe(FullPath);
- expect(component.path).toBe(OldPath);
- done();
- });
- });
-
- it('tests the openRepo function with unchanged path', (done) => {
- const OldPath = '/old';
- const ProjectModalBoolean = true;
- component.path = OldPath;
- component.openFolder = OldPath;
- component.projectModalVisible = ProjectModalBoolean;
- component.openRepo().then((result) => {
- expect(component.projectModalVisible).toBeTruthy();
- expect(result).toBeFalsy();
- done();
- });
- });
-
- it('tests the openRecentRepo function', (done) => {
- const OldPath = '/old';
- const NewPath = '/new';
- component.path = OldPath;
- component.openRecentRepo(NewPath).then(() => {
- expect(component.openFolder).toBe('');
- expect(component.projectModalLoading).toBeFalsy();
- expect(component.projectModalVisible).toBeFalsy();
- done();
- });
- });
-
- it('tests the closeRepo function', () => {
- const Path = '/new';
- const Repo = 'new';
- component.path = Path;
- component.repoName = Repo;
- component.closeRepo();
- expect(component.path).toBeUndefined();
- expect(component.repoName).toBeUndefined();
- });
-
- it('tests the cloneBrowse function', () => {
- component.cloneBrowse();
- expect(component.cloneFolder).toBe('/new');
- });
-
- it('tests the cloneSubmit function with https', () => {
- const CloneFolder = 'path';
- const CloneUrl = 'https://github.com/GitHarpon/git-harpon';
- component.cloneFolder = CloneFolder;
- component.cloneUrl = CloneUrl;
- component.cloneSubmit();
- expect(component.projectModalVisible).toBeFalsy();
- expect(component.credInfoBarVisible).toBeTruthy();
- });
-
- it('tests the cloneSubmit function with ssh', () => {
- const CloneFolder = 'path';
- const CloneUrl = 'git@github.com:GitHarpon/git-harpon.git';
- component.cloneFolder = CloneFolder;
- component.cloneUrl = CloneUrl;
- component.cloneSubmit();
- });
-
- it('tests the cloneSubmit function with invalid url', () => {
- const CloneFolder = 'path';
- const CloneUrl = 'NotAnUrl';
- component.cloneFolder = CloneFolder;
- component.cloneUrl = CloneUrl;
- component.cloneSubmit();
- });
-
- it('tests the cloneSubmit function with invalid folder', () => {
- const CloneFolder = 'invalid';
- const CloneUrl = 'https://github.com/GitHarpon/git-harpon';
- component.cloneFolder = CloneFolder;
- component.cloneUrl = CloneUrl;
- component.cloneSubmit();
- });
-
- it('tests the cloneHttps function with valid arguments', (done) => {
- const CloneUrl = 'https://github.com/GitHarpon/git-harpon';
- const CloneFolder = 'path';
- const Username = 'username';
- const Password = 'password';
- component.cloneUrl = CloneUrl;
- component.cloneFolder = CloneFolder;
- component.username = Username;
- component.password = Password;
- component.openClonedInfoBarVisible = false;
- component.cloneHttps().then(() => {
- expect(component.homeLoading).toBeFalsy();
- expect(component.openClonedInfoBarVisible).toBeTruthy();
- done();
- });
- });
-
- it('tests the cloneHttps function with invalid url or folder', (done) => {
- const CloneUrl = 'invalidurl';
- const CloneFolder = 'invalidfolder';
- const Username = 'username';
- const Password = 'password';
- component.cloneUrl = CloneUrl;
- component.cloneFolder = CloneFolder;
- component.username = Username;
- component.password = Password;
- component.cloneHttps().then(() => {
- expect(component.homeLoading).toBeFalsy();
- done();
- });
- });
-
- it('tests the cloneHttps function with invalid username or password', (done) => {
- const CloneUrl = 'https://github.com/GitHarpon/git-harpon';
- const CloneFolder = 'path';
- const Username = 'badusername';
- const Password = 'badpassword';
- component.cloneUrl = CloneUrl;
- component.cloneFolder = CloneFolder;
- component.username = Username;
- component.password = Password;
- component.cloneHttps().then(() => {
- expect(component.homeLoading).toBeFalsy();
- done();
- });
- });
-
- it('tests the closeCredInfoBar function', () => {
- component.closeCredInfoBar();
- expect(component.credInfoBarVisible).toBeFalsy();
- });
-
- it('tests the openClonedRepo function', () => {
- const NewRepo = '/new';
- component.newClonedRepoPath = NewRepo;
- component.openClonedRepo();
- expect(component.path).toBe(NewRepo);
- expect(component.openClonedInfoBarVisible).toBeFalsy();
- });
-
- it('tests the closeClonedInfoBar function', () => {
- component.closeClonedInfoBar();
- expect(component.openClonedInfoBarVisible).toBeFalsy();
- });
-
- it('tests the resetCloneInputs function', () => {
- component.resetCloneInputs();
- expect(component.username).toBe('');
- expect(component.password).toBe('');
- expect(component.cloneUrl).toBe('');
- expect(component.cloneFolder).toBe('');
- expect(component.newClonedRepoPath).toBe('');
- });
-});
diff --git a/src/app/screens/home/home.component.ts b/src/app/screens/home/home.component.ts
index 16847b1..855778a 100644
--- a/src/app/screens/home/home.component.ts
+++ b/src/app/screens/home/home.component.ts
@@ -1,16 +1,18 @@
-import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Component, OnInit, OnDestroy, HostListener } from '@angular/core';
import { Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
-import { ResizeEvent } from 'angular-resizable-element';
import { GitService } from '../../providers/git.service';
import { ElectronService } from '../../providers/electron.service';
-import { initNgModule } from '@angular/core/src/view/ng_module';
-import { Subscription } from 'rxjs';
-import { ServiceResult } from '../../models/ServiceResult';
+import { Subscription, Subject } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import * as GitUrlParse from 'git-url-parse';
import { TerminalManagerService } from '../../providers/terminal-manager.service';
import { ThemePreferencesService } from '../../providers/theme-preferences.service';
+import { HttpsUser } from '../../models/HttpsUser';
+import { LeftPanelService } from '../../providers/left-panel.service';
+import { NewBranchCouple } from '../../models/NewBranchCouple';
+import { RightPanelService } from '../../providers/right-panel.service';
+import { GraphService } from '../../providers/graph.service';
@Component({
selector: 'app-home',
@@ -35,20 +37,49 @@ export class HomeComponent implements OnDestroy {
repoNameSubscription: Subscription;
recentProject: any[];
recentProjectSubscription: Subscription;
+ cloneCredInfoBarVisible: boolean;
+ pushCredInfoBarVisible: boolean;
+ branchName: any;
+ branchNameSubscription: Subscription;
+ newBranchInfoBarVisible: boolean;
+ newBranchName: string;
+ referenceBranchName: string;
+ newBranchCouple: NewBranchCouple;
+ newBranchNameForRenaming: string;
credInfoBarVisible: boolean;
openClonedInfoBarVisible: boolean;
+ checkoutInfoBarVisible: boolean;
newClonedRepoPath: string;
- username: string;
- password: string;
+ cloneHttpsUser: HttpsUser;
+
+ pullrebaseInfoBarVisible: boolean;
+ pullrebaseAuthErrored: boolean;
+ pullrebaseCredInfoBarVisible: boolean;
+ pullrebaseHttpsUser: HttpsUser;
+
homeLoading: boolean;
openFolder: string;
themePrefSubscription: Subscription;
currentTheme: string;
+ mainPanelVisible: boolean;
+ leftPanelVisible: boolean;
+ graphVisible: boolean;
+ rightPanelVisible: boolean;
+ cloneAuthErrored: boolean;
+ pushAuthErrored: boolean;
+ currentHttpsUserSubscription: Subscription;
+ currentHttpsUser: HttpsUser;
+ localBranch: string;
+ remoteBranch: string;
+ newCheckedoutBranchName: string;
constructor(public router: Router, private toastr: ToastrService,
private electronService: ElectronService, private gitService: GitService,
private translateService: TranslateService, private terminalService: TerminalManagerService,
- private themePrefService: ThemePreferencesService) {
+ private themePrefService: ThemePreferencesService, private leftPanelService: LeftPanelService,
+ private rightPanelService: RightPanelService, private graphService: GraphService) {
+
+ this.newBranchCouple = new NewBranchCouple();
this.pathSubscription = this.gitService.pathSubject.subscribe(
(path: any) => {
this.path = path;
@@ -58,6 +89,7 @@ export class HomeComponent implements OnDestroy {
this.repoNameSubscription = this.gitService.repoNameSubject.subscribe(
(repoName: any) => {
this.repoName = repoName;
+ this.openHomeView();
});
this.gitService.emitRepoNameSubject();
@@ -67,6 +99,12 @@ export class HomeComponent implements OnDestroy {
});
this.gitService.emitRecentProjectSubject();
+ this.branchNameSubscription = this.gitService.branchNameSubject.subscribe(
+ (branchName: any) => {
+ this.branchName = branchName;
+ });
+ this.gitService.emitBranchNameSubject();
+
this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe(
(newTheme: string) => {
this.currentTheme = newTheme;
@@ -74,21 +112,76 @@ export class HomeComponent implements OnDestroy {
);
this.themePrefService.emitThemePreferencesSubject();
+ this.currentHttpsUserSubscription = this.gitService.httpsUserSubject.subscribe(
+ (newUser: HttpsUser) => {
+ this.currentHttpsUser = newUser;
+ }
+ );
+ this.gitService.emitHttpsUserSubject();
+
this.dimensions = 20;
+
+ this.cloneHttpsUser = {
+ username: '',
+ password: ''
+ };
+
+ this.pullrebaseHttpsUser = {
+ username: '',
+ password: ''
+ };
}
- pullButtonClicked() {
+ @HostListener('window:focus', ['$event'])
+ onFocus() {
+ this.gitService.updateFilesDiff();
+ this.leftPanelService.setLocalBranches();
+ this.leftPanelService.setRemoteBranches();
return true;
}
+ async pullrebaseHttps() {
+ this.homeLoading = true;
+ return this.gitService.pullrebaseHttps(this.pullrebaseHttpsUser, this.branchName)
+ .then((data) => {
+ this.homeLoading = false;
+ this.pullrebaseCredInfoBarVisible = false;
+ this.toastr.info(data.message, data.title);
+ this.resetPullrebaseInputs();
+ })
+ .catch((data) => {
+ if (data.newData) {
+ this.pullrebaseAuthErrored = this.pullrebaseCredInfoBarVisible;
+ this.pullrebaseHttpsUser.password = '';
+ this.pullrebaseCredInfoBarVisible = true;
+ } else {
+ this.homeLoading = false;
+ this.resetPullrebaseInputs();
+ this.toastr.error(data.message, data.title);
+ }
+ });
+ }
+
pushButtonClicked() {
+ this.pushCredInfoBarVisible = true;
return true;
}
- branchButtonClicked() {
+ pullButtonClicked() {
+ this.pullrebaseCredInfoBarVisible = true;
return true;
}
+ branchButtonClicked() {
+ this.referenceBranchName = this.branchName;
+ this.newBranchInfoBarVisible = true;
+ }
+
+ openCreateBranchInfoBar(refBranchName) {
+ this.referenceBranchName = refBranchName;
+ this.newBranchInfoBarVisible = true;
+ }
+
async openTerminal() {
return this.terminalService.openTerminal()
.then(() => {
@@ -115,22 +208,7 @@ export class HomeComponent implements OnDestroy {
if (this.repoName) {
return true;
}
- }
-
- validate(event: ResizeEvent): boolean {
- if (
- event.rectangle.width &&
- (event.rectangle.width < this.dimensions)
- ) {
- return false;
- }
- return true;
- }
-
- onResizeEnd(event: ResizeEvent): void {
- this.style = {
- width: `${event.rectangle.width}px`
- };
+ return false;
}
cloneBrowse() {
@@ -145,7 +223,8 @@ export class HomeComponent implements OnDestroy {
var Url = GitUrlParse(this.cloneUrl);
if (Url.protocol === 'https') {
this.projectModalVisible = false;
- this.credInfoBarVisible = true;
+ this.homeLoading = true;
+ this.cloneHttps();
} else if (Url.protocol === 'ssh') {
this.toastr.error('Pas de ssh pour le moment', 'Erreur');
} else {
@@ -158,10 +237,8 @@ export class HomeComponent implements OnDestroy {
}
}
- cloneHttps() {
- this.credInfoBarVisible = false;
- this.homeLoading = true;
- return this.gitService.cloneHttps(GitUrlParse(this.cloneUrl), this.cloneFolder, this.username, this.password)
+ async cloneHttps() {
+ return this.gitService.cloneHttps(GitUrlParse(this.cloneUrl), this.cloneFolder, this.cloneHttpsUser)
.then((data) => {
this.homeLoading = false;
this.openClonedInfoBarVisible = true;
@@ -169,9 +246,16 @@ export class HomeComponent implements OnDestroy {
this.toastr.info(data.message, data.title);
})
.catch((data) => {
- this.homeLoading = false;
- this.resetCloneInputs();
- this.toastr.error(data.message, data.title);
+ if (data.newData) {
+ this.cloneAuthErrored = this.cloneCredInfoBarVisible;
+ this.cloneHttpsUser.password = '';
+ this.cloneCredInfoBarVisible = true;
+ } else {
+ this.projectModalLoading = false;
+ this.homeLoading = false;
+ this.resetCloneInputs();
+ this.toastr.error(data.message, data.title);
+ }
});
}
@@ -183,6 +267,29 @@ export class HomeComponent implements OnDestroy {
this.updateFullPath();
}
+
+ async pushHttps() {
+ this.homeLoading = true;
+ return this.gitService.pushHttps(this.fullPath, this.currentHttpsUser, this.branchName)
+ .then((data) => {
+ this.homeLoading = false;
+ this.pushCredInfoBarVisible = false;
+ this.toastr.info(data.message, data.title);
+ })
+ .catch((data) => {
+ if (data.newData) {
+ this.pushAuthErrored = this.pushCredInfoBarVisible;
+ this.currentHttpsUser.password = '';
+ this.pushCredInfoBarVisible = true;
+ this.homeLoading = false;
+ } else {
+ this.homeLoading = false;
+ this.resetPushInputs();
+ this.toastr.error(data.message, data.title);
+ }
+ });
+ }
+
updateFullPath() {
if (this.initLocation) {
this.fullPath = this.initLocation;
@@ -208,6 +315,7 @@ export class HomeComponent implements OnDestroy {
this.initName = '';
this.initLocation = '';
this.fullPath = '';
+ this.openHomeView();
})
.catch((result) => {
this.toastr.error(result.message, result.title, {
@@ -233,7 +341,9 @@ export class HomeComponent implements OnDestroy {
this.projectModalLoading = false;
this.projectModalVisible = false;
this.openFolder = '';
+ this.openHomeView();
this.toastr.info(data.message, data.title);
+ this.gitService.setHttpsUser({ username: null, password: null });
})
.catch((data) => {
this.projectModalLoading = false;
@@ -248,27 +358,88 @@ export class HomeComponent implements OnDestroy {
}
}
- closeCredInfoBar() {
- this.credInfoBarVisible = false;
+ closeCloneCredInfoBar() {
+ this.cloneCredInfoBarVisible = false;
this.resetCloneInputs();
}
+ async renameBranch() {
+ var TmpNewBr = new NewBranchCouple();
+ TmpNewBr.oldBranch = this.newBranchCouple.oldBranch;
+ TmpNewBr.newBranch = this.newBranchNameForRenaming;
+ this.newBranchCouple = TmpNewBr;
+ if (this.newBranchCouple.newBranch != '' && this.newBranchCouple.oldBranch != '') {
+ return this.gitService.renameBranch(this.newBranchCouple.oldBranch, this.newBranchCouple.newBranch)
+ .then((data) => {
+ this.closeRenameBar();
+ this.toastr.info(data.message, data.title);
+ })
+ .catch((data) => {
+ this.closeRenameBar();
+ });
+ }
+ }
+
+ closeRenameBar() {
+ this.newBranchCouple = new NewBranchCouple();
+ this.newBranchNameForRenaming = '';
+ this.gitService.getLocalBranches().then(() => {
+ this.leftPanelService.setLocalBranches();
+ });
+ }
+
+ closePushCredInfoBar() {
+ this.pushCredInfoBarVisible = false;
+ this.resetPushInputs();
+ }
openClonedRepo() {
+ this.gitService.setHttpsUser(this.cloneHttpsUser);
this.gitService.setPath(this.newClonedRepoPath);
this.closeClonedInfoBar();
+ this.openHomeView();
}
closeClonedInfoBar() {
this.openClonedInfoBarVisible = false;
this.resetCloneInputs();
+ this.resetPullrebaseInputs();
}
resetCloneInputs() {
- this.username = '';
- this.password = '';
+ this.cloneHttpsUser = {
+ username: '',
+ password: ''
+ };
this.cloneUrl = '';
this.cloneFolder = '';
this.newClonedRepoPath = '';
+ this.cloneAuthErrored = false;
+ this.cloneCredInfoBarVisible = false;
+ this.homeLoading = false;
+ }
+
+ resetPushInputs() {
+ this.currentHttpsUser = {
+ username: '',
+ password: ''
+ };
+ this.pushCredInfoBarVisible = false;
+ this.homeLoading = false;
+ }
+
+ closePullrebaseCredInfoBar() {
+ this.pullrebaseCredInfoBarVisible = false;
+ this.resetPullrebaseInputs();
+ }
+
+ resetPullrebaseInputs() {
+ this.pullrebaseHttpsUser = {
+ username: '',
+ password: ''
+ };
+ this.pullrebaseAuthErrored = false;
+ this.pullrebaseCredInfoBarVisible = false;
+ this.homeLoading = false;
}
async openRecentRepo(recentPath: string) {
@@ -279,11 +450,103 @@ export class HomeComponent implements OnDestroy {
closeRepo() {
this.path = undefined;
this.repoName = undefined;
+ this.branchName = undefined;
+ this.closeHomeView();
+ }
+
+ openHomeView() {
+ if (this.repoName) {
+ this.mainPanelVisible = false;
+ this.leftPanelVisible = true;
+ this.graphVisible = true;
+ this.rightPanelVisible = true;
+ this.leftPanelService.setLocalBranches();
+ this.leftPanelService.setRemoteBranches();
+ this.rightPanelService.setView(true);
+ this.graphService.setGraph();
+ } else {
+ this.mainPanelVisible = true;
+ }
+ }
+
+ closeHomeView() {
+ this.mainPanelVisible = true;
+ this.leftPanelVisible = false;
+ this.graphVisible = false;
+ this.rightPanelVisible = false;
+ this.rightPanelService.setCommitHash('');
+ }
+
+ openCheckoutInfoBar(remoteBranch) {
+ this.remoteBranch = remoteBranch;
+ this.localBranch = remoteBranch.split('/')[1];
+ this.checkoutInfoBarVisible = true;
+ }
+
+ createBranchHere() {
+ return this.gitService.createBranchHere(this.newCheckedoutBranchName, this.remoteBranch).then((data) => {
+ this.leftPanelService.setLocalBranches();
+ this.leftPanelService.setRemoteBranches();
+ this.closeCheckoutInfoBar();
+ this.toastr.info(data.message, data.title);
+ })
+ .catch((data) => {
+ this.closeCheckoutInfoBar();
+ this.toastr.error(data.message, data.title);
+ });
+ }
+
+ resetLocalHere() {
+ return this.gitService.resetLocalHere(this.remoteBranch).then((data) => {
+ this.leftPanelService.setLocalBranches();
+ this.leftPanelService.setRemoteBranches();
+ this.closeCheckoutInfoBar();
+ this.toastr.info(data.message, data.title);
+ })
+ .catch((data) => {
+ this.closeCheckoutInfoBar();
+ this.toastr.error(data.message, data.title);
+ });
+
+ }
+
+ closeCheckoutInfoBar() {
+ this.leftPanelService.setLoadingVisible(false);
+ this.remoteBranch = '';
+ this.newCheckedoutBranchName = '';
+ this.checkoutInfoBarVisible = false;
+ }
+
+ async createBranch() {
+ this.homeLoading = true;
+ return this.gitService.setNewBranch(this.newBranchName, this.referenceBranchName)
+ .then((data) => {
+ this.leftPanelService.setLocalBranches();
+ this.leftPanelService.setRemoteBranches();
+ this.newBranchInfoBarVisible = false;
+ this.homeLoading = false;
+ this.referenceBranchName = '';
+ this.newBranchName = '';
+ this.toastr.info(data.message, data.title);
+ })
+ .catch((data) => {
+ this.newBranchInfoBarVisible = true;
+ this.homeLoading = false;
+ this.toastr.error(data.message, data.title);
+ });
+ }
+
+ closeNewBranchInfoBar() {
+ this.referenceBranchName = '';
+ this.newBranchName = '';
+ this.newBranchInfoBarVisible = false;
}
ngOnDestroy() {
this.pathSubscription.unsubscribe();
this.repoNameSubscription.unsubscribe();
this.recentProjectSubscription.unsubscribe();
+ this.branchNameSubscription.unsubscribe();
+ this.currentHttpsUserSubscription.unsubscribe();
}
}
diff --git a/src/app/screens/left-panel/left-panel.component.html b/src/app/screens/left-panel/left-panel.component.html
new file mode 100644
index 0000000..25bb5ed
--- /dev/null
+++ b/src/app/screens/left-panel/left-panel.component.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+ {{ localBranch }}
+
+
+
+
+
{{ remote }}
+
+
+ {{ remoteBranch }}
+
+
+ {{ 'BRANCH.CREATE_HERE' | translate }}
+
+
+
+
+
+
+
+
+ {{ item }}
+ {{ 'BRANCH.CREATE_HERE' | translate }}
+
+
+ {{ 'BRANCH.RENAME' | translate }} {{ item }}
+
+
+
\ No newline at end of file
diff --git a/src/app/screens/left-panel/left-panel.component.scss b/src/app/screens/left-panel/left-panel.component.scss
new file mode 100644
index 0000000..3dd722f
--- /dev/null
+++ b/src/app/screens/left-panel/left-panel.component.scss
@@ -0,0 +1,84 @@
+@import '../../../variables.scss';
+
+.left-panel {
+ display:inline-block;
+ height: calc(100% - 50px);
+ width: 15%;
+ vertical-align: top;
+ overflow: auto;
+ overflow-x: hidden;
+
+ &.dark {
+ background: $low-dark;
+ color: $white;
+ border-top: 1px solid $dark;
+
+ &::-webkit-scrollbar-track-piece {
+ background-color: rgba(55, 56, 65, 0.3);
+ }
+ &::-webkit-scrollbar-thumb:vertical {
+ background-color: #373841;
+ border: 1px solid rgba(55, 56, 65, 0.3);
+ }
+ }
+
+ &.light {
+ background: $dark-grey-light;
+ color: $dark;
+ border-top: 1px solid $border-dark-grey-light;
+
+ &::-webkit-scrollbar-track-piece {
+ background-color: rgba(0, 0, 0, 0.1);
+ }
+ &::-webkit-scrollbar-thumb:vertical {
+ background-color: #C6C6C6;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ }
+ }
+
+ &::-webkit-scrollbar {
+ width: 5px;
+ height: 10px;
+ }
+ &::-webkit-scrollbar-button:start:decrement,
+ &::-webkit-scrollbar-button:end:increment {
+ height: 10px;
+ background-color: transparent;
+ }
+ &::-webkit-scrollbar-track-piece {
+ -webkit-border-radius: 16px;
+ }
+ &::-webkit-scrollbar-thumb:vertical {
+ height: 10px;
+ -webkit-border-radius: 16px;
+ }
+}
+
+.gh-current-branch.light {
+ @include bg-color($light-green-hover-light);
+ &:hover {
+ @include bg-color($light-green-hover-light);
+ cursor: default;
+ }
+}
+
+.gh-current-branch.dark {
+ @include bg-color($light-green-hover);
+
+ &:hover {
+ @include bg-color($light-green-hover);
+ }
+}
+
+.gh-branch.dark {
+ &:hover {
+ @include bg-color($disabled-blue);
+ cursor: pointer;
+ }
+}
+.gh-branch.light {
+ &:hover {
+ @include bg-color($modal-tab-hover-light);
+ cursor: pointer;
+ }
+}
\ No newline at end of file
diff --git a/src/app/screens/left-panel/left-panel.component.spec.ts b/src/app/screens/left-panel/left-panel.component.spec.ts
new file mode 100644
index 0000000..fa77519
--- /dev/null
+++ b/src/app/screens/left-panel/left-panel.component.spec.ts
@@ -0,0 +1,236 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LeftPanelComponent } from './left-panel.component';
+import { ThemePreferencesService } from '../../providers/theme-preferences.service';
+import { MockThemePreferencesService } from '../../models/MockThemePreferencesService';
+import { TranslateModule, TranslateLoader, TranslateService } from '@ngx-translate/core';
+import { MockTranslateLoader } from '../../models/MockTranslateLoader';
+import { GitService } from '../../providers/git.service';
+import { LeftPanelService } from '../../providers/left-panel.service';
+import { MockLeftPanelService } from '../../models/MockLeftPanelService';
+import { AccordionComponent } from '../../components/accordion/accordion.component';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { MockGitService } from '../../models/MockGitService';
+import { ContextMenuModule, ContextMenuComponent, ContextMenuService} from 'ngx-contextmenu';
+import { RightPanelService } from '../../providers/right-panel.service';
+import { MockRightPanelService } from '../../models/MockRightPanelService';
+import { LoaderComponent } from '../../components/loader/loader.component';
+import { ToastrService, ToastrModule } from 'ngx-toastr';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
+import { MockLanguagePreferencesService } from '../../models/MockLanguagePreferenceService';
+import { LanguagePreferencesService } from '../../providers/language-preferences.service';
+import { MockTranslateService } from '../../models/MockTranslateService';
+import { NewBranchCouple } from '../../models/NewBranchCouple';
+
+describe('LeftPanelComponent', () => {
+ /* tslint:disable */
+ let component: LeftPanelComponent;
+ let fixture: ComponentFixture;
+ let leftPanelEl: DebugElement;
+ /* tslint:enable */
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ LeftPanelComponent,
+ AccordionComponent,
+ LoaderComponent
+ ],
+ imports: [
+ ContextMenuModule,
+ TranslateModule.forRoot({
+ loader: {provide: TranslateLoader, useClass: MockTranslateLoader}
+ }),
+ ToastrModule.forRoot(),
+ NgbModule,
+ BrowserAnimationsModule,
+ ContextMenuModule
+ ],
+ providers: [
+ {
+ provide: ThemePreferencesService,
+ useClass: MockThemePreferencesService
+ },
+ {
+ provide: GitService,
+ useClass: MockGitService
+ },
+ {
+ provide: RightPanelService,
+ useClass: MockRightPanelService
+ },
+ {
+ provide: LeftPanelService,
+ useClass: MockLeftPanelService
+ },
+ {
+ provide: LanguagePreferencesService,
+ useClass: MockLanguagePreferencesService
+ },
+ {
+ provide: TranslateService,
+ useClass: MockTranslateService
+ },
+ ToastrService,
+ ContextMenuService
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LeftPanelComponent);
+ component = fixture.componentInstance;
+ leftPanelEl = fixture.debugElement.query(By.css('.left-panel'));
+ });
+
+ it('tests the component creation', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('tests the ngOnInit function', () => {
+ component.ngOnInit();
+
+ expect(component.themePrefSubscription).toBeDefined();
+ expect(component.branchNameSubscription).toBeDefined();
+ expect(component.localBranchesSubscription).toBeDefined();
+ expect(component.loadingVisibleSubscription).toBeDefined();
+ });
+
+ it ('test the checkoutLocalBranch function with local not as current and not conflicted branches', (done) => {
+ const LocalBranch = 'local';
+ const CurrentBranch = 'current';
+ const Visibility = true;
+ component.currentBranch = CurrentBranch;
+ component.loadingVisible = Visibility;
+ component.checkoutLocalBranch(LocalBranch).then(() => {
+ expect(component.loadingVisible).toBeFalsy();
+ done();
+ });
+ });
+
+ it ('test the checkoutLocalBranch function with local not as current and conflicted branches', (done) => {
+ const LocalBranch = 'conflicted';
+ const CurrentBranch = 'current';
+ const Visibility = true;
+ component.currentBranch = CurrentBranch;
+ component.loadingVisible = Visibility;
+ component.checkoutLocalBranch(LocalBranch).then(() => {
+ expect(component.loadingVisible).toBeFalsy();
+ done();
+ });
+ });
+
+ it ('test the checkoutLocalBranch function with local as current', (done) => {
+ const LocalBranch = 'current';
+ const CurrentBranch = 'current';
+ const Visibility = true;
+ component.currentBranch = CurrentBranch;
+ component.loadingVisible = Visibility;
+ component.checkoutLocalBranch(LocalBranch);
+ done();
+ });
+
+ it ('test the checkoutRemoteBranch function with branch not in local', (done) => {
+ const RemoteBranch = 'origin/test';
+ const CurrentBranch = 'current';
+ const LocalBranches = ['master', 'toto'];
+ const Visibility = true;
+ component.currentBranch = CurrentBranch;
+ component.localBranches = LocalBranches;
+ component.loadingVisible = Visibility;
+ component.checkoutRemoteBranch(RemoteBranch).then(() => {
+ expect(component.loadingVisible).toBeFalsy();
+ done();
+ });
+ });
+
+ it ('test the checkoutRemoteBranch function with branch in local and valid', (done) => {
+ const RemoteBranch = 'origin/toto';
+ const CurrentBranch = 'current';
+ const LocalBranches = ['master', 'toto'];
+ const Visibility = true;
+ component.currentBranch = CurrentBranch;
+ component.localBranches = LocalBranches;
+ component.loadingVisible = Visibility;
+ component.checkoutRemoteBranch(RemoteBranch).then(() => {
+ expect(component.loadingVisible).toBeFalsy();
+ done();
+ });
+ });
+
+ it ('test the checkoutRemoteBranch function with branch in local and no data back', (done) => {
+ const RemoteBranch = 'origin/other';
+ const CurrentBranch = 'current';
+ const LocalBranches = ['master', 'toto', 'other'];
+ const Visibility = true;
+ component.currentBranch = CurrentBranch;
+ component.localBranches = LocalBranches;
+ component.loadingVisible = Visibility;
+ component.checkoutRemoteBranch(RemoteBranch).then(() => {
+ expect(component.loadingVisible).toBeFalsy();
+ done();
+ });
+ });
+
+ it ('test the checkoutRemoteBranch function with branch in local and data back', (done) => {
+ const RemoteBranch = 'origin/newdata';
+ const CurrentBranch = 'current';
+ const LocalBranches = ['master', 'toto', 'newdata'];
+ const Visibility = true;
+ component.currentBranch = CurrentBranch;
+ component.localBranches = LocalBranches;
+ component.loadingVisible = Visibility;
+ spyOn(component.checkoutInfoBarChange, 'emit');
+ component.checkoutRemoteBranch(RemoteBranch).then(() => {
+ expect(component.checkoutInfoBarChange.emit).toHaveBeenCalledWith(RemoteBranch);
+ done();
+ });
+ });
+
+ it ('test the openCreateBranchInfoBar function with a local branch', () => {
+ const LocalBranch = 'localBranch';
+ spyOn(component.createBranchInfoBar, 'emit');
+ component.openCreateBranchInfoBar(LocalBranch);
+ fixture.detectChanges();
+ expect(component.createBranchInfoBar.emit).toHaveBeenCalledWith(LocalBranch);
+ });
+
+ it ('test the openCreateBranchInfoBar function with a remote branch', () => {
+ const RemoteBranch = 'origin/newData';
+ spyOn(component.createBranchInfoBar, 'emit');
+ component.openCreateBranchInfoBar(RemoteBranch);
+ fixture.detectChanges();
+ expect(component.createBranchInfoBar.emit).toHaveBeenCalledWith(RemoteBranch);
+ });
+
+
+ it('test the renameBranch function', () => {
+ const OldBranch = 'toto';
+ component.newBranchCouple = new NewBranchCouple();
+ component.newBranchCouple.oldBranch = 'titi';
+ component.renameBranch(OldBranch);
+ expect(component.newBranchCouple.oldBranch).toEqual(OldBranch);
+ });
+
+ it ('test the ngOnDestroy function with defined subscriptions', () => {
+ component.ngOnInit();
+ component.ngOnDestroy();
+
+ expect(component.themePrefSubscription.closed).toBeTruthy();
+ expect(component.branchNameSubscription.closed).toBeTruthy();
+ expect(component.localBranchesSubscription.closed).toBeTruthy();
+ expect(component.loadingVisibleSubscription.closed).toBeTruthy();
+ });
+
+ it ('test the ngOnDestroy function with undefined subscriptions', () => {
+ component.ngOnDestroy();
+
+ expect(component.themePrefSubscription).toBeUndefined();
+ expect(component.branchNameSubscription).toBeUndefined();
+ expect(component.localBranchesSubscription).toBeUndefined();
+ expect(component.loadingVisibleSubscription).toBeUndefined();
+ });
+});
diff --git a/src/app/screens/left-panel/left-panel.component.ts b/src/app/screens/left-panel/left-panel.component.ts
new file mode 100644
index 0000000..e097d1d
--- /dev/null
+++ b/src/app/screens/left-panel/left-panel.component.ts
@@ -0,0 +1,167 @@
+import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ViewChild } from '@angular/core';
+import { ThemePreferencesService } from '../../providers/theme-preferences.service';
+import { Subscription } from 'rxjs';
+import { GitService } from '../../providers/git.service';
+import { LeftPanelService } from '../../providers/left-panel.service';
+import { TranslateService } from '@ngx-translate/core';
+import { LanguagePreferencesService } from '../../providers/language-preferences.service';
+import { NewBranchCouple } from '../../models/NewBranchCouple';
+import { ToastrService } from 'ngx-toastr';
+import { ContextMenuComponent } from 'ngx-contextmenu';
+import { RightPanelService } from '../../providers/right-panel.service';
+
+@Component({
+ selector: 'app-left-panel',
+ templateUrl: './left-panel.component.html',
+ styleUrls: ['./left-panel.component.scss']
+})
+export class LeftPanelComponent implements OnInit, OnDestroy {
+ @ViewChild('branchCM') branchCM: ContextMenuComponent;
+ @ViewChild('remoteCM') remoteCM: ContextMenuComponent;
+ currentTheme: string;
+ themePrefSubscription: Subscription;
+ localBranches: any;
+ localBranchesSubscription: Subscription;
+ currentNewBranchCouple: NewBranchCouple;
+ @Output()
+ newBranchCoupleChange = new EventEmitter();
+ remoteBranches: any;
+ remoteBranchesSubscription: Subscription;
+ currentBranch: any;
+ branchNameSubscription: Subscription;
+ objectKeys = Object.keys;
+ loadingVisible: Boolean;
+ loadingVisibleSubscription: Subscription;
+ @Output() checkoutInfoBarChange = new EventEmitter();
+ @Output() createBranchInfoBar = new EventEmitter();
+
+ constructor(private themePrefService: ThemePreferencesService, private gitService: GitService,
+ private leftPanelService: LeftPanelService, private translate: TranslateService,
+ private langPrefService: LanguagePreferencesService, private toastr: ToastrService,
+ private rightPanelService: RightPanelService) {
+
+ }
+
+ @Input()
+ get newBranchCouple() {
+ return this.currentNewBranchCouple;
+ }
+
+ set newBranchCouple(couple) {
+ this.currentNewBranchCouple = couple;
+ this.newBranchCoupleChange.emit(this.currentNewBranchCouple);
+ }
+
+ ngOnInit() {
+ this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe(
+ (newTheme: string) => {
+ this.currentTheme = newTheme;
+ }
+ );
+ this.themePrefService.emitThemePreferencesSubject();
+
+ this.branchNameSubscription = this.gitService.branchNameSubject.subscribe(
+ (currentBranch: any) => {
+ this.currentBranch = currentBranch;
+ });
+ this.gitService.emitBranchNameSubject();
+
+ this.localBranchesSubscription = this.leftPanelService.localBranchesSubject.subscribe(
+ (localBranches: any) => {
+ this.localBranches = localBranches;
+ });
+
+ this.remoteBranchesSubscription = this.leftPanelService.remoteBranchesSubject.subscribe(
+ (remoteBranches: any) => {
+ this.remoteBranches = remoteBranches;
+ });
+
+ this.loadingVisibleSubscription = this.leftPanelService.loadingVisibleSubject.subscribe(
+ (loadingVisible: any) => {
+ this.loadingVisible = loadingVisible;
+ }
+ );
+
+ this.leftPanelService.setLocalBranches();
+ this.leftPanelService.setRemoteBranches();
+ this.leftPanelService.setLoadingVisible(this.loadingVisible);
+ }
+
+ async checkoutLocalBranch(localBranch) {
+ if (localBranch !== this.currentBranch) {
+ this.loadingVisible = true;
+ return this.gitService.checkoutLocalBranch(localBranch).then((result) => {
+ this.toastr.info(result.message, result.title, {
+ onActivateTick: true
+ });
+
+ this.loadingVisible = false;
+ this.leftPanelService.setLocalBranches();
+ this.leftPanelService.setRemoteBranches();
+ this.updateCommitDescription();
+ }).catch((result) => {
+ this.loadingVisible = false;
+ this.toastr.error(result.message, result.title, {
+ onActivateTick: true
+ });
+ });
+ }
+ }
+
+ checkoutRemoteBranch(remoteBranch) {
+ this.loadingVisible = true;
+ const IsInLocal = this.localBranches.includes(remoteBranch.split('/')[1]);
+ return this.gitService.checkoutRemoteBranch(remoteBranch, this.currentBranch, IsInLocal).then((result) => {
+ this.toastr.info(result.message, result.title, {
+ onActivateTick: true
+ });
+ this.loadingVisible = false;
+ this.leftPanelService.setLocalBranches();
+ this.leftPanelService.setRemoteBranches();
+ this.updateCommitDescription();
+ }).catch((result) => {
+ if (!result.newData) {
+ this.loadingVisible = false;
+ this.toastr.error(result.message, result.title, {
+ onActivateTick: true
+ });
+ } else {
+ this.checkoutInfoBarChange.emit(remoteBranch);
+ }
+ });
+ }
+
+ openCreateBranchInfoBar(branch) {
+ this.createBranchInfoBar.emit(branch);
+ }
+
+ renameBranch(branch: string) {
+ var TmpNewBr = new NewBranchCouple();
+ TmpNewBr.oldBranch = branch;
+ this.newBranchCouple = TmpNewBr;
+ }
+
+ async updateCommitDescription() {
+ return this.gitService.revParseHEAD().then((data) => {
+ this.rightPanelService.setCommitHash(data.replace('\n', ''));
+ });
+ }
+
+ ngOnDestroy() {
+ if (this.themePrefSubscription) {
+ this.themePrefSubscription.unsubscribe();
+ }
+ if (this.localBranchesSubscription) {
+ this.localBranchesSubscription.unsubscribe();
+ }
+ if (this.remoteBranchesSubscription) {
+ this.remoteBranchesSubscription.unsubscribe();
+ }
+ if (this.branchNameSubscription) {
+ this.branchNameSubscription.unsubscribe();
+ }
+ if (this.loadingVisibleSubscription) {
+ this.loadingVisibleSubscription.unsubscribe();
+ }
+ }
+}
diff --git a/src/app/screens/preferences/preferences.component.spec.ts b/src/app/screens/preferences/preferences.component.spec.ts
index 6695d87..e746241 100644
--- a/src/app/screens/preferences/preferences.component.spec.ts
+++ b/src/app/screens/preferences/preferences.component.spec.ts
@@ -40,12 +40,22 @@ import { ElectronService } from '../../providers/electron.service';
import { MockElectronService } from '../../models/MockElectronService';
import { TerminalManagerService } from '../../providers/terminal-manager.service';
import { MockTerminalManagerService } from '../../models/MockTerminalManagerService';
+import { LeftPanelComponent } from '../left-panel/left-panel.component';
+import { GraphComponent } from '../graph/graph.component';
+import { RightPanelComponent } from '../right-panel/right-panel.component';
+import { ViewCommitComponent } from '../view-commit/view-commit.component';
+import { SendCommitComponent } from '../send-commit/send-commit.component';
+import { TextAreaComponent } from '../../components/text-area/text-area.component';
+import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component';
+import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component';
describe('PreferencesComponent', () => {
+ /* tslint:disable */
let component: PreferencesComponent;
let fixture: ComponentFixture;
let langPrefService: LanguagePreferencesService;
let terminalService: TerminalManagerService;
+ /* tslint:enable */
beforeEach(async(() => {
TestBed.configureTestingModule({
@@ -66,6 +76,14 @@ describe('PreferencesComponent', () => {
InputComponent,
CopyButtonComponent,
InputNumberComponent,
+ LeftPanelComponent,
+ GraphComponent,
+ RightPanelComponent,
+ SendCommitComponent,
+ ViewCommitComponent,
+ TextAreaComponent,
+ CommitTextAreaComponent,
+ FileDiffCommitComponent
],
imports: [
FormsModule,
diff --git a/src/app/screens/right-panel/right-panel.component.html b/src/app/screens/right-panel/right-panel.component.html
new file mode 100644
index 0000000..14fd465
--- /dev/null
+++ b/src/app/screens/right-panel/right-panel.component.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/src/app/screens/right-panel/right-panel.component.scss b/src/app/screens/right-panel/right-panel.component.scss
new file mode 100644
index 0000000..320423c
--- /dev/null
+++ b/src/app/screens/right-panel/right-panel.component.scss
@@ -0,0 +1,9 @@
+@import '../../../variables.scss';
+
+.right-panel {
+ display:inline-block;
+ height: calc(100% - 50px);
+ width: 20%;
+ text-align:center;
+ vertical-align: top;
+}
\ No newline at end of file
diff --git a/src/app/screens/right-panel/right-panel.component.spec.ts b/src/app/screens/right-panel/right-panel.component.spec.ts
new file mode 100644
index 0000000..6da48ad
--- /dev/null
+++ b/src/app/screens/right-panel/right-panel.component.spec.ts
@@ -0,0 +1,80 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RightPanelComponent } from './right-panel.component';
+import { ThemePreferencesService } from '../../providers/theme-preferences.service';
+import { MockThemePreferencesService } from '../../models/MockThemePreferencesService';
+import { ViewCommitComponent } from '../view-commit/view-commit.component';
+import { SendCommitComponent } from '../send-commit/send-commit.component';
+import { MockRightPanelService } from '../../models/MockRightPanelService';
+import { RightPanelService } from '../../providers/right-panel.service';
+import { ButtonComponent } from '../../components/button/button.component';
+import { TranslateModule } from '@ngx-translate/core';
+import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component';
+import { LoaderComponent } from '../../components/loader/loader.component';
+import { TextAreaComponent } from '../../components/text-area/text-area.component';
+import { CommitTextAreaComponent } from '../../components/commit-text-area/commit-text-area.component';
+import { NgbTooltip, NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { FormsModule } from '@angular/forms';
+
+describe('RightPanelComponent', () => {
+ /* tslint:disable */
+ let component: RightPanelComponent;
+ let fixture: ComponentFixture;
+ /* tslint:enable */
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ RightPanelComponent,
+ ViewCommitComponent,
+ SendCommitComponent,
+ ButtonComponent,
+ FileDiffCommitComponent,
+ LoaderComponent,
+ TextAreaComponent,
+ CommitTextAreaComponent,
+ FileDiffCommitComponent
+ ],
+ imports: [
+ TranslateModule,
+ NgbModule,
+ FormsModule
+ ],
+ providers: [
+ {
+ provide: RightPanelService,
+ useClass: MockRightPanelService
+ },
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RightPanelComponent);
+ component = fixture.componentInstance;
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it ('tests the ngOnInit function', () => {
+ component.ngOnInit();
+
+ expect(component.isViewSubscription).toBeDefined();
+ });
+
+ it ('tests the ngOnDestroy function with defined isViewSubscription', () => {
+ component.ngOnInit();
+ component.ngOnDestroy();
+
+ expect(component.isViewSubscription.closed).toBeTruthy();
+ });
+
+ it ('tests the ngOnDestroy function with undefined isViewSubscription', () => {
+ component.ngOnDestroy();
+
+ expect(component.isViewSubscription).toBeUndefined();
+ });
+});
diff --git a/src/app/screens/right-panel/right-panel.component.ts b/src/app/screens/right-panel/right-panel.component.ts
new file mode 100644
index 0000000..7635425
--- /dev/null
+++ b/src/app/screens/right-panel/right-panel.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Subscription } from 'rxjs';
+import { RightPanelService } from '../../providers/right-panel.service';
+import { GitService } from '../../providers/git.service';
+
+@Component({
+ selector: 'app-right-panel',
+ templateUrl: './right-panel.component.html',
+ styleUrls: ['./right-panel.component.scss']
+})
+export class RightPanelComponent implements OnInit, OnDestroy {
+ isViewSubscription: Subscription;
+ isView: Boolean;
+
+ constructor(private rightPanelService: RightPanelService) { }
+
+ ngOnInit() {
+ this.isViewSubscription = this.rightPanelService.isViewSubject.subscribe(
+ (view: Boolean) => {
+ this.isView = view;
+ }
+ );
+ this.rightPanelService.emitIsViewSubject();
+ }
+
+ ngOnDestroy() {
+ if (this.isViewSubscription) {
+ this.isViewSubscription.unsubscribe();
+ }
+ }
+}
diff --git a/src/app/screens/send-commit/send-commit.component.html b/src/app/screens/send-commit/send-commit.component.html
new file mode 100644
index 0000000..31395ac
--- /dev/null
+++ b/src/app/screens/send-commit/send-commit.component.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ {{ 'UNSTAGED_FILES' | translate }} ({{ listUnstagedFiles.length }})
+
+
+
+
+
+
+
+ {{ 'STAGED_FILES' | translate }} ({{ listStagedFiles.length }})
+
+
+
+
+
+
diff --git a/src/app/screens/send-commit/send-commit.component.scss b/src/app/screens/send-commit/send-commit.component.scss
new file mode 100644
index 0000000..3119caa
--- /dev/null
+++ b/src/app/screens/send-commit/send-commit.component.scss
@@ -0,0 +1,61 @@
+@import '../../../variables.scss';
+
+.send-commit {
+ height: 100%;
+
+ &.dark {
+ background: $light-grey;
+ color: $muted-white;
+ border-top: 1px solid $light-grey;
+ }
+
+ &.light {
+ background: $dark-grey-light;
+ color: $dark;
+ border-top: 1px solid $border-dark-grey-light;
+ }
+
+ &_header {
+ display: none;
+
+ &.dark {
+ color: $white;
+ background: $blue-grey;
+ border-bottom: 1px solid $dark-grey;
+ }
+
+ &.light {
+ background: $light-grey-light;
+ border-bottom: 1px solid $border-dark-grey-light;
+ }
+ }
+
+ &_container {
+ @include wX-hX(100%, calc(100% - #{$footer-height}));
+ }
+
+ &_stage {
+ height: 28%;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ }
+
+ &_unstage {
+ height: 28%;
+ padding-top: 30px;
+ padding-bottom: 10px;
+ }
+
+ &_text {
+ font-size: $fs-medium;
+
+ &_button {
+ font-size: $fs-small;
+ float: right;
+ }
+ }
+}
+
+.visible {
+ display: block;
+}
\ No newline at end of file
diff --git a/src/app/screens/send-commit/send-commit.component.spec.ts b/src/app/screens/send-commit/send-commit.component.spec.ts
new file mode 100644
index 0000000..3fe16ca
--- /dev/null
+++ b/src/app/screens/send-commit/send-commit.component.spec.ts
@@ -0,0 +1,105 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SendCommitComponent } from './send-commit.component';
+import { ThemePreferencesService } from '../../providers/theme-preferences.service';
+import { MockThemePreferencesService } from '../../models/MockThemePreferencesService';
+import { ButtonComponent } from '../../components/button/button.component';
+import { TranslateModule, TranslateService, TranslateLoader } from '@ngx-translate/core';
+import { GitService } from '../../providers/git.service';
+import { MockGitService } from '../../models/MockGitService';
+import { MockTranslateService } from '../../models/MockTranslateService';
+import { FileDiffCommitComponent } from '../../components/file-diff-commit/file-diff-commit.component';
+import { RightPanelService } from '../../providers/right-panel.service';
+import { MockRightPanelService } from '../../models/MockRightPanelService';
+import { LeftPanelService } from '../../providers/left-panel.service';
+import { MockLeftPanelService } from '../../models/MockLeftPanelService';
+import { MockTranslateLoader } from '../../models/MockTranslateLoader';
+
+describe('SendCommitComponent', () => {
+ /* tslint:disable */
+ let component: SendCommitComponent;
+ let fixture: ComponentFixture;
+ /* tslint:enable */
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ SendCommitComponent,
+ ButtonComponent,
+ FileDiffCommitComponent
+ ],
+ imports: [
+ TranslateModule.forRoot({
+ loader: {provide: TranslateLoader, useClass: MockTranslateLoader}
+ }),
+ ],
+ providers: [
+ {
+ provide: ThemePreferencesService,
+ useClass: MockThemePreferencesService
+ },
+ {
+ provide: GitService,
+ useClass: MockGitService
+ },
+ {
+ provide: RightPanelService,
+ useClass: MockRightPanelService
+ },
+ {
+ provide: LeftPanelService,
+ useClass: MockLeftPanelService
+ },
+ {
+ provide: TranslateService,
+ useClass: MockTranslateService
+ },
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SendCommitComponent);
+ component = fixture.componentInstance;
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it ('tests the ngOnInit function', () => {
+ component.ngOnInit();
+
+ expect(component.themePrefSubscription).toBeDefined();
+ });
+
+ it ('tests the ngOnDestroy function with defined themePrefSubscription', () => {
+ component.ngOnInit();
+ component.ngOnDestroy();
+
+ expect(component.themePrefSubscription.closed).toBeTruthy();
+ });
+
+ it ('tests the ngOnDestroy function with undefined themePrefSubscription', () => {
+ component.ngOnDestroy();
+
+ expect(component.themePrefSubscription).toBeUndefined();
+ });
+
+ it ('test the addAllFile function', () => {
+ const Path = 'src/file3';
+ component.ngOnInit();
+ component.listStagedFiles = [];
+ component.addAllFile();
+ expect(component.listStagedFiles[0].path).toBe(Path);
+ });
+
+ it ('test the removeAllFile function', () => {
+ const Path = 'src/file1';
+ component.ngOnInit();
+ component.listUnstagedFiles = [];
+ component.removeAllFile();
+ expect(component.listUnstagedFiles[0].path).toBe(Path);
+ });
+});
diff --git a/src/app/screens/send-commit/send-commit.component.ts b/src/app/screens/send-commit/send-commit.component.ts
new file mode 100644
index 0000000..b31b2f3
--- /dev/null
+++ b/src/app/screens/send-commit/send-commit.component.ts
@@ -0,0 +1,63 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { Subscription } from 'rxjs';
+import { ThemePreferencesService } from '../../providers/theme-preferences.service';
+import { GitService } from '../../providers/git.service';
+import { RightPanelService } from '../../providers/right-panel.service';
+
+@Component({
+ selector: 'app-send-commit',
+ templateUrl: './send-commit.component.html',
+ styleUrls: ['./send-commit.component.scss']
+})
+export class SendCommitComponent implements OnInit, OnDestroy {
+ themePrefSubscription: Subscription;
+ currentTheme: string;
+ listUnstagedFiles: any[];
+ listUnstagedFilesSubscription: Subscription;
+ listStagedFiles: any[];
+ listStagedFilesSubscription: Subscription;
+
+ constructor(private themePrefService: ThemePreferencesService, private gitService: GitService,
+ private rightPanelService: RightPanelService) { }
+
+ ngOnInit() {
+ this.themePrefSubscription = this.themePrefService.themePreferenceSubject.subscribe(
+ (newTheme: string) => {
+ this.currentTheme = newTheme;
+ }
+ );
+ this.themePrefService.emitThemePreferencesSubject();
+
+ this.listUnstagedFilesSubscription = this.rightPanelService.listUnstagedFilesSubject.subscribe(
+ (listUnstagedFiles: any) => {
+ this.listUnstagedFiles = listUnstagedFiles;
+ });
+ this.rightPanelService.emitListUnstagedFilesSubject();
+
+ this.listStagedFilesSubscription = this.rightPanelService.listStagedFilesSubject.subscribe(
+ (listStagedFiles: any) => {
+ this.listStagedFiles = listStagedFiles;
+ });
+ this.rightPanelService.emitListStagedFilesSubject();
+ }
+
+ addAllFile() {
+ this.gitService.addFile('.');
+ }
+
+ removeAllFile() {
+ this.gitService.removeFile('.');
+ }
+
+ ngOnDestroy() {
+ if (this.themePrefSubscription) {
+ this.themePrefSubscription.unsubscribe();
+ }
+ if (this.listUnstagedFilesSubscription) {
+ this.listUnstagedFilesSubscription.unsubscribe();
+ }
+ if (this.listStagedFilesSubscription) {
+ this.listStagedFilesSubscription.unsubscribe();
+ }
+ }
+}
diff --git a/src/app/screens/toolbox/toolbox.component.html b/src/app/screens/toolbox/toolbox.component.html
index 3fa9a73..8eea2d5 100644
--- a/src/app/screens/toolbox/toolbox.component.html
+++ b/src/app/screens/toolbox/toolbox.component.html
@@ -657,6 +657,82 @@ Dropdowns