diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/tag/by_name.rs b/mediarepo-daemon/mediarepo-logic/src/dao/tag/by_name.rs index de7e4c9..1ae99bc 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/tag/by_name.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/tag/by_name.rs @@ -4,7 +4,7 @@ use mediarepo_core::error::RepoResult; use mediarepo_database::entities::{namespace, tag}; use sea_orm::prelude::*; use sea_orm::sea_query::Expr; -use sea_orm::Condition; +use sea_orm::{Condition, QuerySelect}; #[derive(Clone, Debug)] pub struct TagByNameQuery { @@ -30,6 +30,7 @@ impl TagDao { let tags = tag::Entity::find() .find_also_related(namespace::Entity) .filter(condition) + .group_by(tag::Column::Id) .all(&self.ctx.db) .await? .into_iter() diff --git a/mediarepo-daemon/mediarepo-logic/src/dao/tag/mod.rs b/mediarepo-daemon/mediarepo-logic/src/dao/tag/mod.rs index bd4a6e9..9b8e142 100644 --- a/mediarepo-daemon/mediarepo-logic/src/dao/tag/mod.rs +++ b/mediarepo-daemon/mediarepo-logic/src/dao/tag/mod.rs @@ -70,6 +70,7 @@ impl TagDao { content_descriptor_tag::Relation::ContentDescriptorId.def(), ) .filter(content_descriptor::Column::Descriptor.is_in(cds)) + .group_by(tag::Column::Id) .all(&self.ctx.db) .await? .into_iter() diff --git a/mediarepo-ui/src/app/app.component.scss b/mediarepo-ui/src/app/app.component.scss index 8b79f48..a48a9a2 100644 --- a/mediarepo-ui/src/app/app.component.scss +++ b/mediarepo-ui/src/app/app.component.scss @@ -1,3 +1,5 @@ +@import "src/colors"; + ::ng-deep .mat-button-wrapper > ng-icon { font-size: 26px; } @@ -5,3 +7,11 @@ ::ng-deep ng-icon { font-size: 24px; } + +::ng-deep .app-warn { + background-color: $warn-chill; +} + +::ng-deep .app-error { + background-color: $warn; +} diff --git a/mediarepo-ui/src/app/app.component.ts b/mediarepo-ui/src/app/app.component.ts index 3885fcc..af0801f 100644 --- a/mediarepo-ui/src/app/app.component.ts +++ b/mediarepo-ui/src/app/app.component.ts @@ -1,7 +1,9 @@ import {Component, OnInit} from "@angular/core"; import {RepositoryService} from "./services/repository/repository.service"; import {MatSnackBar} from "@angular/material/snack-bar"; -import {ErrorBrokerService} from "./services/error-broker/error-broker.service"; +import {LoggingService} from "./services/logging/logging.service"; +import {LogEntry, LogLevel} from "./services/logging/LogEntry"; +import {environment} from "../environments/environment"; @Component({ selector: "app-root", @@ -13,21 +15,39 @@ export class AppComponent implements OnInit { constructor( private snackBar: MatSnackBar, - private errorBroker: ErrorBrokerService, + private logger: LoggingService, private repoService: RepositoryService, ) { } async ngOnInit() { - this.errorBroker.errorCb = (err: { message: string }) => this.showError( - err); - this.errorBroker.infoCb = (info: string) => this.showInfo(info); + this.logger.logs.subscribe(entry => { + this.logEntry(entry); + switch (entry.getLevel()) { + case LogLevel.Info: + this.showInfo(entry.getMessage()); + break; + case LogLevel.Warn: + this.showWarning(entry.getMessage()); + break; + case LogLevel.Error: + this.showError(entry.getMessage()); + break; + } + }); await this.repoService.loadRepositories(); } - private showError(err: { message: string }) { - this.snackBar.open(err.message, undefined, { - panelClass: "warn", + private showError(err: string) { + this.snackBar.open(err, undefined, { + panelClass: "app-error", + duration: 2000, + }); + } + + private showWarning(err: string) { + this.snackBar.open(err, undefined, { + panelClass: "app-warn", duration: 2000, }); } @@ -38,4 +58,26 @@ export class AppComponent implements OnInit { duration: 2000, }); } + + private logEntry(entry: LogEntry) { + if (!environment.production) { + switch (entry.getLevel()) { + case LogLevel.Trace: + console.trace(entry.getMessage(), entry); + break; + case LogLevel.Debug: + console.debug(entry.getMessage(), entry); + break; + case LogLevel.Info: + console.info(entry.getMessage(), entry); + break; + case LogLevel.Warn: + console.warn(entry.getMessage(), entry); + break; + } + } + if (entry.getLevel() == LogLevel.Error) { + console.error(entry.getMessage(), entry.getError(), entry); + } + } } diff --git a/mediarepo-ui/src/app/components/core/files-tab/files-tab.component.ts b/mediarepo-ui/src/app/components/core/files-tab/files-tab.component.ts index a64a857..7a588f3 100644 --- a/mediarepo-ui/src/app/components/core/files-tab/files-tab.component.ts +++ b/mediarepo-ui/src/app/components/core/files-tab/files-tab.component.ts @@ -30,6 +30,7 @@ export class FilesTabComponent implements OnInit { } else { this.state.selectedCD.next(undefined); } + console.debug(this.selectedFiles); } public getStateSelectedFile(): File | undefined { 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 c98d9ae..cd88052 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 @@ -6,7 +6,7 @@ import {DownloadDaemonDialogComponent} from "./download-daemon-dialog/download-d import { AddRepositoryDialogComponent } from "../../shared/repository/repository/add-repository-dialog/add-repository-dialog.component"; -import {ErrorBrokerService} from "../../../services/error-broker/error-broker.service"; +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"; @@ -24,7 +24,7 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit { public selectedRepository?: Repository; constructor( - private errorBroker: ErrorBrokerService, + private logger: LoggingService, private repoService: RepositoryService, private jobService: JobService, private stateService: StateService, @@ -60,8 +60,8 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit { }); } await this.selectRepository(repository, dialogContext); - } catch (err) { - this.errorBroker.showError(err); + } catch (err: any) { + this.logger.error(err); } } @@ -75,7 +75,7 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit { await this.repoService.loadRepositories(); dialogContext.dialog.close(true); } catch (err: any) { - this.errorBroker.showError(err); + this.logger.error(err); dialogContext.message.next( "Failed to open repository: " + err.toString()); await this.forceCloseRepository(); diff --git a/mediarepo-ui/src/app/components/shared/app-base/file-action-base/file-action-base.component.ts b/mediarepo-ui/src/app/components/shared/app-base/file-action-base/file-action-base.component.ts index 1063fc6..e912a41 100644 --- a/mediarepo-ui/src/app/components/shared/app-base/file-action-base/file-action-base.component.ts +++ b/mediarepo-ui/src/app/components/shared/app-base/file-action-base/file-action-base.component.ts @@ -9,7 +9,7 @@ import {BehaviorSubject} from "rxjs"; import {BusyDialogComponent} from "../../app-common/busy-dialog/busy-dialog.component"; import {ConfirmDialogComponent, ConfirmDialogData} from "../../app-common/confirm-dialog/confirm-dialog.component"; import {MatDialog, MatDialogConfig, MatDialogRef} from "@angular/material/dialog"; -import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service"; +import {LoggingService} from "../../../../services/logging/logging.service"; type ProgressDialogContext = { dialog: MatDialogRef, @@ -22,7 +22,7 @@ type ProgressDialogContext = { template: "

