Move repository overview to separate component
Signed-off-by: trivernis <trivernis@protonmail.com>pull/16/head
parent
ed8ddf4d9f
commit
bb2a63c703
@ -1,18 +1,6 @@
|
||||
<div *ngIf="!selectedRepository" class="repo-page-content">
|
||||
<div class="add-repo-tools">
|
||||
<button (click)="openAddRepositoryDialog()" color="primary" mat-flat-button>Add Repository</button>
|
||||
</div>
|
||||
<div class="repository-list">
|
||||
<div *ngFor="let repository of repositories" class="repository-container">
|
||||
<app-repository-card (openEvent)="this.onOpenRepository($event)"
|
||||
[repository]="repository"></app-repository-card>
|
||||
</div>
|
||||
<app-middle-centered *ngIf="this.repositories.length === 0" class="add-repository-prompt">
|
||||
<h1>There are no repositories yet. You can create a repository or add an existing one.</h1>
|
||||
<button (click)="this.openAddRepositoryDialog()" color="primary" mat-flat-button>Add Repository</button>
|
||||
</app-middle-centered>
|
||||
</div>
|
||||
<div *ngIf="!this.selectedRepository">
|
||||
<app-repository-overview></app-repository-overview>
|
||||
</div>
|
||||
<div *ngIf="selectedRepository" class="repo-details">
|
||||
<app-repository-details-view [repository]="selectedRepository"></app-repository-details-view>
|
||||
<div *ngIf="this.selectedRepository" class="repo-details">
|
||||
<app-repository-details-view [repository]="this.selectedRepository"></app-repository-details-view>
|
||||
</div>
|
||||
|
@ -1,164 +1,18 @@
|
||||
import {AfterViewInit, Component, OnInit} from "@angular/core";
|
||||
import {Repository} from "../../../../api/models/Repository";
|
||||
import {Component} from "@angular/core";
|
||||
import {RepositoryService} from "../../../services/repository/repository.service";
|
||||
import {MatDialog, MatDialogRef} from "@angular/material/dialog";
|
||||
import {DownloadDaemonDialogComponent} from "./download-daemon-dialog/download-daemon-dialog.component";
|
||||
import {
|
||||
AddRepositoryDialogComponent
|
||||
} from "../../shared/repository/repository/add-repository-dialog/add-repository-dialog.component";
|
||||
import {LoggingService} from "../../../services/logging/logging.service";
|
||||
import {BehaviorSubject} from "rxjs";
|
||||
import {BusyDialogComponent} from "../../shared/app-common/busy-dialog/busy-dialog.component";
|
||||
import {JobService} from "../../../services/job/job.service";
|
||||
import {StateService} from "../../../services/state/state.service";
|
||||
import {Repository} from "../../../../api/models/Repository";
|
||||
|
||||
type BusyDialogContext = { message: BehaviorSubject<string>, dialog: MatDialogRef<BusyDialogComponent> };
|
||||
|
||||
@Component({
|
||||
selector: "app-repositories-tab",
|
||||
templateUrl: "./repositories-tab.component.html",
|
||||
styleUrls: ["./repositories-tab.component.scss"]
|
||||
})
|
||||
export class RepositoriesTabComponent implements OnInit, AfterViewInit {
|
||||
public repositories: Repository[] = [];
|
||||
public selectedRepository?: Repository;
|
||||
|
||||
constructor(
|
||||
private logger: LoggingService,
|
||||
private repoService: RepositoryService,
|
||||
private jobService: JobService,
|
||||
private stateService: StateService,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.repoService.repositories.subscribe({
|
||||
next: (repos) => {
|
||||
this.repositories = repos;
|
||||
}
|
||||
});
|
||||
this.repoService.selectedRepository.subscribe(
|
||||
repo => this.selectedRepository = repo);
|
||||
}
|
||||
|
||||
public async ngAfterViewInit() {
|
||||
await this.checkAndPromptDaemonExecutable();
|
||||
}
|
||||
|
||||
public async startDaemonAndSelectRepository(repository: Repository) {
|
||||
try {
|
||||
let dialogContext = this.openStartupDialog(repository);
|
||||
let daemonRunning = await this.repoService.checkDaemonRunning(
|
||||
repository.path!);
|
||||
if (!daemonRunning) {
|
||||
dialogContext.message.next("Starting repository daemon...");
|
||||
await this.repoService.startDaemon(repository.path!);
|
||||
|
||||
await new Promise((res, _) => {
|
||||
setTimeout(res, 2000); // wait for the daemon to start
|
||||
});
|
||||
}
|
||||
await this.selectRepository(repository, dialogContext);
|
||||
} catch (err: any) {
|
||||
this.logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async selectRepository(repository: Repository, dialogContext?: BusyDialogContext) {
|
||||
dialogContext = dialogContext ?? this.openStartupDialog(repository);
|
||||
try {
|
||||
dialogContext.message.next("Opening repository...");
|
||||
await this.repoService.setRepository(repository);
|
||||
await this.runRepositoryStartupTasks(dialogContext);
|
||||
dialogContext.message.next("Restoring previous tabs...");
|
||||
await this.repoService.loadRepositories();
|
||||
dialogContext.dialog.close(true);
|
||||
} catch (err: any) {
|
||||
this.logger.error(err);
|
||||
dialogContext.message.next(
|
||||
"Failed to open repository: " + err.toString());
|
||||
await this.forceCloseRepository();
|
||||
setTimeout(() => dialogContext!.dialog.close(true), 1000);
|
||||
}
|
||||
}
|
||||
export class RepositoriesTabComponent {
|
||||
|
||||
public openAddRepositoryDialog() {
|
||||
this.dialog.open(AddRepositoryDialogComponent, {
|
||||
disableClose: true,
|
||||
minWidth: "30%",
|
||||
minHeight: "30%",
|
||||
});
|
||||
}
|
||||
|
||||
public async onOpenRepository(repository: Repository) {
|
||||
if (!repository.local) {
|
||||
await this.selectRepository(repository);
|
||||
} else {
|
||||
await this.startDaemonAndSelectRepository(repository);
|
||||
}
|
||||
}
|
||||
|
||||
private async forceCloseRepository() {
|
||||
try {
|
||||
await this.repoService.closeSelectedRepository();
|
||||
} catch {
|
||||
}
|
||||
try {
|
||||
await this.repoService.disconnectSelectedRepository();
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
private async runRepositoryStartupTasks(dialogContext: BusyDialogContext): Promise<void> {
|
||||
dialogContext.message.next("Checking integrity...");
|
||||
await this.jobService.runJob("CheckIntegrity");
|
||||
dialogContext.message.next("Running a vacuum on the database...");
|
||||
await this.jobService.runJob("Vacuum");
|
||||
dialogContext.message.next(
|
||||
"Migrating content descriptors to new format...");
|
||||
await this.jobService.runJob("MigrateContentDescriptors");
|
||||
dialogContext.message.next("Calculating repository sizes...");
|
||||
await this.jobService.runJob("CalculateSizes", false);
|
||||
dialogContext.message.next("Generating missing thumbnails...");
|
||||
await this.jobService.runJob("GenerateThumbnails");
|
||||
dialogContext.message.next("Finished repository startup");
|
||||
}
|
||||
|
||||
private openStartupDialog(repository: Repository): BusyDialogContext {
|
||||
const dialogMessage = new BehaviorSubject<string>(
|
||||
"Opening repository...");
|
||||
let dialog = this.dialog.open(BusyDialogComponent, {
|
||||
data: {
|
||||
title: `Opening repository '${repository.name}'`,
|
||||
message: dialogMessage,
|
||||
allowCancel: true,
|
||||
}, disableClose: true,
|
||||
minWidth: "30%",
|
||||
minHeight: "30%",
|
||||
});
|
||||
dialog.afterClosed().subscribe(async (result) => {
|
||||
if (!result) {
|
||||
await this.forceCloseRepository();
|
||||
}
|
||||
});
|
||||
|
||||
return { message: dialogMessage, dialog };
|
||||
}
|
||||
public selectedRepository?: Repository;
|
||||
|
||||
private async checkAndPromptDaemonExecutable() {
|
||||
if (!await this.repoService.checkDameonConfigured()) {
|
||||
const result = await this.dialog.open(
|
||||
DownloadDaemonDialogComponent,
|
||||
{
|
||||
disableClose: true,
|
||||
}
|
||||
).afterClosed().toPromise();
|
||||
if (result) {
|
||||
// recursion avoidance
|
||||
setTimeout(
|
||||
async () => await this.checkAndPromptDaemonExecutable(), 0);
|
||||
}
|
||||
}
|
||||
constructor(private repositoryService: RepositoryService) {
|
||||
const sub = this.repositoryService.selectedRepository.subscribe(repo => this.selectedRepository = repo);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
<div class="repo-page-content">
|
||||
<div class="add-repo-tools">
|
||||
<button (click)="openAddRepositoryDialog()" color="primary" mat-flat-button>Add Repository</button>
|
||||
</div>
|
||||
<div class="repository-list">
|
||||
<div *ngFor="let repository of repositories" class="repository-container">
|
||||
<app-repository-card (openEvent)="this.onOpenRepository($event)"
|
||||
[repository]="repository"></app-repository-card>
|
||||
</div>
|
||||
<app-middle-centered *ngIf="this.repositories.length === 0" class="add-repository-prompt">
|
||||
<h1>There are no repositories yet. You can create a repository or add an existing one.</h1>
|
||||
<button (click)="this.openAddRepositoryDialog()" color="primary" mat-flat-button>Add Repository</button>
|
||||
</app-middle-centered>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,42 @@
|
||||
.repository-container {
|
||||
margin: 1em;
|
||||
display: block;
|
||||
width: 600px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.repo-page-content {
|
||||
margin: 0 10%;
|
||||
height: calc(100% - 2em);
|
||||
}
|
||||
|
||||
.add-repo-tools {
|
||||
height: 5em;
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
||||
button {
|
||||
margin: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.repository-list {
|
||||
display: block;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
height: calc(100% - 5em);
|
||||
}
|
||||
|
||||
app-repository-card {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.add-repository-prompt {
|
||||
button {
|
||||
font-size: 1.5em;
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 0.5em;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RepositoryOverviewComponent } from './repository-overview.component';
|
||||
|
||||
describe('RepositoryOverviewComponent', () => {
|
||||
let component: RepositoryOverviewComponent;
|
||||
let fixture: ComponentFixture<RepositoryOverviewComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ RepositoryOverviewComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(RepositoryOverviewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,159 @@
|
||||
import {AfterViewInit, ChangeDetectionStrategy, Component, OnInit} from "@angular/core";
|
||||
import {Repository} from "../../../../../api/models/Repository";
|
||||
import {LoggingService} from "../../../../services/logging/logging.service";
|
||||
import {RepositoryService} from "../../../../services/repository/repository.service";
|
||||
import {JobService} from "../../../../services/job/job.service";
|
||||
import {StateService} from "../../../../services/state/state.service";
|
||||
import {MatDialog, MatDialogRef} from "@angular/material/dialog";
|
||||
import {
|
||||
AddRepositoryDialogComponent
|
||||
} from "../../../shared/repository/repository/add-repository-dialog/add-repository-dialog.component";
|
||||
import {BehaviorSubject} from "rxjs";
|
||||
import {BusyDialogComponent} from "../../../shared/app-common/busy-dialog/busy-dialog.component";
|
||||
import {DownloadDaemonDialogComponent} from "../download-daemon-dialog/download-daemon-dialog.component";
|
||||
|
||||
type BusyDialogContext = { message: BehaviorSubject<string>, dialog: MatDialogRef<BusyDialogComponent> };
|
||||
|
||||
@Component({
|
||||
selector: "app-repository-overview",
|
||||
templateUrl: "./repository-overview.component.html",
|
||||
styleUrls: ["./repository-overview.component.scss"],
|
||||
changeDetection: ChangeDetectionStrategy.Default
|
||||
})
|
||||
export class RepositoryOverviewComponent implements OnInit, AfterViewInit {
|
||||
|
||||
public repositories: Repository[] = [];
|
||||
|
||||
constructor(
|
||||
private logger: LoggingService,
|
||||
private repoService: RepositoryService,
|
||||
private jobService: JobService,
|
||||
private stateService: StateService,
|
||||
public dialog: MatDialog
|
||||
) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.repoService.repositories.subscribe(repos => this.repositories = repos);
|
||||
}
|
||||
|
||||
public async ngAfterViewInit() {
|
||||
await this.checkAndPromptDaemonExecutable();
|
||||
}
|
||||
|
||||
public async startDaemonAndSelectRepository(repository: Repository) {
|
||||
try {
|
||||
let dialogContext = this.openStartupDialog(repository);
|
||||
let daemonRunning = await this.repoService.checkDaemonRunning(
|
||||
repository.path!);
|
||||
if (!daemonRunning) {
|
||||
dialogContext.message.next("Starting repository daemon...");
|
||||
await this.repoService.startDaemon(repository.path!);
|
||||
|
||||
await new Promise((res, _) => {
|
||||
setTimeout(res, 2000); // wait for the daemon to start
|
||||
});
|
||||
}
|
||||
await this.selectRepository(repository, dialogContext);
|
||||
} catch (err: any) {
|
||||
this.logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async selectRepository(repository: Repository, dialogContext?: BusyDialogContext) {
|
||||
dialogContext = dialogContext ?? this.openStartupDialog(repository);
|
||||
try {
|
||||
dialogContext.message.next("Opening repository...");
|
||||
await this.repoService.setRepository(repository);
|
||||
await this.runRepositoryStartupTasks(dialogContext);
|
||||
dialogContext.message.next("Restoring previous tabs...");
|
||||
await this.repoService.loadRepositories();
|
||||
dialogContext.dialog.close(true);
|
||||
} catch (err: any) {
|
||||
this.logger.error(err);
|
||||
dialogContext.message.next(
|
||||
"Failed to open repository: " + err.toString());
|
||||
await this.forceCloseRepository();
|
||||
setTimeout(() => dialogContext!.dialog.close(true), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
public openAddRepositoryDialog() {
|
||||
this.dialog.open(AddRepositoryDialogComponent, {
|
||||
disableClose: true,
|
||||
minWidth: "30%",
|
||||
minHeight: "30%",
|
||||
});
|
||||
}
|
||||
|
||||
public async onOpenRepository(repository: Repository) {
|
||||
if (!repository.local) {
|
||||
await this.selectRepository(repository);
|
||||
} else {
|
||||
await this.startDaemonAndSelectRepository(repository);
|
||||
}
|
||||
}
|
||||
|
||||
private async forceCloseRepository() {
|
||||
try {
|
||||
await this.repoService.closeSelectedRepository();
|
||||
} catch {
|
||||
}
|
||||
try {
|
||||
await this.repoService.disconnectSelectedRepository();
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
private async runRepositoryStartupTasks(dialogContext: BusyDialogContext): Promise<void> {
|
||||
dialogContext.message.next("Checking integrity...");
|
||||
await this.jobService.runJob("CheckIntegrity");
|
||||
dialogContext.message.next("Running a vacuum on the database...");
|
||||
await this.jobService.runJob("Vacuum");
|
||||
dialogContext.message.next(
|
||||
"Migrating content descriptors to new format...");
|
||||
await this.jobService.runJob("MigrateContentDescriptors");
|
||||
dialogContext.message.next("Calculating repository sizes...");
|
||||
await this.jobService.runJob("CalculateSizes", false);
|
||||
dialogContext.message.next("Generating missing thumbnails...");
|
||||
await this.jobService.runJob("GenerateThumbnails");
|
||||
dialogContext.message.next("Finished repository startup");
|
||||
}
|
||||
|
||||
private openStartupDialog(repository: Repository): BusyDialogContext {
|
||||
const dialogMessage = new BehaviorSubject<string>(
|
||||
"Opening repository...");
|
||||
let dialog = this.dialog.open(BusyDialogComponent, {
|
||||
data: {
|
||||
title: `Opening repository '${repository.name}'`,
|
||||
message: dialogMessage,
|
||||
allowCancel: true,
|
||||
}, disableClose: true,
|
||||
minWidth: "30%",
|
||||
minHeight: "30%",
|
||||
});
|
||||
dialog.afterClosed().subscribe(async (result) => {
|
||||
if (!result) {
|
||||
await this.forceCloseRepository();
|
||||
}
|
||||
});
|
||||
|
||||
return { message: dialogMessage, dialog };
|
||||
}
|
||||
|
||||
private async checkAndPromptDaemonExecutable() {
|
||||
if (!await this.repoService.checkDameonConfigured()) {
|
||||
const result = await this.dialog.open(
|
||||
DownloadDaemonDialogComponent,
|
||||
{
|
||||
disableClose: true,
|
||||
}
|
||||
).afterClosed().toPromise();
|
||||
if (result) {
|
||||
// recursion avoidance
|
||||
setTimeout(
|
||||
async () => await this.checkAndPromptDaemonExecutable(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue