diff --git a/mediarepo-ui/src/app/components/core/core.module.ts b/mediarepo-ui/src/app/components/core/core.module.ts index a514aba..067bb3a 100644 --- a/mediarepo-ui/src/app/components/core/core.module.ts +++ b/mediarepo-ui/src/app/components/core/core.module.ts @@ -40,6 +40,7 @@ import { RepositoryDetailsViewComponent } from "./repositories-tab/repository-details-view/repository-details-view.component"; import { EmptyTabComponent } from './empty-tab/empty-tab.component'; +import { RepositoryOverviewComponent } from './repositories-tab/repository-overview/repository-overview.component'; @NgModule({ @@ -54,6 +55,7 @@ import { EmptyTabComponent } from './empty-tab/empty-tab.component'; DownloadDaemonDialogComponent, RepositoryDetailsViewComponent, EmptyTabComponent, + RepositoryOverviewComponent, ], exports: [ CoreComponent, diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.html b/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.html index 48622cd..4093b2a 100644 --- a/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.html +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.html @@ -1,18 +1,6 @@ -
-
- -
-
-
- -
- -

There are no repositories yet. You can create a repository or add an existing one.

- -
-
+
+
-
- +
+
diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.scss b/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.scss index 4e2ab92..2e3c1d6 100644 --- a/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.scss +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.scss @@ -1,41 +1,3 @@ -.repository-container { - margin: 1em; -} - -.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: flex; - flex-direction: column; - overflow-y: auto; - height: calc(100% - 5em); -} - -app-repository-card { - position: relative; -} - -app-repository-details-view, .repo-details { +app-repository-details-view { height: 100%; } - -.add-repository-prompt { - button { - font-size: 1.5em; - padding: 0.5em 1em; - border-radius: 0.5em; - } -} diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.ts b/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.ts index 3c4f421..4b17e68 100644 --- a/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.ts +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repositories-tab.component.ts @@ -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, dialog: MatDialogRef }; @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); - } +export class RepositoriesTabComponent { - 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 { - 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( - "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); } } diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.html b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.html new file mode 100644 index 0000000..6b9d51f --- /dev/null +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.html @@ -0,0 +1,15 @@ +
+
+ +
+
+
+ +
+ +

There are no repositories yet. You can create a repository or add an existing one.

+ +
+
+
diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.scss b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.scss new file mode 100644 index 0000000..4f97bb8 --- /dev/null +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.scss @@ -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; + } +} diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.spec.ts b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.spec.ts new file mode 100644 index 0000000..89b6b32 --- /dev/null +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.spec.ts @@ -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; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RepositoryOverviewComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RepositoryOverviewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.ts b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.ts new file mode 100644 index 0000000..3b109f0 --- /dev/null +++ b/mediarepo-ui/src/app/components/core/repositories-tab/repository-overview/repository-overview.component.ts @@ -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, dialog: MatDialogRef }; + +@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 { + 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( + "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); + } + } + } +}