Do not use

", }) export class FileActionBaseComponent { - constructor(private dialog: MatDialog, private errorBroker: ErrorBrokerService, private fileService: FileService) { + constructor(private dialog: MatDialog, private errorBroker: LoggingService, private fileService: FileService) { } public async copyFileContentDescriptor(file: File): Promise { diff --git a/mediarepo-ui/src/app/components/shared/app-common/busy-dialog/busy-dialog.component.ts b/mediarepo-ui/src/app/components/shared/app-common/busy-dialog/busy-dialog.component.ts index 2e330b4..d422f31 100644 --- a/mediarepo-ui/src/app/components/shared/app-common/busy-dialog/busy-dialog.component.ts +++ b/mediarepo-ui/src/app/components/shared/app-common/busy-dialog/busy-dialog.component.ts @@ -23,13 +23,18 @@ export class BusyDialogComponent { public progress = 0; public mode: ProgressBarMode = "indeterminate"; - constructor(public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) data: BusyDialogData) { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) data: BusyDialogData + ) { this.title = data.title; if (data.message) { data.message.subscribe(m => this.message = m); } if (data.progress) { - data.progress.subscribe(p => this.progress = p); + data.progress.subscribe(p => { + this.progress = Math.floor(p * 100); + }); this.mode = "determinate"; } this.allowCancel = data.allowCancel ?? false; diff --git a/mediarepo-ui/src/app/components/shared/file/content-viewer/content-viewer.component.ts b/mediarepo-ui/src/app/components/shared/file/content-viewer/content-viewer.component.ts index 2f52ce5..cc83300 100644 --- a/mediarepo-ui/src/app/components/shared/file/content-viewer/content-viewer.component.ts +++ b/mediarepo-ui/src/app/components/shared/file/content-viewer/content-viewer.component.ts @@ -12,7 +12,7 @@ import {SafeResourceUrl} from "@angular/platform-browser"; import {File} from "../../../../../api/models/File"; import {FileService} from "../../../../services/file/file.service"; import {FileHelper} from "../../../../services/file/file.helper"; -import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service"; +import {LoggingService} from "../../../../services/logging/logging.service"; import {BusyIndicatorComponent} from "../../app-common/busy-indicator/busy-indicator.component"; type ContentType = "image" | "video" | "audio" | "other"; @@ -33,7 +33,7 @@ export class ContentViewerComponent implements AfterViewInit, OnChanges, OnDestr @ViewChild(BusyIndicatorComponent) busyIndicator!: BusyIndicatorComponent; constructor( - private errorBroker: ErrorBrokerService, + private errorBroker: LoggingService, private fileService: FileService ) { } @@ -70,8 +70,8 @@ export class ContentViewerComponent implements AfterViewInit, OnChanges, OnDestr if (path) { try { await this.fileService.saveFile(this.file, path); - } catch (err) { - this.errorBroker.showError(err); + } catch (err: any) { + this.errorBroker.error(err); } } } diff --git a/mediarepo-ui/src/app/components/shared/file/file-context-menu/file-context-menu.component.ts b/mediarepo-ui/src/app/components/shared/file/file-context-menu/file-context-menu.component.ts index 83040bc..90a2151 100644 --- a/mediarepo-ui/src/app/components/shared/file/file-context-menu/file-context-menu.component.ts +++ b/mediarepo-ui/src/app/components/shared/file/file-context-menu/file-context-menu.component.ts @@ -2,19 +2,11 @@ import {Component, EventEmitter, OnChanges, Output, SimpleChanges, ViewChild} fr import {File} from "../../../../../api/models/File"; import {ContextMenuComponent} from "../../app-common/context-menu/context-menu.component"; import {FileService} from "../../../../services/file/file.service"; -import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service"; -import {MatDialog, MatDialogRef} from "@angular/material/dialog"; -import {BusyDialogComponent} from "../../app-common/busy-dialog/busy-dialog.component"; -import {BehaviorSubject} from "rxjs"; +import {LoggingService} from "../../../../services/logging/logging.service"; +import {MatDialog} from "@angular/material/dialog"; import {FileActionBaseComponent} from "../../app-base/file-action-base/file-action-base.component"; import {FileStatus} from "../../../../../api/api-types/files"; -type ProgressDialogContext = { - dialog: MatDialogRef, - progress: BehaviorSubject, - message: BehaviorSubject, -}; - @Component({ selector: "app-file-context-menu", templateUrl: "./file-context-menu.component.html", @@ -34,7 +26,7 @@ export class FileContextMenuComponent extends FileActionBaseComponent implements @Output() fileDeleted = new EventEmitter(); @Output() fileStatusChange = new EventEmitter(); - constructor(fileService: FileService, errorBroker: ErrorBrokerService, dialog: MatDialog) { + constructor(fileService: FileService, errorBroker: LoggingService, dialog: MatDialog) { super(dialog, errorBroker, fileService); } diff --git a/mediarepo-ui/src/app/components/shared/file/file-multiview/file-grid/file-grid.component.html b/mediarepo-ui/src/app/components/shared/file/file-multiview/file-grid/file-grid.component.html index 13dafcc..be22ac3 100644 --- a/mediarepo-ui/src/app/components/shared/file/file-multiview/file-grid/file-grid.component.html +++ b/mediarepo-ui/src/app/components/shared/file/file-multiview/file-grid/file-grid.component.html @@ -5,7 +5,7 @@ class="file-gallery-inner"> -
+
- +