Merge pull request #6 from Trivernis/fix/file-multiselection-tags

Fix/file multiselection tags
pull/7/head
Julius Riegel 3 years ago committed by GitHub
commit 6d0b5773df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -4,7 +4,7 @@ use mediarepo_core::error::RepoResult;
use mediarepo_database::entities::{namespace, tag}; use mediarepo_database::entities::{namespace, tag};
use sea_orm::prelude::*; use sea_orm::prelude::*;
use sea_orm::sea_query::Expr; use sea_orm::sea_query::Expr;
use sea_orm::Condition; use sea_orm::{Condition, QuerySelect};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct TagByNameQuery { pub struct TagByNameQuery {
@ -30,6 +30,7 @@ impl TagDao {
let tags = tag::Entity::find() let tags = tag::Entity::find()
.find_also_related(namespace::Entity) .find_also_related(namespace::Entity)
.filter(condition) .filter(condition)
.group_by(tag::Column::Id)
.all(&self.ctx.db) .all(&self.ctx.db)
.await? .await?
.into_iter() .into_iter()

@ -70,6 +70,7 @@ impl TagDao {
content_descriptor_tag::Relation::ContentDescriptorId.def(), content_descriptor_tag::Relation::ContentDescriptorId.def(),
) )
.filter(content_descriptor::Column::Descriptor.is_in(cds)) .filter(content_descriptor::Column::Descriptor.is_in(cds))
.group_by(tag::Column::Id)
.all(&self.ctx.db) .all(&self.ctx.db)
.await? .await?
.into_iter() .into_iter()

@ -1,3 +1,5 @@
@import "src/colors";
::ng-deep .mat-button-wrapper > ng-icon { ::ng-deep .mat-button-wrapper > ng-icon {
font-size: 26px; font-size: 26px;
} }
@ -5,3 +7,11 @@
::ng-deep ng-icon { ::ng-deep ng-icon {
font-size: 24px; font-size: 24px;
} }
::ng-deep .app-warn {
background-color: $warn-chill;
}
::ng-deep .app-error {
background-color: $warn;
}

@ -1,7 +1,9 @@
import {Component, OnInit} from "@angular/core"; import {Component, OnInit} from "@angular/core";
import {RepositoryService} from "./services/repository/repository.service"; import {RepositoryService} from "./services/repository/repository.service";
import {MatSnackBar} from "@angular/material/snack-bar"; 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({ @Component({
selector: "app-root", selector: "app-root",
@ -13,21 +15,39 @@ export class AppComponent implements OnInit {
constructor( constructor(
private snackBar: MatSnackBar, private snackBar: MatSnackBar,
private errorBroker: ErrorBrokerService, private logger: LoggingService,
private repoService: RepositoryService, private repoService: RepositoryService,
) { ) {
} }
async ngOnInit() { async ngOnInit() {
this.errorBroker.errorCb = (err: { message: string }) => this.showError( this.logger.logs.subscribe(entry => {
err); this.logEntry(entry);
this.errorBroker.infoCb = (info: string) => this.showInfo(info); 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(); await this.repoService.loadRepositories();
} }
private showError(err: { message: string }) { private showError(err: string) {
this.snackBar.open(err.message, undefined, { this.snackBar.open(err, undefined, {
panelClass: "warn", panelClass: "app-error",
duration: 2000,
});
}
private showWarning(err: string) {
this.snackBar.open(err, undefined, {
panelClass: "app-warn",
duration: 2000, duration: 2000,
}); });
} }
@ -38,4 +58,26 @@ export class AppComponent implements OnInit {
duration: 2000, 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);
}
}
} }

@ -30,6 +30,7 @@ export class FilesTabComponent implements OnInit {
} else { } else {
this.state.selectedCD.next(undefined); this.state.selectedCD.next(undefined);
} }
console.debug(this.selectedFiles);
} }
public getStateSelectedFile(): File | undefined { public getStateSelectedFile(): File | undefined {

@ -6,7 +6,7 @@ import {DownloadDaemonDialogComponent} from "./download-daemon-dialog/download-d
import { import {
AddRepositoryDialogComponent AddRepositoryDialogComponent
} from "../../shared/repository/repository/add-repository-dialog/add-repository-dialog.component"; } 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 {BehaviorSubject} from "rxjs";
import {BusyDialogComponent} from "../../shared/app-common/busy-dialog/busy-dialog.component"; import {BusyDialogComponent} from "../../shared/app-common/busy-dialog/busy-dialog.component";
import {JobService} from "../../../services/job/job.service"; import {JobService} from "../../../services/job/job.service";
@ -24,7 +24,7 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit {
public selectedRepository?: Repository; public selectedRepository?: Repository;
constructor( constructor(
private errorBroker: ErrorBrokerService, private logger: LoggingService,
private repoService: RepositoryService, private repoService: RepositoryService,
private jobService: JobService, private jobService: JobService,
private stateService: StateService, private stateService: StateService,
@ -60,8 +60,8 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit {
}); });
} }
await this.selectRepository(repository, dialogContext); await this.selectRepository(repository, dialogContext);
} catch (err) { } catch (err: any) {
this.errorBroker.showError(err); this.logger.error(err);
} }
} }
@ -75,7 +75,7 @@ export class RepositoriesTabComponent implements OnInit, AfterViewInit {
await this.repoService.loadRepositories(); await this.repoService.loadRepositories();
dialogContext.dialog.close(true); dialogContext.dialog.close(true);
} catch (err: any) { } catch (err: any) {
this.errorBroker.showError(err); this.logger.error(err);
dialogContext.message.next( dialogContext.message.next(
"Failed to open repository: " + err.toString()); "Failed to open repository: " + err.toString());
await this.forceCloseRepository(); await this.forceCloseRepository();

@ -9,7 +9,7 @@ import {BehaviorSubject} from "rxjs";
import {BusyDialogComponent} from "../../app-common/busy-dialog/busy-dialog.component"; import {BusyDialogComponent} from "../../app-common/busy-dialog/busy-dialog.component";
import {ConfirmDialogComponent, ConfirmDialogData} from "../../app-common/confirm-dialog/confirm-dialog.component"; import {ConfirmDialogComponent, ConfirmDialogData} from "../../app-common/confirm-dialog/confirm-dialog.component";
import {MatDialog, MatDialogConfig, MatDialogRef} from "@angular/material/dialog"; 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 = { type ProgressDialogContext = {
dialog: MatDialogRef<BusyDialogComponent>, dialog: MatDialogRef<BusyDialogComponent>,
@ -22,7 +22,7 @@ type ProgressDialogContext = {
template: "<h1>Do not use</h1>", template: "<h1>Do not use</h1>",
}) })
export class FileActionBaseComponent { 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<void> { public async copyFileContentDescriptor(file: File): Promise<void> {

@ -23,13 +23,18 @@ export class BusyDialogComponent {
public progress = 0; public progress = 0;
public mode: ProgressBarMode = "indeterminate"; public mode: ProgressBarMode = "indeterminate";
constructor(public dialogRef: MatDialogRef<BusyDialogComponent>, @Inject(MAT_DIALOG_DATA) data: BusyDialogData) { constructor(
public dialogRef: MatDialogRef<BusyDialogComponent>,
@Inject(MAT_DIALOG_DATA) data: BusyDialogData
) {
this.title = data.title; this.title = data.title;
if (data.message) { if (data.message) {
data.message.subscribe(m => this.message = m); data.message.subscribe(m => this.message = m);
} }
if (data.progress) { if (data.progress) {
data.progress.subscribe(p => this.progress = p); data.progress.subscribe(p => {
this.progress = Math.floor(p * 100);
});
this.mode = "determinate"; this.mode = "determinate";
} }
this.allowCancel = data.allowCancel ?? false; this.allowCancel = data.allowCancel ?? false;

@ -12,7 +12,7 @@ import {SafeResourceUrl} from "@angular/platform-browser";
import {File} from "../../../../../api/models/File"; import {File} from "../../../../../api/models/File";
import {FileService} from "../../../../services/file/file.service"; import {FileService} from "../../../../services/file/file.service";
import {FileHelper} from "../../../../services/file/file.helper"; 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"; import {BusyIndicatorComponent} from "../../app-common/busy-indicator/busy-indicator.component";
type ContentType = "image" | "video" | "audio" | "other"; type ContentType = "image" | "video" | "audio" | "other";
@ -33,7 +33,7 @@ export class ContentViewerComponent implements AfterViewInit, OnChanges, OnDestr
@ViewChild(BusyIndicatorComponent) busyIndicator!: BusyIndicatorComponent; @ViewChild(BusyIndicatorComponent) busyIndicator!: BusyIndicatorComponent;
constructor( constructor(
private errorBroker: ErrorBrokerService, private errorBroker: LoggingService,
private fileService: FileService private fileService: FileService
) { ) {
} }
@ -70,8 +70,8 @@ export class ContentViewerComponent implements AfterViewInit, OnChanges, OnDestr
if (path) { if (path) {
try { try {
await this.fileService.saveFile(this.file, path); await this.fileService.saveFile(this.file, path);
} catch (err) { } catch (err: any) {
this.errorBroker.showError(err); this.errorBroker.error(err);
} }
} }
} }

@ -2,19 +2,11 @@ import {Component, EventEmitter, OnChanges, Output, SimpleChanges, ViewChild} fr
import {File} from "../../../../../api/models/File"; import {File} from "../../../../../api/models/File";
import {ContextMenuComponent} from "../../app-common/context-menu/context-menu.component"; import {ContextMenuComponent} from "../../app-common/context-menu/context-menu.component";
import {FileService} from "../../../../services/file/file.service"; import {FileService} from "../../../../services/file/file.service";
import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service"; import {LoggingService} from "../../../../services/logging/logging.service";
import {MatDialog, MatDialogRef} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
import {BusyDialogComponent} from "../../app-common/busy-dialog/busy-dialog.component";
import {BehaviorSubject} from "rxjs";
import {FileActionBaseComponent} from "../../app-base/file-action-base/file-action-base.component"; import {FileActionBaseComponent} from "../../app-base/file-action-base/file-action-base.component";
import {FileStatus} from "../../../../../api/api-types/files"; import {FileStatus} from "../../../../../api/api-types/files";
type ProgressDialogContext = {
dialog: MatDialogRef<BusyDialogComponent>,
progress: BehaviorSubject<number>,
message: BehaviorSubject<string>,
};
@Component({ @Component({
selector: "app-file-context-menu", selector: "app-file-context-menu",
templateUrl: "./file-context-menu.component.html", templateUrl: "./file-context-menu.component.html",
@ -34,7 +26,7 @@ export class FileContextMenuComponent extends FileActionBaseComponent implements
@Output() fileDeleted = new EventEmitter<File[]>(); @Output() fileDeleted = new EventEmitter<File[]>();
@Output() fileStatusChange = new EventEmitter<File[]>(); @Output() fileStatusChange = new EventEmitter<File[]>();
constructor(fileService: FileService, errorBroker: ErrorBrokerService, dialog: MatDialog) { constructor(fileService: FileService, errorBroker: LoggingService, dialog: MatDialog) {
super(dialog, errorBroker, fileService); super(dialog, errorBroker, fileService);
} }

@ -5,7 +5,7 @@
class="file-gallery-inner"> class="file-gallery-inner">
<cdk-virtual-scroll-viewport #virtualScrollGrid class="file-scroll" itemSize="260" maxBufferPx="2000" <cdk-virtual-scroll-viewport #virtualScrollGrid class="file-scroll" itemSize="260" maxBufferPx="2000"
minBufferPx="500"> minBufferPx="500">
<div *cdkVirtualFor="let rowEntry of partitionedGridEntries; trackByFileRowId"> <div *cdkVirtualFor="let rowEntry of partitionedGridEntries; trackBy: trackByFileRowId">
<div class="file-row"> <div class="file-row">
<app-file-card <app-file-card
(clickEvent)="setSelectedFile($event.entry)" (clickEvent)="setSelectedFile($event.entry)"
@ -18,7 +18,9 @@
</cdk-virtual-scroll-viewport> </cdk-virtual-scroll-viewport>
</div> </div>
<app-file-context-menu #fileContextMenu (fileDeleted)="this.fileDeleted.emit($event)" (fileStatusChange)="this.onFileStatusChange()"> <app-file-context-menu #fileContextMenu
(fileDeleted)="this.fileDeleted.emit($event)"
(fileStatusChange)="this.onFileStatusChange()">
<button (click)="this.fileOpen.emit(fileContextMenu.files[0])" <button (click)="this.fileOpen.emit(fileContextMenu.files[0])"
*ngIf="fileContextMenu.files.length === 1" *ngIf="fileContextMenu.files.length === 1"
content-before="" content-before=""

@ -19,6 +19,7 @@ import {FileService} from "../../../../../services/file/file.service";
import {Selectable} from "../../../../../models/Selectable"; import {Selectable} from "../../../../../models/Selectable";
import {Key} from "w3c-keys"; import {Key} from "w3c-keys";
import {BehaviorSubject} from "rxjs"; import {BehaviorSubject} from "rxjs";
import {LoggingService} from "../../../../../services/logging/logging.service";
@Component({ @Component({
selector: "app-file-grid", selector: "app-file-grid",
@ -48,6 +49,7 @@ export class FileGridComponent implements OnChanges, OnInit, AfterViewInit {
private gridEntries: Selectable<File>[] = []; private gridEntries: Selectable<File>[] = [];
constructor( constructor(
private logger: LoggingService,
private tabService: TabService, private tabService: TabService,
private fileService: FileService, private fileService: FileService,
) { ) {
@ -88,12 +90,14 @@ export class FileGridComponent implements OnChanges, OnInit, AfterViewInit {
this.handleShiftSelect(clickedEntry); this.handleShiftSelect(clickedEntry);
} else { } else {
clickedEntry.selected.next(!clickedEntry.selected.value); clickedEntry.selected.next(!clickedEntry.selected.value);
if (!clickedEntry.selected) { if (!clickedEntry.selected.value) {
this.logger.trace("File wasn't selected");
const index = this.selectedEntries.indexOf(clickedEntry); const index = this.selectedEntries.indexOf(clickedEntry);
if (index > -1) { if (index > -1) {
this.selectedEntries.splice(index, 1); this.selectedEntries.splice(index, 1);
} }
} else { } else {
this.logger.trace("File was selected");
this.selectedEntries.push(clickedEntry); this.selectedEntries.push(clickedEntry);
} }
} }
@ -258,11 +262,13 @@ export class FileGridComponent implements OnChanges, OnInit, AfterViewInit {
private selectAll() { private selectAll() {
this.selectedEntries = this.gridEntries; this.selectedEntries = this.gridEntries;
this.gridEntries.forEach(g => g.select()); this.gridEntries.forEach(g => g.select());
this.fileSelect.emit(this.selectedEntries.map(e => e.data));
} }
private selectNone() { private selectNone() {
this.selectedEntries = []; this.selectedEntries = [];
this.gridEntries.forEach(g => g.unselect()); this.gridEntries.forEach(g => g.unselect());
this.fileSelect.emit([]);
} }
private handleArrowSelect(direction: "up" | "down" | "left" | "right") { private handleArrowSelect(direction: "up" | "down" | "left" | "right") {

@ -4,7 +4,7 @@ import {FileGalleryComponent} from "./file-gallery/file-gallery.component";
import {FileGridComponent} from "./file-grid/file-grid.component"; import {FileGridComponent} from "./file-grid/file-grid.component";
import {FileActionBaseComponent} from "../../app-base/file-action-base/file-action-base.component"; import {FileActionBaseComponent} from "../../app-base/file-action-base/file-action-base.component";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service"; import {LoggingService} from "../../../../services/logging/logging.service";
import {FileService} from "../../../../services/file/file.service"; import {FileService} from "../../../../services/file/file.service";
import {TabState} from "../../../../models/TabState"; import {TabState} from "../../../../models/TabState";
@ -29,7 +29,7 @@ export class FileMultiviewComponent extends FileActionBaseComponent implements A
public selectedFiles: File[] = []; public selectedFiles: File[] = [];
@Input() public preselectedFile: File | undefined; @Input() public preselectedFile: File | undefined;
constructor(dialog: MatDialog, errorBroker: ErrorBrokerService, fileService: FileService) { constructor(dialog: MatDialog, errorBroker: LoggingService, fileService: FileService) {
super(dialog, errorBroker, fileService); super(dialog, errorBroker, fileService);
} }

@ -113,7 +113,7 @@ export class FilterInputComponent implements OnChanges {
private validateFilters(filters: FilterQuery[]): boolean { private validateFilters(filters: FilterQuery[]): boolean {
for (const filter of filters) { for (const filter of filters) {
if ("Tag" in filter && !this.tagsForAutocomplete.includes(filter["Tag"].tag)) { if ("Tag" in filter && !filter.Tag.tag.endsWith("*") && !this.tagsForAutocomplete.includes(filter.Tag.tag)) {
console.debug("tags don't include", filter); console.debug("tags don't include", filter);
return false; return false;
} }

@ -1,14 +1,8 @@
import {Component, Inject, ViewChild} from "@angular/core"; import {Component, Inject, ViewChild} from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import { import {RepositoryFormComponent} from "../repository-form/repository-form.component";
RepositoryFormComponent import {RepositoryService} from "../../../../../services/repository/repository.service";
} from "../repository-form/repository-form.component"; import {LoggingService} from "../../../../../services/logging/logging.service";
import {
RepositoryService
} from "../../../../../services/repository/repository.service";
import {
ErrorBrokerService
} from "../../../../../services/error-broker/error-broker.service";
@Component({ @Component({
selector: "app-add-repository-dialog", selector: "app-add-repository-dialog",
@ -21,9 +15,10 @@ export class AddRepositoryDialogComponent {
constructor( constructor(
public repoService: RepositoryService, public repoService: RepositoryService,
public errorBroker: ErrorBrokerService, public errorBroker: LoggingService,
public dialogRef: MatDialogRef<AddRepositoryDialogComponent>, public dialogRef: MatDialogRef<AddRepositoryDialogComponent>,
@Inject(MAT_DIALOG_DATA) data: any) { @Inject(MAT_DIALOG_DATA) data: any
) {
} }
public async checkLocalRepoExists() { public async checkLocalRepoExists() {
@ -35,22 +30,23 @@ export class AddRepositoryDialogComponent {
const path = this.repositoryForm.formGroup.value.path; const path = this.repositoryForm.formGroup.value.path;
try { try {
await this.repoService.initRepository(path); await this.repoService.initRepository(path);
} catch (err) { } catch (err: any) {
this.errorBroker.showError(err); this.errorBroker.error(err);
} }
await this.checkLocalRepoExists(); await this.checkLocalRepoExists();
} }
public async addRepository() { public async addRepository() {
let {name, repositoryType, path, address} = this.repositoryForm.formGroup.value; let { name, repositoryType, path, address } = this.repositoryForm.formGroup.value;
path = repositoryType === "local" ? path : undefined; path = repositoryType === "local" ? path : undefined;
address = repositoryType === "remote" ? address : undefined; address = repositoryType === "remote" ? address : undefined;
try { try {
await this.repoService.addRepository(name, path, address, await this.repoService.addRepository(name, path, address,
repositoryType === "local"); repositoryType === "local"
);
this.dialogRef.close(); this.dialogRef.close();
} catch (err) { } catch (err: any) {
this.errorBroker.showError(err); this.errorBroker.error(err);
} }
} }

@ -1,13 +1,7 @@
import {Component, Inject, ViewChild} from "@angular/core"; import {Component, Inject, ViewChild} from "@angular/core";
import { import {RepositoryFormComponent} from "../repository-form/repository-form.component";
RepositoryFormComponent import {RepositoryService} from "../../../../../services/repository/repository.service";
} from "../repository-form/repository-form.component"; import {LoggingService} from "../../../../../services/logging/logging.service";
import {
RepositoryService
} from "../../../../../services/repository/repository.service";
import {
ErrorBrokerService
} from "../../../../../services/error-broker/error-broker.service";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog"; import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {Repository} from "../../../../../../api/models/Repository"; import {Repository} from "../../../../../../api/models/Repository";
@ -25,9 +19,10 @@ export class EditRepositoryDialogComponent {
constructor( constructor(
public repoService: RepositoryService, public repoService: RepositoryService,
public errorBroker: ErrorBrokerService, public errorBroker: LoggingService,
public dialogRef: MatDialogRef<EditRepositoryDialogComponent>, public dialogRef: MatDialogRef<EditRepositoryDialogComponent>,
@Inject(MAT_DIALOG_DATA) data: any) { @Inject(MAT_DIALOG_DATA) data: any
) {
this.selectedRepository = data.repository; this.selectedRepository = data.repository;
this.originalName = this.selectedRepository.name; this.originalName = this.selectedRepository.name;
} }
@ -41,14 +36,14 @@ export class EditRepositoryDialogComponent {
const path = this.repositoryForm.formGroup.value.path; const path = this.repositoryForm.formGroup.value.path;
try { try {
await this.repoService.initRepository(path); await this.repoService.initRepository(path);
} catch (err) { } catch (err: any) {
this.errorBroker.showError(err); this.errorBroker.error(err);
} }
await this.checkLocalRepoExists(); await this.checkLocalRepoExists();
} }
public async addRepository() { public async addRepository() {
let {name, repositoryType, path, address} = this.repositoryForm.formGroup.value; let { name, repositoryType, path, address } = this.repositoryForm.formGroup.value;
path = repositoryType === "local" ? path : undefined; path = repositoryType === "local" ? path : undefined;
address = repositoryType === "remote" ? address : undefined; address = repositoryType === "remote" ? address : undefined;
@ -61,12 +56,13 @@ export class EditRepositoryDialogComponent {
await this.repoService.removeRepository(this.originalName); await this.repoService.removeRepository(this.originalName);
} }
await this.repoService.addRepository(name, path, address, await this.repoService.addRepository(name, path, address,
repositoryType === "local"); repositoryType === "local"
this.selectedRepository.update({name, local: repositoryType === "local", path, address}); );
this.selectedRepository.update({ name, local: repositoryType === "local", path, address });
this.dialogRef.close(); this.dialogRef.close();
} catch (err) { } catch (err: any) {
this.errorBroker.showError(err); this.errorBroker.error(err);
} }
} }

@ -1,6 +1,6 @@
import {Component, EventEmitter, Output} from "@angular/core"; import {Component, EventEmitter, Output} from "@angular/core";
import {ImportService} from "../../../../../services/import/import.service"; import {ImportService} from "../../../../../services/import/import.service";
import {ErrorBrokerService} from "../../../../../services/error-broker/error-broker.service"; import {LoggingService} from "../../../../../services/logging/logging.service";
import {AddFileOptions} from "../../../../../models/AddFileOptions"; import {AddFileOptions} from "../../../../../models/AddFileOptions";
import {File} from "../../../../../../api/models/File"; import {File} from "../../../../../../api/models/File";
import {DialogFilter} from "@tauri-apps/api/dialog"; import {DialogFilter} from "@tauri-apps/api/dialog";
@ -36,7 +36,7 @@ export class FilesystemImportComponent {
public importing = false; public importing = false;
public importingProgress = 0; public importingProgress = 0;
constructor(private errorBroker: ErrorBrokerService, private importService: ImportService) { constructor(private errorBroker: LoggingService, private importService: ImportService) {
} }
public async setSelectedPaths(paths: string[]) { public async setSelectedPaths(paths: string[]) {
@ -44,9 +44,9 @@ export class FilesystemImportComponent {
try { try {
this.files = await this.importService.resolvePathsToFiles(paths); this.files = await this.importService.resolvePathsToFiles(paths);
this.fileCount = this.files.length; this.fileCount = this.files.length;
} catch (err) { } catch (err: any) {
console.log(err); console.log(err);
this.errorBroker.showError(err); this.errorBroker.error(err);
} }
this.resolving = false; this.resolving = false;
} }
@ -64,9 +64,9 @@ export class FilesystemImportComponent {
this.importOptions this.importOptions
); );
this.fileImported.emit(resultFile); this.fileImported.emit(resultFile);
} catch (err) { } catch (err: any) {
console.log(err); console.log(err);
this.errorBroker.showError(err); this.errorBroker.error(err);
} }
count++; count++;
this.importingProgress = (count / this.fileCount) * 100; this.importingProgress = (count / this.fileCount) * 100;

@ -12,7 +12,7 @@ import {
import {SortKey} from "../../../../models/SortKey"; import {SortKey} from "../../../../models/SortKey";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
import {SortDialogComponent} from "./sort-dialog/sort-dialog.component"; import {SortDialogComponent} from "./sort-dialog/sort-dialog.component";
import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service"; import {LoggingService} from "../../../../services/logging/logging.service";
import {FilterDialogComponent} from "./filter-dialog/filter-dialog.component"; import {FilterDialogComponent} from "./filter-dialog/filter-dialog.component";
import {Tag} from "../../../../../api/models/Tag"; import {Tag} from "../../../../../api/models/Tag";
import {clipboard} from "@tauri-apps/api"; import {clipboard} from "@tauri-apps/api";
@ -57,7 +57,7 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
private needsScroll = false; private needsScroll = false;
constructor( constructor(
private errorBroker: ErrorBrokerService, private logger: LoggingService,
public dialog: MatDialog public dialog: MatDialog
) { ) {
this.assignDisplayedFilters(); this.assignDisplayedFilters();
@ -85,8 +85,8 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
this.searchStartEvent.emit(); this.searchStartEvent.emit();
try { try {
await this.state.findFiles(); await this.state.findFiles();
} catch (err) { } catch (err: any) {
this.errorBroker.showError(err); this.logger.error(err);
} }
this.searchEndEvent.emit(); this.searchEndEvent.emit();
} }

@ -13,7 +13,7 @@ import {File} from "../../../../../api/models/File";
import {Tag} from "../../../../../api/models/Tag"; import {Tag} from "../../../../../api/models/Tag";
import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling"; import {CdkVirtualScrollViewport} from "@angular/cdk/scrolling";
import {TagService} from "../../../../services/tag/tag.service"; import {TagService} from "../../../../services/tag/tag.service";
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"; import {BusyIndicatorComponent} from "../../app-common/busy-indicator/busy-indicator.component";
@Component({ @Component({
@ -36,7 +36,7 @@ export class TagEditComponent implements AfterViewInit, OnChanges {
private fileTags: { [key: number]: Tag[] } = {}; private fileTags: { [key: number]: Tag[] } = {};
constructor( constructor(
private errorBroker: ErrorBrokerService, private errorBroker: LoggingService,
private tagService: TagService, private tagService: TagService,
) { ) {
} }
@ -180,7 +180,7 @@ export class TagEditComponent implements AfterViewInit, OnChanges {
try { try {
return cb(); return cb();
} catch (err: any) { } catch (err: any) {
this.errorBroker.showError(err); this.errorBroker.error(err);
return undefined; return undefined;
} }
} else { } else {

@ -1,53 +0,0 @@
import {Injectable} from "@angular/core";
import {listen} from "@tauri-apps/api/event";
@Injectable({
providedIn: "root"
})
export class ErrorBrokerService {
errorCb: Function | undefined;
infoCb: Function | undefined;
constructor() {
this.registerListener().catch(err => console.error(err));
}
async registerListener() {
const _unlisten = await listen("error", event => {
const payload: any = event.payload;
if (payload.message) {
this.showError(payload);
} else {
this.showError(payload.toString());
}
});
}
async try<T>(fn: () => Promise<T>): Promise<T | undefined> {
try {
return await fn();
} catch (err) {
this.showError(err);
return;
}
}
showInfo(info: string) {
console.log(info);
if (this.infoCb) {
this.infoCb(info);
}
}
showError(error: { message: string } | any) {
console.error(error);
if (this.errorCb) {
if (!error.message) {
this.errorCb({ message: error });
} else {
this.errorCb({ ...error });
}
}
}
}

@ -0,0 +1,24 @@
export enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
}
export class LogEntry {
constructor(private message: string, private level: LogLevel, private error?: Error) {
}
public getMessage(): string {
return this.message;
}
public getLevel(): LogLevel {
return this.level;
}
public getError(): Error | undefined {
return this.error;
}
}

@ -1,13 +1,13 @@
import {TestBed} from "@angular/core/testing"; import {TestBed} from "@angular/core/testing";
import {ErrorBrokerService} from "./error-broker.service"; import {LoggingService} from "./logging.service";
describe("ErrorBrokerService", () => { describe("ErrorBrokerService", () => {
let service: ErrorBrokerService; let service: LoggingService;
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({}); TestBed.configureTestingModule({});
service = TestBed.inject(ErrorBrokerService); service = TestBed.inject(LoggingService);
}); });
it("should be created", () => { it("should be created", () => {

@ -0,0 +1,60 @@
import {Injectable} from "@angular/core";
import {listen} from "@tauri-apps/api/event";
import {BehaviorSubject} from "rxjs";
import {LogEntry, LogLevel} from "./LogEntry";
@Injectable({
providedIn: "root"
})
export class LoggingService {
logs = new BehaviorSubject<LogEntry>(new LogEntry("Log initialized", LogLevel.Trace));
constructor() {
this.registerListener().catch(err => console.error(err));
}
async registerListener() {
const _unlisten = await listen("error", event => {
const payload: any = event.payload;
if (payload.message) {
this.error(payload);
} else {
this.error(payload.toString());
}
});
}
async try<T>(fn: () => Promise<T>): Promise<T | undefined> {
try {
return await fn();
} catch (err: any) {
this.error(err);
return;
}
}
trace(message: string) {
this.log(LogLevel.Trace, message);
}
debug(message: string) {
this.log(LogLevel.Debug, message);
}
info(message: string) {
this.log(LogLevel.Info, message);
}
warn(message: string) {
this.log(LogLevel.Warn, message);
}
error(error: Error, message?: string) {
this.log(LogLevel.Error, message ?? error.message ?? error.toString(), error);
}
public log(level: LogLevel, message: string, error?: Error) {
this.logs.next(new LogEntry(message, level, error));
}
}

@ -3,7 +3,7 @@ import {Repository} from "../../../api/models/Repository";
import {BehaviorSubject} from "rxjs"; import {BehaviorSubject} from "rxjs";
import {listen} from "@tauri-apps/api/event"; import {listen} from "@tauri-apps/api/event";
import {Info} from "../../models/Info"; import {Info} from "../../models/Info";
import {ErrorBrokerService} from "../error-broker/error-broker.service"; import {LoggingService} from "../logging/logging.service";
import {RepositoryMetadata} from "../../models/RepositoryMetadata"; import {RepositoryMetadata} from "../../models/RepositoryMetadata";
import {MediarepoApi} from "../../../api/Api"; import {MediarepoApi} from "../../../api/Api";
import {mapMany, mapNew, mapOptional,} from "../../../api/models/adaptors"; import {mapMany, mapNew, mapOptional,} from "../../../api/models/adaptors";
@ -17,7 +17,7 @@ export class RepositoryService {
public selectedRepository = new BehaviorSubject<Repository | undefined>( public selectedRepository = new BehaviorSubject<Repository | undefined>(
undefined); undefined);
constructor(private errorBroker: ErrorBrokerService) { constructor(private errorBroker: LoggingService) {
this.registerListener().catch(err => console.error(err)); this.registerListener().catch(err => console.error(err));
} }
@ -25,7 +25,7 @@ export class RepositoryService {
async registerListener() { async registerListener() {
await listen("info", (event: { payload: Info }) => { await listen("info", (event: { payload: Info }) => {
const message = `Connected to ${event.payload.name}, Version: ${event.payload.version}`; const message = `Connected to ${event.payload.name}, Version: ${event.payload.version}`;
this.errorBroker.showInfo(message); this.errorBroker.info(message);
}); });
} }
@ -68,7 +68,7 @@ export class RepositoryService {
console.warn(err); console.warn(err);
} }
} }
await MediarepoApi.selectRepository({name: repo.name}); await MediarepoApi.selectRepository({ name: repo.name });
} }
/** /**
@ -98,7 +98,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async addRepository(name: string, path: string | undefined, address: string | undefined, local: boolean) { public async addRepository(name: string, path: string | undefined, address: string | undefined, local: boolean) {
let repos = await MediarepoApi.addRepository({name, path, address, local}).then(mapMany(mapNew(Repository))); let repos = await MediarepoApi.addRepository({ name, path, address, local }).then(mapMany(mapNew(Repository)));
this.repositories.next(repos); this.repositories.next(repos);
} }
@ -108,7 +108,7 @@ export class RepositoryService {
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
public async checkDaemonRunning(address: string): Promise<boolean> { public async checkDaemonRunning(address: string): Promise<boolean> {
return MediarepoApi.checkDaemonRunning({address}); return MediarepoApi.checkDaemonRunning({ address });
} }
/** /**
@ -117,7 +117,7 @@ export class RepositoryService {
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
public async checkLocalRepositoryExists(path: string): Promise<boolean> { public async checkLocalRepositoryExists(path: string): Promise<boolean> {
return await MediarepoApi.checkLocalRepositoryExists({path}); return await MediarepoApi.checkLocalRepositoryExists({ path });
} }
/** /**
@ -126,7 +126,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async removeRepository(name: string): Promise<void> { public async removeRepository(name: string): Promise<void> {
await MediarepoApi.removeRepository({name}); await MediarepoApi.removeRepository({ name });
await this.loadRepositories(); await this.loadRepositories();
} }
@ -136,7 +136,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async deleteRepository(name: string): Promise<void> { public async deleteRepository(name: string): Promise<void> {
await MediarepoApi.deleteRepository({name}); await MediarepoApi.deleteRepository({ name });
await this.removeRepository(name); await this.removeRepository(name);
} }
@ -146,7 +146,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async startDaemon(repoPath: string): Promise<void> { public async startDaemon(repoPath: string): Promise<void> {
return MediarepoApi.startDaemon({repoPath}); return MediarepoApi.startDaemon({ repoPath });
} }
/** /**
@ -155,7 +155,7 @@ export class RepositoryService {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async initRepository(repoPath: string): Promise<void> { public async initRepository(repoPath: string): Promise<void> {
return MediarepoApi.initRepository({repoPath}); return MediarepoApi.initRepository({ repoPath });
} }
/** /**
@ -172,7 +172,7 @@ export class RepositoryService {
* @param sizeType * @param sizeType
*/ */
public async getSize(sizeType: SizeType): Promise<SizeMetadata> { public async getSize(sizeType: SizeType): Promise<SizeMetadata> {
return MediarepoApi.getSize({sizeType}); return MediarepoApi.getSize({ sizeType });
} }
async loadSelectedRepository() { async loadSelectedRepository() {

@ -13,6 +13,10 @@ $primary-darker-30: darken($primary, 30%);
$accent: mat.get-color-from-palette($accent-pallette, 'text'); $accent: mat.get-color-from-palette($accent-pallette, 'text');
$accent-lighter-10: lighten($accent, 10%); $accent-lighter-10: lighten($accent, 10%);
$warn: mat.get-color-from-palette($warn-palette);
$warn-lighter: lighten($warn, 10%);
$warn-chill: #fcd349;
$text: #FFF; $text: #FFF;
$text-darker-10: darken($text, 10%); $text-darker-10: darken($text, 10%);

Loading…
Cancel
Save