Cleanup code

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 9773444d7f
commit 60faf81187

@ -1,11 +1,7 @@
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {NgIconsModule} from "@ng-icons/core";
import * as materialIcons from "@ng-icons/material-icons";
import {CoreModule} from "./components/core/core.module";
@NgModule({

@ -20,8 +20,8 @@ export class CoreComponent implements OnInit {
constructor(
private tabService: TabService,
private repoService: RepositoryService,
private tagService: TagService)
{}
private tagService: TagService) {
}
public async ngOnInit(): Promise<void> {
this.selectedRepository = this.repoService.selectedRepository.getValue();

@ -7,34 +7,14 @@ import {FilesTabSidebarComponent} from "./files-tab/files-tab-sidebar/files-tab-
import {ImportTabComponent} from "./import-tab/import-tab.component";
import {ImportTabSidebarComponent} from "./import-tab/import-tab-sidebar/import-tab-sidebar.component";
import {FilesystemImportComponent} from "./import-tab/import-tab-sidebar/filesystem-import/filesystem-import.component";
import {MatCardModule} from "@angular/material/card";
import {MatListModule} from "@angular/material/list";
import {MatButtonModule} from "@angular/material/button";
import {MatToolbarModule} from "@angular/material/toolbar";
import {MatSnackBarModule} from "@angular/material/snack-bar";
import {MatFormFieldModule} from "@angular/material/form-field";
import {MatInputModule} from "@angular/material/input";
import {ReactiveFormsModule} from "@angular/forms";
import {MatSidenavModule} from "@angular/material/sidenav";
import {MatGridListModule} from "@angular/material/grid-list";
import {MatProgressBarModule} from "@angular/material/progress-bar";
import {MatPaginatorModule} from "@angular/material/paginator";
import {ScrollingModule} from "@angular/cdk/scrolling";
import {MatChipsModule} from "@angular/material/chips";
import {MatAutocompleteModule} from "@angular/material/autocomplete";
import {MatTabsModule} from "@angular/material/tabs";
import {FlexModule, GridModule} from "@angular/flex-layout";
import {MatOptionModule, MatRippleModule} from "@angular/material/core";
import {MatDialogModule} from "@angular/material/dialog";
import {FlexModule} from "@angular/flex-layout";
import {MatOptionModule} from "@angular/material/core";
import {MatSelectModule} from "@angular/material/select";
import {MatProgressSpinnerModule} from "@angular/material/progress-spinner";
import {BlockUIModule} from "primeng/blockui";
import {PanelModule} from "primeng/panel";
import {DragDropModule} from "@angular/cdk/drag-drop";
import {MatSliderModule} from "@angular/material/slider";
import {MatTooltipModule} from "@angular/material/tooltip";
import {MatMenuModule} from "@angular/material/menu";
import {MatExpansionModule} from "@angular/material/expansion";
import {MatCheckboxModule} from "@angular/material/checkbox";
import {SharedModule} from "../shared/shared.module";
import {MatDividerModule} from "@angular/material/divider";

@ -14,23 +14,28 @@
</div>
<div class="file-tag-list" fxFlex fxFlexAlign="start" fxFlexFill>
<cdk-virtual-scroll-viewport itemSize="50" maxBufferPx="4000" minBufferPx="500">
<div (click)="addSearchTag(tag)" *cdkVirtualFor="let tag of tags" class="selectable-tag" matRipple
(contextmenu)="contextMenuTag = tag; contextMenu.onContextMenu($event)">
<div (click)="addSearchTag(tag)" (contextmenu)="contextMenuTag = tag; contextMenu.onContextMenu($event)" *cdkVirtualFor="let tag of tags" class="selectable-tag"
matRipple>
<app-tag-item [tag]="tag"></app-tag-item>
</div>
</cdk-virtual-scroll-viewport>
</div>
</div>
</mat-tab>
<mat-tab label="Edit" *ngIf="this.selectedFiles.length > 0">
<mat-tab *ngIf="this.selectedFiles.length > 0" label="Edit">
<app-file-edit #fileedit [files]="this.selectedFiles"></app-file-edit>
</mat-tab>
</mat-tab-group>
</div>
<app-context-menu #contextMenu>
<button *ngIf="this.contextMenuTag" mat-menu-item (click)="this.copyToClipboard(this.contextMenuTag!.getNormalizedOutput())">Copy "{{contextMenuTag!.getNormalizedOutput()}}"</button>
<button *ngIf="this.contextMenuTag?.namespace" mat-menu-item (click)="this.copyToClipboard(this.contextMenuTag!.name)">Copy "{{this.contextMenuTag!.name}}"</button>
<button *ngIf="this.contextMenuTag?.namespace" mat-menu-item
(click)="this.copyToClipboard(this.contextMenuTag!.namespace!)">Copy "{{this.contextMenuTag!.namespace!}}"
<button (click)="this.copyToClipboard(this.contextMenuTag!.getNormalizedOutput())" *ngIf="this.contextMenuTag"
mat-menu-item>Copy
"{{contextMenuTag!.getNormalizedOutput()}}"
</button>
<button (click)="this.copyToClipboard(this.contextMenuTag!.name)" *ngIf="this.contextMenuTag?.namespace"
mat-menu-item>Copy "{{this.contextMenuTag!.name}}"
</button>
<button (click)="this.copyToClipboard(this.contextMenuTag!.namespace!)" *ngIf="this.contextMenuTag?.namespace"
mat-menu-item>Copy "{{this.contextMenuTag!.namespace!}}"
</button>
</app-context-menu>

@ -49,6 +49,7 @@ cdk-virtual-scroll-viewport {
height: 100%;
width: 100%;
overflow-y: auto;
::ng-deep .cdk-virtual-scroll-content-wrapper {
width: 100%;
}

@ -75,12 +75,17 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
}
async showFileDetails(files: File[]) {
this.tagsOfSelection = await this.tagService.getTagsForFiles(files.map(f => f.hash))
this.tagsOfSelection = await this.tagService.getTagsForFiles(
files.map(f => f.hash))
this.tagsOfSelection = this.tagsOfSelection.sort(
(a, b) => a.getNormalizedOutput().localeCompare(b.getNormalizedOutput()));
this.tags = this.tagsOfSelection;
}
public async copyToClipboard(text: string) {
await clipboard.writeText(text);
}
private async refreshFileSelection() {
const filteredSelection = this.selectedFiles.filter(
file => this.files.findIndex(f => f.id === file.id) >= 0);
@ -100,8 +105,4 @@ export class FilesTabSidebarComponent implements OnInit, OnChanges {
.localeCompare(b.getNormalizedOutput()));
}
}
public async copyToClipboard(text: string) {
await clipboard.writeText(text);
}
}

@ -5,7 +5,7 @@
[selectedFiles]="this.selectedFiles"></app-files-tab-sidebar>
</mat-drawer>
<mat-drawer-content>
<app-busy-indicator [busy]="contentLoading" [blurBackground]="true">
<app-busy-indicator [blurBackground]="true" [busy]="contentLoading">
<app-file-multiview (fileSelectEvent)="this.onFileSelect($event)" [files]="this.files"></app-file-multiview>
</app-busy-indicator>
</mat-drawer-content>

@ -5,26 +5,31 @@
<mat-option value="files">Files</mat-option>
</mat-select>
</mat-form-field>
<app-native-file-select [filters]="this.filters" [mode]="selectionType.value" (fileSelect)="this.setSelectedPaths($event)"></app-native-file-select>
<app-native-file-select (fileSelect)="this.setSelectedPaths($event)" [filters]="this.filters"
[mode]="selectionType.value"></app-native-file-select>
<button mat-flat-button>
{{resolving ? "Searching for files..." : this.fileCount + " files found"}}
<mat-progress-bar *ngIf="resolving" mode="indeterminate" color="primary"></mat-progress-bar>
<mat-progress-bar *ngIf="resolving" color="primary" mode="indeterminate"></mat-progress-bar>
</button>
<mat-divider></mat-divider>
<section class="binary-import-options">
<mat-checkbox [checked]="this.importOptions.read_tags_from_txt" (change)="this.importOptions.read_tags_from_txt = $event.checked">Import tags from
<mat-checkbox (change)="this.importOptions.read_tags_from_txt = $event.checked"
[checked]="this.importOptions.read_tags_from_txt">Import tags from
adjacent .txt tag files
</mat-checkbox>
<mat-checkbox [checked]="this.importOptions.delete_after_import" (change)="this.importOptions.delete_after_import = $event.checked" color="warn">
<mat-checkbox (change)="this.importOptions.delete_after_import = $event.checked"
[checked]="this.importOptions.delete_after_import" color="warn">
Delete files from original location after import
</mat-checkbox>
</section>
<mat-divider></mat-divider>
<button mat-flat-button color="primary" class="import-button" [disabled]="importing || this.fileCount === 0" (click)="import()">
<button (click)="import()" [disabled]="importing || this.fileCount === 0" class="import-button" color="primary"
mat-flat-button>
{{importing ? "Importing..." : "Import"}}
</button>
<mat-progress-bar *ngIf="importing" mode="determinate" color="primary" [value]="this.importingProgress"></mat-progress-bar>
<mat-progress-bar *ngIf="importing" [value]="this.importingProgress" color="primary"
mode="determinate"></mat-progress-bar>

@ -12,7 +12,8 @@
<mat-divider></mat-divider>
</div>
<div class="import-configuration" fxFlex fxFlexFill>
<app-filesystem-import (fileImported)="this.fileImported.emit($event)" (importFinished)="importFinished.emit()"></app-filesystem-import>
<app-filesystem-import (fileImported)="this.fileImported.emit($event)"
(importFinished)="importFinished.emit()"></app-filesystem-import>
</div>
</div>
</mat-tab>

@ -18,6 +18,7 @@ mat-tab-group, mat-tab {
width: calc(100% - 2em);
height: calc(100% - 2em);
margin: 1em;
mat-select {
height: 100%;
}

@ -1,11 +1,4 @@
import {
Component,
EventEmitter,
OnInit,
Output,
ViewChild
} from '@angular/core';
import {MatTabGroup} from "@angular/material/tabs";
import {Component, EventEmitter, Output} from '@angular/core';
import {File} from "../../../../models/File";
@Component({
@ -18,5 +11,6 @@ export class ImportTabSidebarComponent {
@Output() fileImported = new EventEmitter<File>();
@Output() importFinished = new EventEmitter<void>();
constructor() { }
constructor() {
}
}

@ -1,6 +1,7 @@
<mat-drawer-container autosize>
<mat-drawer disableClose="true" mode="side" opened>
<app-import-tab-sidebar (fileImported)="this.addFileFromImport($event)" (importFinished)="this.refreshFileView()"></app-import-tab-sidebar>
<app-import-tab-sidebar (fileImported)="this.addFileFromImport($event)"
(importFinished)="this.refreshFileView()"></app-import-tab-sidebar>
</mat-drawer>
<mat-drawer-content>
<app-file-multiview [files]="this.files"></app-file-multiview>

@ -1,4 +1,5 @@
<ng-content></ng-content>
<div *ngIf="this.busy" class="busy-indicator-overlay" [class.blur]="this.blurBackground" [class.darken]="this.darkenBackground">
<mat-progress-spinner color="primary" [mode]="mode" [value]="value"></mat-progress-spinner>
<div *ngIf="this.busy" [class.blur]="this.blurBackground" [class.darken]="this.darkenBackground"
class="busy-indicator-overlay">
<mat-progress-spinner [mode]="mode" [value]="value" color="primary"></mat-progress-spinner>
</div>

@ -1,4 +1,4 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, Input} from '@angular/core';
import {ProgressSpinnerMode} from "@angular/material/progress-spinner";
@Component({
@ -14,7 +14,8 @@ export class BusyIndicatorComponent {
@Input() mode: ProgressSpinnerMode = "indeterminate";
@Input() value: number | undefined;
constructor() { }
constructor() {
}
public setBusy(busy: boolean) {
this.busy = busy;

@ -1,5 +1,5 @@
<div #imageContainer (window:resize)="this.adjustSize(image, imageContainer)" class="image-container">
<img #image (load)="this.adjustSize(image, imageContainer)" [class.scale-height]="(!scaleWidth) && maximizeHeight"
[class.scale-width]="scaleWidth && maximizeWidth" [style]="{borderRadius: this.borderRadius}"
[src]="this.imageSrc" alt="">
[class.scale-width]="scaleWidth && maximizeWidth" [src]="this.imageSrc"
[style]="{borderRadius: this.borderRadius}" alt="">
</div>

@ -1,4 +1,4 @@
<div class="menu-anchor" [matMenuTriggerFor]="contextMenu" [style.left]="x" [style.top]="y"></div>
<div [matMenuTriggerFor]="contextMenu" [style.left]="x" [style.top]="y" class="menu-anchor"></div>
<mat-menu #contextMenu="matMenu">
<ng-content></ng-content>
</mat-menu>

@ -1,7 +1,4 @@
import {
Component,
ViewChild,
} from '@angular/core';
import {Component, ViewChild,} from '@angular/core';
import {MatMenuTrigger} from "@angular/material/menu";
@Component({

@ -1,5 +1,5 @@
<app-context-menu #contextMenu>
<button mat-menu-item (click)="this.copyFileHash()">Copy Hash</button>
<button mat-menu-item (click)="this.exportFile()">Save As...</button>
<button (click)="this.copyFileHash()" mat-menu-item>Copy Hash</button>
<button (click)="this.exportFile()" mat-menu-item>Save As...</button>
<ng-content></ng-content>
</app-context-menu>

@ -1,10 +1,9 @@
import {Component, ViewChild} from '@angular/core';
import {File} from "../../../../models/File";
import {ContextMenuComponent} from "../context-menu.component";
import {clipboard, dialog} from "@tauri-apps/api";
import {clipboard} from "@tauri-apps/api";
import {FileService} from "../../../../services/file/file.service";
import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service";
import {downloadDir} from "@tauri-apps/api/path";
import {FileHelper} from "../../../../services/file/file.helper";
@Component({
@ -18,7 +17,8 @@ export class FileContextMenuComponent {
@ViewChild("contextMenu") contextMenu!: ContextMenuComponent;
constructor(private fileService: FileService, private errorBroker: ErrorBrokerService) { }
constructor(private fileService: FileService, private errorBroker: ErrorBrokerService) {
}
public onContextMenu(event: MouseEvent, file: File) {
this.file = file;

@ -3,14 +3,14 @@
<h1>Edit File</h1>
<mat-form-field *ngIf="this.files.length === 1" appearance="fill">
<mat-label>Name</mat-label>
<input #fileNameInput matInput (focusout)="this.changeFileName(fileNameInput.value)">
<input #fileNameInput (focusout)="this.changeFileName(fileNameInput.value)" matInput>
</mat-form-field>
</div>
<div class="tag-edit-list" fxFlex fxFlexFill fxFlexAlign="start">
<div class="tag-edit-list" fxFlex fxFlexAlign="start" fxFlexFill>
<cdk-virtual-scroll-viewport #tagScroll itemSize="50" maxBufferPx="2000" minBufferPx="1000">
<div class="editable-tag" *cdkVirtualFor="let tag of tags">
<div *cdkVirtualFor="let tag of tags" class="editable-tag">
<app-tag-item [tag]="tag"></app-tag-item>
<button class="tag-remove-button" mat-icon-button (click)="removeTag(tag)">
<button (click)="removeTag(tag)" class="tag-remove-button" mat-icon-button>
<ng-icon name="mat-remove"></ng-icon>
</button>
</div>
@ -19,14 +19,15 @@
<mat-divider fxFlex="1em"></mat-divider>
<div class="tag-input" fxFlex="200px">
<div class="tag-input-field">
<app-tag-input [availableTags]="this.allTags" [allowInvalid]="true" (tagAdded)="this.editTag($event)"></app-tag-input>
<button mat-icon-button class="add-tag-button">
<app-tag-input (tagAdded)="this.editTag($event)" [allowInvalid]="true"
[availableTags]="this.allTags"></app-tag-input>
<button class="add-tag-button" mat-icon-button>
<ng-icon *ngIf="editMode === 'Toggle'" name="mat-change-circle"></ng-icon>
<ng-icon *ngIf="editMode === 'Add'" name="mat-add-circle"></ng-icon>
<ng-icon *ngIf="editMode === 'Remove'" name="mat-remove-circle"></ng-icon>
</button>
</div>
<mat-form-field class="form-field-mode" appearance="fill">
<mat-form-field appearance="fill" class="form-field-mode">
<mat-label>Mode</mat-label>
<mat-select #modeSelect [(value)]="editMode">
<mat-option value="Toggle">Toggle</mat-option>

@ -27,6 +27,7 @@ cdk-virtual-scroll-viewport {
height: 100%;
width: 100%;
overflow-y: auto;
::ng-deep .cdk-virtual-scroll-content-wrapper {
width: 100%;
}
@ -56,6 +57,7 @@ cdk-virtual-scroll-viewport {
.tag-input-field {
display: flex;
flex-direction: row;
.add-tag-button {
width: 65px;
height: 65px;

@ -1,5 +1,6 @@
import {
Component, ElementRef,
Component,
ElementRef,
Input,
OnChanges,
OnInit,
@ -23,12 +24,10 @@ export class FileEditComponent implements OnInit, OnChanges {
public tags: Tag[] = [];
public allTags: Tag[] = [];
private fileTags: {[key: number]: Tag[]} = {};
public editMode: string = "Toggle";
@ViewChild("tagScroll") tagScroll!: CdkVirtualScrollViewport;
@ViewChild("fileNameInput") fileNameInput!: ElementRef<HTMLInputElement>;
private fileTags: { [key: number]: Tag[] } = {};
constructor(
private tagService: TagService,
@ -95,7 +94,8 @@ export class FileEditComponent implements OnInit, OnChanges {
} else {
removedTags.push(tag.id);
}
this.fileTags[file.id] = await this.tagService.changeFileTags(file.id, addedTags, removedTags);
this.fileTags[file.id] = await this.tagService.changeFileTags(file.id,
addedTags, removedTags);
}
this.mapFileTagsToTagList();
const index = this.tags.indexOf(tag);
@ -126,7 +126,8 @@ export class FileEditComponent implements OnInit, OnChanges {
private async loadFileTags() {
for (const file of this.files) {
this.fileTags[file.id] = await this.tagService.getTagsForFiles([file.hash]);
this.fileTags[file.id] = await this.tagService.getTagsForFiles(
[file.hash]);
}
this.mapFileTagsToTagList();
}
@ -141,8 +142,10 @@ export class FileEditComponent implements OnInit, OnChanges {
let tags: Tag[] = [];
for (const file of this.files) {
const fileTags = this.fileTags[file.id];
tags.push(...fileTags.filter(t => tags.findIndex(tag => tag.id === t.id) < 0));
tags.push(
...fileTags.filter(t => tags.findIndex(tag => tag.id === t.id) < 0));
}
this.tags = tags.sort((a, b) => a.getNormalizedOutput().localeCompare(b.getNormalizedOutput()));
this.tags = tags.sort(
(a, b) => a.getNormalizedOutput().localeCompare(b.getNormalizedOutput()));
}
}

@ -1,4 +1,4 @@
<div class="audio-container">
<audio controls [src]="this.blobUrl">
<audio [src]="this.blobUrl" controls>
</audio>
</div>

@ -1,4 +1,4 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, Input} from '@angular/core';
import {SafeResourceUrl} from "@angular/platform-browser";
@Component({
@ -10,5 +10,6 @@ export class AudioViewerComponent {
@Input() blobUrl!: SafeResourceUrl;
constructor() { }
constructor() {
}
}

@ -4,6 +4,6 @@
<div *ngIf="getContentType() === 'other'" class="download-prompt">
<span>Unsupported content type <b>{{this.file.mime_type}}</b></span>
<button mat-flat-button color="primary" (click)="this.downloadContent()">Download</button>
<button (click)="this.downloadContent()" color="primary" mat-flat-button>Download</button>
</div>
<app-busy-indicator></app-busy-indicator>

@ -8,10 +8,12 @@ app-image-viewer, app-video-viewer, app-audio-viewer {
height: 100%;
display: flex;
flex-direction: column;
button {
margin: 1em 0 auto;
align-self: center;
}
span {
margin: auto 0 0;
align-self: center;

@ -1,11 +1,11 @@
import {
AfterContentInit, AfterViewInit,
AfterViewInit,
Component,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges, ViewChild
SimpleChanges,
ViewChild
} from '@angular/core';
import {SafeResourceUrl} from "@angular/platform-browser";
import {File} from "../../../../../models/File";
@ -45,7 +45,8 @@ export class ContentViewerComponent implements AfterViewInit, OnChanges, OnDestr
public async ngOnChanges(changes: SimpleChanges) {
if (changes["file"]) {
if (["audio", "video"].includes(this.getContentType()) && this.busyIndicator) {
if (["audio", "video"].includes(
this.getContentType()) && this.busyIndicator) {
await this.loadBlobUrl();
} else {
this.contentUrl = this.fileService.buildContentUrl(this.file);

@ -1,10 +1,9 @@
import {
Component,
ElementRef,
HostListener,
Input, OnChanges,
OnInit, SimpleChanges,
ViewChild
Input,
OnChanges,
SimpleChanges
} from '@angular/core';
import {CdkDragMove} from "@angular/cdk/drag-drop";
import {SafeResourceUrl} from "@angular/platform-browser";
@ -20,7 +19,8 @@ export class ImageViewerComponent implements OnChanges {
public imagePosition = {x: 0, y: 0};
public mouseInImageView = false;
constructor() { }
constructor() {
}
public ngOnChanges(changes: SimpleChanges): void {
if (changes["imageUrl"]) {

@ -1,3 +1,3 @@
<video controls [src]="this.blobUrl">
<video [src]="this.blobUrl" controls>
Unsupported video type
</video>

@ -1,7 +1,4 @@
import {
Component,
Input,
} from '@angular/core';
import {Component, Input,} from '@angular/core';
import {SafeResourceUrl} from "@angular/platform-browser";
@Component({

@ -1,6 +1,7 @@
import {
Component,
EventEmitter, Inject,
EventEmitter,
Inject,
Input,
OnChanges,
OnInit,
@ -27,7 +28,8 @@ export class FileGalleryEntryComponent implements OnInit, OnChanges {
private cachedFile: File | undefined;
private urlSetTimeout: number | undefined;
constructor(@Inject(DomSanitizer) private sanitizer: DomSanitizer, private fileService: FileService, private errorBroker: ErrorBrokerService) {
constructor(@Inject(
DomSanitizer) private sanitizer: DomSanitizer, private fileService: FileService, private errorBroker: ErrorBrokerService) {
}
ngOnChanges(changes: SimpleChanges) {
@ -45,6 +47,8 @@ export class FileGalleryEntryComponent implements OnInit, OnChanges {
private setImageDelayed() {
this.contentUrl = undefined;
clearTimeout(this.urlSetTimeout);
this.urlSetTimeout = setTimeout(() => this.contentUrl = this.fileService.buildThumbnailUrl(this.file.data, 250, 250), 200);
this.urlSetTimeout = setTimeout(
() => this.contentUrl = this.fileService.buildThumbnailUrl(this.file.data,
250, 250), 200);
}
}

@ -5,7 +5,8 @@
<div (dblclick)="this.selectedFile? this.fileDblClickEvent.emit(this.selectedFile.data) : null" class="file-full-view"
fxFlex="80%">
<app-content-viewer
(contextmenu)="this.selectedFile && fileContextMenu.onContextMenu($event, this.selectedFile!.data)" [file]="this.selectedFile!.data"></app-content-viewer>
(contextmenu)="this.selectedFile && fileContextMenu.onContextMenu($event, this.selectedFile!.data)"
[file]="this.selectedFile!.data"></app-content-viewer>
</div>
<mat-divider fxFlex></mat-divider>
<div class="file-scroll-view" fxFlex="20%">

@ -62,28 +62,14 @@ export class FileGalleryComponent implements OnChanges, OnInit {
}
}
private scrollToSelection(): void {
if (this.selectedFile) {
const selectedIndex = this.entries.indexOf(this.selectedFile);
const viewportSize = this.virtualScroll.getViewportSize();
const indexAdjustment = (viewportSize / 260) / 2; // adjustment to have the selected item centered
this.virtualScroll.scrollToIndex(
Math.max(selectedIndex - indexAdjustment, 0), "smooth");
if (selectedIndex > indexAdjustment) {
this.virtualScroll.scrollToOffset(
this.virtualScroll.measureScrollOffset("left") + 130, "smooth");
}
}
}
/**
* Loads the content url of the selected file
* @returns {Promise<void>}
*/
async loadSelectedFile() {
if (this.selectedFile) {
this.fileContentUrl = this.fileService.buildContentUrl(this.selectedFile.data)
this.fileContentUrl = this.fileService.buildContentUrl(
this.selectedFile.data)
}
}
@ -140,6 +126,27 @@ export class FileGalleryComponent implements OnChanges, OnInit {
}
}
public adjustElementSizes(): void {
if (this.virtualScroll) {
this.virtualScroll.checkViewportSize();
this.scrollToSelection();
}
}
private scrollToSelection(): void {
if (this.selectedFile) {
const selectedIndex = this.entries.indexOf(this.selectedFile);
const viewportSize = this.virtualScroll.getViewportSize();
const indexAdjustment = (viewportSize / 260) / 2; // adjustment to have the selected item centered
this.virtualScroll.scrollToIndex(
Math.max(selectedIndex - indexAdjustment, 0), "smooth");
if (selectedIndex > indexAdjustment) {
this.virtualScroll.scrollToOffset(
this.virtualScroll.measureScrollOffset("left") + 130, "smooth");
}
}
}
@HostListener("window:keydown", ["$event"])
private async handleKeydownEvent(event: KeyboardEvent) {
@ -163,11 +170,4 @@ export class FileGalleryComponent implements OnChanges, OnInit {
}
return undefined;
}
public adjustElementSizes(): void {
if (this.virtualScroll) {
this.virtualScroll.checkViewportSize();
this.scrollToSelection();
}
}
}

@ -49,7 +49,9 @@ export class FileGridEntryComponent implements OnInit, OnChanges {
private setImageDelayed() {
this.contentUrl = undefined;
clearTimeout(this.urlSetTimeout);
this.urlSetTimeout = setTimeout(() => this.contentUrl = this.fileService.buildThumbnailUrl(this.gridEntry.file,
this.urlSetTimeout = setTimeout(
() => this.contentUrl = this.fileService.buildThumbnailUrl(
this.gridEntry.file,
250, 250), 200);
}
}

@ -4,9 +4,9 @@
<div *cdkVirtualFor="let rowEntry of partitionedGridEntries">
<div class="file-row">
<app-file-grid-entry
*ngFor="let gridEntry of rowEntry" (clickEvent)="setSelectedFile($event.gridEntry)"
(clickEvent)="setSelectedFile($event.gridEntry)" (contextmenu)="fileContextMenu.onContextMenu($event, gridEntry.file)"
(dblClickEvent)="fileOpenEvent.emit($event.gridEntry.file)"
(contextmenu)="fileContextMenu.onContextMenu($event, gridEntry.file)"
*ngFor="let gridEntry of rowEntry"
[gridEntry]="gridEntry"></app-file-grid-entry>
</div>
</div>
@ -14,5 +14,5 @@
</div>
<app-file-context-menu #fileContextMenu>
<button mat-menu-item (click)="this.regenerateThumbnail(fileContextMenu.file)">Regenerate thumbnail</button>
<button (click)="this.regenerateThumbnail(fileContextMenu.file)" mat-menu-item>Regenerate thumbnail</button>
</app-file-context-menu>

@ -92,6 +92,16 @@ export class FileGridComponent implements OnChanges, OnInit {
this.fileSelectEvent.emit(this.selectedEntries.map(g => g.file));
}
public adjustElementSizes(): void {
if (this.virtualScroll) {
this.virtualScroll.checkViewportSize();
}
}
public async regenerateThumbnail(file: File) {
await this.fileService.deleteThumbnails(file);
}
private setPartitionedGridEntries() {
this.partitionedGridEntries = [];
let scrollToIndex = -1;
@ -178,14 +188,4 @@ export class FileGridComponent implements OnChanges, OnInit {
break;
}
}
public adjustElementSizes(): void {
if (this.virtualScroll) {
this.virtualScroll.checkViewportSize();
}
}
public async regenerateThumbnail(file: File) {
await this.fileService.deleteThumbnails(file);
}
}

@ -1,4 +1,5 @@
<app-file-grid *ngIf="this.mode === 'grid'" [files]="this.files" [preselectedFile]="this.preselectedFile"
(fileSelectEvent)="this.onFileSelect($event)" (fileOpenEvent)="this.onFileOpen($event)"></app-file-grid>
<app-file-gallery *ngIf="this.mode === 'gallery'" [files]="this.files" [preselectedFile]="this.preselectedFile"
(fileSelectEvent)="this.onSinglefileSelect($event)" (closeEvent)="this.mode = 'grid'"></app-file-gallery>
<app-file-grid (fileOpenEvent)="this.onFileOpen($event)" (fileSelectEvent)="this.onFileSelect($event)" *ngIf="this.mode === 'grid'"
[files]="this.files" [preselectedFile]="this.preselectedFile"></app-file-grid>
<app-file-gallery (closeEvent)="this.mode = 'grid'" (fileSelectEvent)="this.onSinglefileSelect($event)" *ngIf="this.mode === 'gallery'"
[files]="this.files"
[preselectedFile]="this.preselectedFile"></app-file-gallery>

@ -1,4 +1,4 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {File} from "../../../models/File";
@Component({
@ -17,7 +17,8 @@ export class FileMultiviewComponent {
public selectedFiles: File[] = [];
public preselectedFile: File | undefined;
constructor() { }
constructor() {
}
public onFileSelect(files: File[]): void {
this.selectedFiles = files;

@ -9,8 +9,9 @@
<ng-icon name="mat-delete-sweep"></ng-icon>
</button>
</div>
<app-tag-input class="full-width" [allowNegation]="true" [availableTags]="getValidSearchTags()" (tagAdded)="addSearchTag($event); searchForFiles()">
<button mat-button class="filter-dialog-button" (click)="openFilterDialog()">
<app-tag-input (tagAdded)="addSearchTag($event); searchForFiles()" [allowNegation]="true" [availableTags]="getValidSearchTags()"
class="full-width">
<button (click)="openFilterDialog()" class="filter-dialog-button" mat-button>
<ng-icon name="mat-filter-alt"></ng-icon>
</button>
</app-tag-input>

@ -43,7 +43,8 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
private errorBroker: ErrorBrokerService,
private fileService: FileService,
public dialog: MatDialog
) {}
) {
}
public async ngOnInit() {
await this.searchForFiles();
@ -74,7 +75,8 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
}
public getValidSearchTags(): Tag[] {
return this.availableTags.filter(t => this.filters.findIndex(f => f.partiallyEq(t.getNormalizedOutput())) < 0);
return this.availableTags.filter(t => this.filters.findIndex(
f => f.partiallyEq(t.getNormalizedOutput())) < 0);
}
public async removeAllSearchTags() {

@ -1,26 +1,26 @@
<h1 mat-dialog-title>Filters</h1>
<div mat-dialog-content class="filter-dialog-content">
<div class="filter-dialog-content" mat-dialog-content>
<div class="filter-dialog-list">
<mat-list class="mat-filter-dialog-list">
<mat-list-item class="filter-list-item" *ngFor="let expression of filters" [class.selected]="expression.selected">
<app-tag-filter-list-item (querySelect)="this.addToSelection($event)"
<mat-list-item *ngFor="let expression of filters" [class.selected]="expression.selected" class="filter-list-item">
<app-tag-filter-list-item (contextmenu)="contextMenu.onContextMenu($event)"
(querySelect)="this.addToSelection($event)"
(queryUnselect)="this.removeFromSelection($event)"
(removeClicked)="this.removeFilter($event)"
(contextmenu)="contextMenu.onContextMenu($event)"
[expression]="expression"></app-tag-filter-list-item>
</mat-list-item>
</mat-list>
</div>
<mat-divider></mat-divider>
<app-tag-input class="tag-input" [allowNegation]="true" [availableTags]="this.availableTags"
(tagAdded)="this.addFilter($event)"></app-tag-input>
<app-tag-input (tagAdded)="this.addFilter($event)" [allowNegation]="true" [availableTags]="this.availableTags"
class="tag-input"></app-tag-input>
</div>
<div class="dialog-actions" mat-dialog-actions>
<button mat-flat-button color="primary" (click)="confirmFilter()">Filter</button>
<button mat-stroked-button color="accent" (click)="cancelFilter()">Cancel</button>
<button (click)="confirmFilter()" color="primary" mat-flat-button>Filter</button>
<button (click)="cancelFilter()" color="accent" mat-stroked-button>Cancel</button>
</div>
<app-context-menu #contextMenu>
<button mat-menu-item (click)="this.convertSelectionToOrExpression()">Copy to OR-Expression</button>
<button mat-menu-item (click)="this.convertSelectionToAndExpression()">Copy to AND-Expression</button>
<button mat-menu-item (click)="this.invertSelection()">Invert</button>
<button (click)="this.convertSelectionToOrExpression()" mat-menu-item>Copy to OR-Expression</button>
<button (click)="this.convertSelectionToAndExpression()" mat-menu-item>Copy to AND-Expression</button>
<button (click)="this.invertSelection()" mat-menu-item>Invert</button>
</app-context-menu>

@ -35,6 +35,24 @@ export class FilterDialogComponent {
this.availableTags = data.availableTags ?? [];
}
private static checkFiltersEqual(l: FilterExpression, r: FilterExpression): boolean {
const lTags = l.queryList().map(q => q.getNormalizedTag()).sort();
const rTags = r.queryList().map(q => q.getNormalizedTag()).sort();
let match = false;
if (lTags.length == rTags.length) {
match = true;
for (const tag of lTags) {
match = rTags.includes(tag);
if (!match) {
break;
}
}
}
return match;
}
public cancelFilter(): void {
this.dialogRef.close();
}
@ -97,7 +115,9 @@ export class FilterDialogComponent {
public convertSelectionToAndExpression(): void {
for (const query of this.selectedQueries) {
this.filters.push(new Selectable<FilterExpression>(new SingleFilterExpression(query), false));
this.filters.push(
new Selectable<FilterExpression>(new SingleFilterExpression(query),
false));
}
this.removeFilterDuplicates();
this.unselectAll();
@ -123,9 +143,12 @@ export class FilterDialogComponent {
if (filterItem.data.filter_type == "OrExpression") {
(filterItem.data as OrFilterExpression).removeDuplicates();
}
if (newFilters.findIndex(f => FilterDialogComponent.checkFiltersEqual(f.data, filterItem.data)) < 0) {
if (newFilters.findIndex(
f => FilterDialogComponent.checkFiltersEqual(f.data,
filterItem.data)) < 0) {
if (filterItem.data.filter_type == "OrExpression" && filterItem.data.queryList().length === 1) {
filterItem.data = new SingleFilterExpression(filterItem.data.queryList()[0]);
filterItem.data = new SingleFilterExpression(
filterItem.data.queryList()[0]);
}
newFilters.push(filterItem);
}
@ -133,24 +156,6 @@ export class FilterDialogComponent {
this.filters = newFilters;
}
private static checkFiltersEqual(l: FilterExpression, r: FilterExpression): boolean {
const lTags = l.queryList().map(q => q.getNormalizedTag()).sort();
const rTags = r.queryList().map(q => q.getNormalizedTag()).sort();
let match = false;
if (lTags.length == rTags.length) {
match = true;
for (const tag of lTags) {
match = rTags.includes(tag);
if (!match) {
break;
}
}
}
return match;
}
@HostListener("window:keydown", ["$event"])
private async handleKeydownEvent(event: KeyboardEvent) {
if (event.key === "Shift") {

@ -1,16 +1,18 @@
<div *ngIf="expression.data.filter_type === 'Query'" (click)="onSelect()">
<div (click)="onSelect()" *ngIf="expression.data.filter_type === 'Query'">
{{expression.data.getDisplayName()}}
<button mat-button class="remove-button" (click)="this.removeClicked.emit(this)">
<button (click)="this.removeClicked.emit(this)" class="remove-button" mat-button>
<ng-icon name="mat-remove"></ng-icon>
</button>
</div>
<div *ngIf="expression.data.filter_type === 'OrExpression'">
<mat-list>
<mat-list-item class="or-filter-list-item" *ngFor="let entry of enumerate(this.expression.data.queryList())"
(mousedown)="$event.button === 0 && this.selectInnerIndex(entry[0])" [class.selected]="this.selectedIndices.includes(entry[0])">
<span class="or-span" *ngIf="entry[0] > 0">OR</span>
<mat-list-item (mousedown)="$event.button === 0 && this.selectInnerIndex(entry[0])" *ngFor="let entry of enumerate(this.expression.data.queryList())"
[class.selected]="this.selectedIndices.includes(entry[0])"
class="or-filter-list-item">
<span *ngIf="entry[0] > 0" class="or-span">OR</span>
{{entry[1].getNormalizedTag()}}
<button mat-button class="remove-button-inner-list" (mousedown)="$event.button === 0 && this.removeOrExpression(entry[0])">
<button (mousedown)="$event.button === 0 && this.removeOrExpression(entry[0])" class="remove-button-inner-list"
mat-button>
<ng-icon name="mat-remove"></ng-icon>
</button>
</mat-list-item>

@ -2,14 +2,15 @@ import {
ChangeDetectorRef,
Component,
EventEmitter,
Inject,
Input, OnChanges,
OnInit,
Output, SimpleChanges
Input,
OnChanges,
Output,
SimpleChanges
} from '@angular/core';
import {
FilterExpression,
OrFilterExpression, SingleFilterExpression
OrFilterExpression,
SingleFilterExpression
} from "../../../../../models/FilterExpression";
import {TagQuery} from "../../../../../models/TagQuery";
import {Selectable} from "../../../../../models/Selectable";
@ -28,7 +29,8 @@ export class TagFilterListItemComponent implements OnChanges {
public selectedIndices: number[] = [];
constructor(private changeDetector: ChangeDetectorRef) { }
constructor(private changeDetector: ChangeDetectorRef) {
}
public ngOnChanges(changes: SimpleChanges): void {
if (changes["expression"]) {

@ -21,7 +21,8 @@
</mat-form-field>
<mat-form-field *ngIf="sortKey.sortType === 'Namespace'">
<mat-label>Namespace Name</mat-label>
<input #namespaceInput (change)="sortKey.namespaceName = namespaceInput.value" [value]="sortKey.namespaceName ?? ''" matInput
<input #namespaceInput (change)="sortKey.namespaceName = namespaceInput.value"
[value]="sortKey.namespaceName ?? ''" matInput
required>
</mat-form-field>
<div *ngIf="sortKey.sortType !== 'Namespace'" class="filler"></div>

@ -1,10 +1,10 @@
<app-content-aware-image borderRadius="0.25em" *ngIf="this.getThumbnailSupported() && this.thumbUrl"
[imageSrc]="this.thumbUrl"></app-content-aware-image>
<div class="file-icon-overlay" *ngIf="this.getThumbnailSupported() && this.thumbUrl">
<app-content-aware-image *ngIf="this.getThumbnailSupported() && this.thumbUrl" [imageSrc]="this.thumbUrl"
borderRadius="0.25em"></app-content-aware-image>
<div *ngIf="this.getThumbnailSupported() && this.thumbUrl" class="file-icon-overlay">
<ng-icon *ngIf="getFileType() === 'video'" name="mat-movie"></ng-icon>
<ng-icon *ngIf="this.file.mime_type === 'image/gif'" class="gif-icon" name="mat-gif"></ng-icon>
</div>
<div class="file-type-icon" *ngIf="!this.getThumbnailSupported() || !this.thumbUrl">
<div *ngIf="!this.getThumbnailSupported() || !this.thumbUrl" class="file-type-icon">
<ng-icon *ngIf="getFileType() === 'image'" name="mat-image"></ng-icon>
<ng-icon *ngIf="getFileType() === 'video'" name="mat-movie"></ng-icon>
<ng-icon *ngIf="getFileType() === 'audio'" name="mat-audiotrack"></ng-icon>

@ -1,4 +1,10 @@
import {Component, Input, OnChanges, OnInit, SimpleChanges} from '@angular/core';
import {
Component,
Input,
OnChanges,
OnInit,
SimpleChanges
} from '@angular/core';
import {File} from "../../../models/File";
import {FileService} from "../../../services/file/file.service";
import {FileHelper} from "../../../services/file/file.helper";

@ -2,13 +2,14 @@
<mat-form-field appearance="fill">
<mat-label>{{label}}</mat-label>
<input #filesInput matInput [value]="files.join(', ')" (change)="this.setFiles(filesInput.value)" class="file-input">
<input #filesInput (change)="this.setFiles(filesInput.value)" [value]="files.join(', ')" class="file-input"
matInput>
<div class="buttons-native-select">
<button *ngIf="mode === 'files'" (click)="openNativeFileSelectDialog(false)" mat-button>
<button (click)="openNativeFileSelectDialog(false)" *ngIf="mode === 'files'" mat-button>
<ng-icon name="mat-insert-drive-file"></ng-icon>
</button>
<button *ngIf="mode === 'folders'" (click)="openNativeFileSelectDialog(true)" mat-button>
<button (click)="openNativeFileSelectDialog(true)" *ngIf="mode === 'folders'" mat-button>
<ng-icon name="mat-folder"></ng-icon>
</button>
</div>

@ -3,10 +3,10 @@
Enter a tag
</mat-label>
<input #tagInput
[formControl]="formControl"
matInput
(keydown.enter)="addTagByInput($event)"
[matAutocomplete]="auto">
[formControl]="formControl"
[matAutocomplete]="auto"
matInput>
<ng-content></ng-content>
<mat-autocomplete #auto (optionSelected)="addTagByAutocomplete($event)">
<mat-option *ngFor="let tag of autosuggestTags | async" [value]="tag">

@ -1,16 +1,18 @@
import {
Component, ElementRef,
Component,
ElementRef,
EventEmitter,
Input, OnChanges,
OnInit,
Output, SimpleChanges,
Input,
OnChanges,
Output,
SimpleChanges,
ViewChild
} from '@angular/core';
import {Tag} from "../../../../models/Tag";
import {FormControl} from "@angular/forms";
import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete";
import {Observable} from "rxjs";
import {debounceTime, delay, map, startWith} from "rxjs/operators";
import {debounceTime, map, startWith} from "rxjs/operators";
@Component({
selector: 'app-tag-input',
@ -30,16 +32,19 @@ export class TagInputComponent implements OnChanges{
private tagsForAutocomplete: string[] = [];
constructor() {
this.tagsForAutocomplete = this.availableTags.map(t => t.getNormalizedOutput());
this.tagsForAutocomplete = this.availableTags.map(
t => t.getNormalizedOutput());
this.autosuggestTags = this.formControl.valueChanges.pipe(
startWith(null),
debounceTime(250),
map((tag: string | null) => tag ? this.filterSuggestionTag(tag) : this.tagsForAutocomplete.slice(0, 20)));
map((tag: string | null) => tag ? this.filterSuggestionTag(
tag) : this.tagsForAutocomplete.slice(0, 20)));
}
ngOnChanges(changes: SimpleChanges): void {
if (changes["availableTags"]) {
this.tagsForAutocomplete = this.availableTags.map(t => t.getNormalizedOutput());
this.tagsForAutocomplete = this.availableTags.map(
t => t.getNormalizedOutput());
}
}
@ -63,7 +68,8 @@ export class TagInputComponent implements OnChanges{
private filterSuggestionTag(tag: string) {
let normalizedTag = this.normalizeTag(tag);
const negated = normalizedTag.startsWith("-") && this.allowNegation;
normalizedTag = this.allowNegation? normalizedTag.replace(/^-/, "") : normalizedTag;
normalizedTag = this.allowNegation ? normalizedTag.replace(/^-/,
"") : normalizedTag;
return this.tagsForAutocomplete.filter(
t => t.includes(normalizedTag))

@ -1,6 +1,5 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {AppComponent} from "../../app.component";
import {RepositoryCardComponent} from "../core/repositories-tab/repository-card/repository-card.component";
import {FileGridComponent} from "./file-multiview/file-grid/file-grid.component";
import {FileGridEntryComponent} from "./file-multiview/file-grid/file-grid-entry/file-grid-entry.component";
@ -60,7 +59,6 @@ import {NgIconsModule} from "@ng-icons/core";
import * as materialIcons from "@ng-icons/material-icons";
@NgModule({
declarations: [
RepositoryCardComponent,
@ -135,4 +133,5 @@ import * as materialIcons from "@ng-icons/material-icons";
NgIconsModule.withIcons({...materialIcons}),
]
})
export class SharedModule { }
export class SharedModule {
}

@ -82,7 +82,8 @@ export class SingleFilterExpression implements FilterExpression {
}
public clone(): FilterExpression {
return new SingleFilterExpression(new TagQuery(this.filter.tag, this.filter.negate))
return new SingleFilterExpression(
new TagQuery(this.filter.tag, this.filter.negate))
}
public queryList(): TagQuery[] {

@ -1,5 +1,3 @@
import {SingleFilterExpression} from "./FilterExpression";
export class TagQuery {
constructor(public tag: string, public negate: boolean) {
}

@ -41,7 +41,8 @@ export class FileService {
}
public async updateFileName(file: File, name: string): Promise<File> {
return await invoke<File>("plugin:mediarepo|update_file_name", {id: file.id, name})
return await invoke<File>("plugin:mediarepo|update_file_name",
{id: file.id, name})
}
/**
@ -52,7 +53,8 @@ export class FileService {
* @returns {SafeResourceUrl}
*/
public buildThumbnailUrl(file: File, height: number, width: number): SafeResourceUrl {
return this.sanitizer.bypassSecurityTrustResourceUrl(`thumb://${file.hash}?width=${250}&height=${250}`)
return this.sanitizer.bypassSecurityTrustResourceUrl(
`thumb://${file.hash}?width=${250}&height=${250}`)
}
/**
@ -61,7 +63,8 @@ export class FileService {
* @returns {SafeResourceUrl}
*/
public buildContentUrl(file: File): SafeResourceUrl {
return this.sanitizer.bypassSecurityTrustResourceUrl(`content://${file.hash}`)
return this.sanitizer.bypassSecurityTrustResourceUrl(
`content://${file.hash}`)
}
/**
@ -71,7 +74,8 @@ export class FileService {
* @returns {Promise<void>}
*/
public async saveFile(file: File, targetPath: string) {
await invoke("plugin:mediarepo|save_file_locally", {id: file.id, path: targetPath})
await invoke("plugin:mediarepo|save_file_locally",
{id: file.id, path: targetPath})
}
/**
@ -89,7 +93,8 @@ export class FileService {
* @returns {Promise<SafeResourceUrl>}
*/
public async readFile(file: File): Promise<SafeResourceUrl> {
const data = await invoke<number[]>("plugin:mediarepo|read_file", {hash: file.hash, mimeType: file.mime_type});
const data = await invoke<number[]>("plugin:mediarepo|read_file",
{hash: file.hash, mimeType: file.mime_type});
const blob = new Blob([new Uint8Array(data)], {type: file.mime_type});
const url = URL?.createObjectURL(blob);
return this.sanitizer.bypassSecurityTrustResourceUrl(url);

@ -9,7 +9,8 @@ import {File} from "../../models/File";
})
export class ImportService {
constructor() { }
constructor() {
}
/**
* Resolves paths from the local file system into a list of files that can be imported
@ -17,7 +18,8 @@ export class ImportService {
* @returns {Promise<FileOsMetadata[]>}
*/
public async resolvePathsToFiles(paths: string[]): Promise<FileOsMetadata[]> {
return await invoke<FileOsMetadata[]>("plugin:mediarepo|resolve_paths_to_files", {paths});
return await invoke<FileOsMetadata[]>(
"plugin:mediarepo|resolve_paths_to_files", {paths});
}
/**
@ -27,6 +29,7 @@ export class ImportService {
* @returns {Promise<File>}
*/
public async addLocalFile(metadata: FileOsMetadata, options: AddFileOptions): Promise<File> {
return await invoke<File>("plugin:mediarepo|add_local_file", {metadata, options});
return await invoke<File>("plugin:mediarepo|add_local_file",
{metadata, options});
}
}

@ -7,7 +7,9 @@ import {BehaviorSubject} from "rxjs";
export class TabService {
public selectedTab = new BehaviorSubject<number>(0);
constructor() { }
constructor() {
}
public setSelectedTab(index: number) {
this.selectedTab.next(index);

@ -2,7 +2,6 @@ import {Injectable} from '@angular/core';
import {invoke} from "@tauri-apps/api/tauri";
import {Tag} from "../../models/Tag";
import {BehaviorSubject} from "rxjs";
import {File} from "../../models/File";
@Injectable({
providedIn: 'root'
@ -29,12 +28,14 @@ export class TagService {
}
public async createTags(tags: string[]): Promise<Tag[]> {
const resultTags = await invoke<Tag[]>("plugin:mediarepo|create_tags", {tags});
const resultTags = await invoke<Tag[]>("plugin:mediarepo|create_tags",
{tags});
return resultTags.map(t => new Tag(t.id, t.name, t.namespace));
}
public async changeFileTags(fileId: number, addedTags: number[], removedTags: number[]): Promise<Tag[]> {
const tags = await invoke<Tag[]>("plugin:mediarepo|change_file_tags", {id: fileId, addedTags, removedTags});
const tags = await invoke<Tag[]>("plugin:mediarepo|change_file_tags",
{id: fileId, addedTags, removedTags});
return tags.map(t => new Tag(t.id, t.name, t.namespace));
}
}

Loading…
Cancel
Save