From e721344d07548a6123429e20d2e2121474f4ab50 Mon Sep 17 00:00:00 2001 From: trivernis Date: Tue, 26 Oct 2021 15:13:10 +0200 Subject: [PATCH] Move file search to separate component Signed-off-by: trivernis --- mediarepo-ui/src-tauri/Cargo.lock | 2 +- mediarepo-ui/src-tauri/Cargo.toml | 2 +- mediarepo-ui/src/app/app.module.ts | 46 +++++++------ .../file-search/file-search.component.html | 23 +++++++ .../file-search/file-search.component.scss | 7 ++ .../file-search/file-search.component.spec.ts | 25 +++++++ .../file-search/file-search.component.ts | 66 +++++++++++++++++++ .../src/app/pages/home/home.component.html | 17 +---- .../src/app/pages/home/home.component.scss | 4 -- .../src/app/pages/home/home.component.ts | 42 ++++-------- .../services/dataloader/dataloader.service.ts | 10 ++- .../src/app/services/tag/tag.service.ts | 8 +++ 12 files changed, 180 insertions(+), 72 deletions(-) create mode 100644 mediarepo-ui/src/app/components/file-search/file-search.component.html create mode 100644 mediarepo-ui/src/app/components/file-search/file-search.component.scss create mode 100644 mediarepo-ui/src/app/components/file-search/file-search.component.spec.ts create mode 100644 mediarepo-ui/src/app/components/file-search/file-search.component.ts diff --git a/mediarepo-ui/src-tauri/Cargo.lock b/mediarepo-ui/src-tauri/Cargo.lock index 6097b35..df284a3 100644 --- a/mediarepo-ui/src-tauri/Cargo.lock +++ b/mediarepo-ui/src-tauri/Cargo.lock @@ -1581,7 +1581,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[package]] name = "mediarepo-api" version = "0.1.0" -source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=2b6a968e6d5343326e20c73f4fcbbb88848b0c35#2b6a968e6d5343326e20c73f4fcbbb88848b0c35" +source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=4600810fca96bf6b457adc39667245a46127e5e8#4600810fca96bf6b457adc39667245a46127e5e8" dependencies = [ "async-trait", "chrono", diff --git a/mediarepo-ui/src-tauri/Cargo.toml b/mediarepo-ui/src-tauri/Cargo.toml index 30c2c17..cf835ff 100644 --- a/mediarepo-ui/src-tauri/Cargo.toml +++ b/mediarepo-ui/src-tauri/Cargo.toml @@ -30,7 +30,7 @@ features = ["env-filter"] [dependencies.mediarepo-api] git = "https://github.com/Trivernis/mediarepo-api.git" -rev = "2b6a968e6d5343326e20c73f4fcbbb88848b0c35" +rev = "4600810fca96bf6b457adc39667245a46127e5e8" features = ["tauri-plugin"] [features] diff --git a/mediarepo-ui/src/app/app.module.ts b/mediarepo-ui/src/app/app.module.ts index 3045b44..f2189e5 100644 --- a/mediarepo-ui/src/app/app.module.ts +++ b/mediarepo-ui/src/app/app.module.ts @@ -26,6 +26,8 @@ import {ScrollingModule} from "@angular/cdk/scrolling"; import {LightboxModule} from "ngx-lightbox"; import {MatChipsModule} from "@angular/material/chips"; import {MatIconModule} from "@angular/material/icon"; +import {MatAutocompleteModule} from "@angular/material/autocomplete"; +import { FileSearchComponent } from './components/file-search/file-search.component'; @NgModule({ declarations: [ @@ -36,28 +38,30 @@ import {MatIconModule} from "@angular/material/icon"; RepoFormComponent, FileGridComponent, FileGridEntryComponent, + FileSearchComponent, ], - imports: [ - BrowserModule, - AppRoutingModule, - BrowserAnimationsModule, - MatCardModule, - MatListModule, - MatButtonModule, - MatToolbarModule, - MatSnackBarModule, - MatFormFieldModule, - MatInputModule, - ReactiveFormsModule, - MatSidenavModule, - MatGridListModule, - MatProgressBarModule, - MatPaginatorModule, - ScrollingModule, - LightboxModule, - MatChipsModule, - MatIconModule - ], + imports: [ + BrowserModule, + AppRoutingModule, + BrowserAnimationsModule, + MatCardModule, + MatListModule, + MatButtonModule, + MatToolbarModule, + MatSnackBarModule, + MatFormFieldModule, + MatInputModule, + ReactiveFormsModule, + MatSidenavModule, + MatGridListModule, + MatProgressBarModule, + MatPaginatorModule, + ScrollingModule, + LightboxModule, + MatChipsModule, + MatIconModule, + MatAutocompleteModule + ], providers: [], bootstrap: [AppComponent] }) diff --git a/mediarepo-ui/src/app/components/file-search/file-search.component.html b/mediarepo-ui/src/app/components/file-search/file-search.component.html new file mode 100644 index 0000000..a74f731 --- /dev/null +++ b/mediarepo-ui/src/app/components/file-search/file-search.component.html @@ -0,0 +1,23 @@ + + + + {{tag}} + + + + + + + {{tag}} + + + diff --git a/mediarepo-ui/src/app/components/file-search/file-search.component.scss b/mediarepo-ui/src/app/components/file-search/file-search.component.scss new file mode 100644 index 0000000..47109f6 --- /dev/null +++ b/mediarepo-ui/src/app/components/file-search/file-search.component.scss @@ -0,0 +1,7 @@ +#tag-search { + width: 100%; +} + +.full-width { + width: 100%; +} diff --git a/mediarepo-ui/src/app/components/file-search/file-search.component.spec.ts b/mediarepo-ui/src/app/components/file-search/file-search.component.spec.ts new file mode 100644 index 0000000..c2d77de --- /dev/null +++ b/mediarepo-ui/src/app/components/file-search/file-search.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FileSearchComponent } from './file-search.component'; + +describe('FileSearchComponent', () => { + let component: FileSearchComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ FileSearchComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(FileSearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/mediarepo-ui/src/app/components/file-search/file-search.component.ts b/mediarepo-ui/src/app/components/file-search/file-search.component.ts new file mode 100644 index 0000000..7240fcd --- /dev/null +++ b/mediarepo-ui/src/app/components/file-search/file-search.component.ts @@ -0,0 +1,66 @@ +import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; +import {TagService} from "../../services/tag/tag.service"; +import {FileService} from "../../services/file/file.service"; +import {FormControl} from "@angular/forms"; +import {COMMA, ENTER} from "@angular/cdk/keycodes"; +import {MatChipInputEvent} from "@angular/material/chips"; +import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete"; +import {map, startWith} from "rxjs/operators"; +import {Observable} from "rxjs"; + +@Component({ + selector: 'app-file-search', + templateUrl: './file-search.component.html', + styleUrls: ['./file-search.component.scss'] +}) +export class FileSearchComponent { + + public searchInputSeparators = [ENTER, COMMA]; + public formControl = new FormControl(); + public searchTags: string[] = []; + public suggestionTags: Observable; + private allTags: string[] = []; + + @ViewChild('tagInput') tagInput!: ElementRef; + + constructor(private tagService: TagService, private fileService: FileService) { + this.tagService.tags.subscribe( + (tag) => this.allTags = tag.map(t => t.getNormalizedOutput())); + + this.suggestionTags = this.formControl.valueChanges.pipe(startWith(null), map( + (tag: string | null) => tag ? this.allTags.filter( + (t: string) => t.includes(tag)).slice(0, 20) : this.allTags.slice(0, 20))); + } + + public async searchForFiles() { + await this.fileService.findFiles(this.searchTags); + } + + public addSearchTag(tag: string) { + this.searchTags.push(tag); + } + + async removeSearchTag(tag: string) { + const index = this.searchTags.indexOf(tag); + if (index >= 0) { + this.searchTags.splice(index, 1); + } + await this.searchForFiles(); + } + + async addSearchTagByChip(event: MatChipInputEvent) { + const tag = event.value.trim(); + if (tag.length > 0 && this.allTags.includes(tag)) { + this.searchTags.push(tag); + event.chipInput?.clear(); + this.formControl.setValue(null); + await this.searchForFiles(); } + } + + async addSearchTagByAutocomplete(event: MatAutocompleteSelectedEvent) { + const tag = event.option.viewValue; + this.searchTags.push(tag); + this.formControl.setValue(null); + this.tagInput.nativeElement.value = ''; + await this.searchForFiles(); } +} diff --git a/mediarepo-ui/src/app/pages/home/home.component.html b/mediarepo-ui/src/app/pages/home/home.component.html index e9ad88f..f01c3a8 100644 --- a/mediarepo-ui/src/app/pages/home/home.component.html +++ b/mediarepo-ui/src/app/pages/home/home.component.html @@ -5,22 +5,7 @@
- - - - {{tag}} - - - - - - +

Selection Tags

diff --git a/mediarepo-ui/src/app/pages/home/home.component.scss b/mediarepo-ui/src/app/pages/home/home.component.scss index 6bcacf4..ff745ae 100644 --- a/mediarepo-ui/src/app/pages/home/home.component.scss +++ b/mediarepo-ui/src/app/pages/home/home.component.scss @@ -8,10 +8,6 @@ overflow: hidden } -#tag-search { - width: 100%; -} - #file-tag-list { height: calc(100% - 8em); overflow: auto; diff --git a/mediarepo-ui/src/app/pages/home/home.component.ts b/mediarepo-ui/src/app/pages/home/home.component.ts index 1b42431..cca01b7 100644 --- a/mediarepo-ui/src/app/pages/home/home.component.ts +++ b/mediarepo-ui/src/app/pages/home/home.component.ts @@ -1,15 +1,18 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, ElementRef, OnInit, ViewChild} from '@angular/core'; import {FileService} from "../../services/file/file.service"; import {File} from "../../models/File"; -import {PageEvent} from "@angular/material/paginator"; import {Lightbox, LIGHTBOX_EVENT, LightboxEvent} from "ngx-lightbox"; -import {SafeResourceUrl} from "@angular/platform-browser"; import {ErrorBrokerService} from "../../services/error-broker/error-broker.service"; import {TagService} from "../../services/tag/tag.service"; import {Tag} from "../../models/Tag"; import {MatChipInputEvent} from "@angular/material/chips"; import {COMMA, ENTER} from "@angular/cdk/keycodes"; import {MatSelectionListChange} from "@angular/material/list"; +import {MatAutocompleteSelectedEvent} from "@angular/material/autocomplete"; +import {Observable} from "rxjs"; +import {map, startWith} from "rxjs/operators"; +import {FormControl} from "@angular/forms"; +import {FileSearchComponent} from "../../components/file-search/file-search.component"; @Component({ selector: 'app-home', @@ -18,22 +21,22 @@ import {MatSelectionListChange} from "@angular/material/list"; }) export class HomeComponent implements OnInit { - files: File[] = []; tags: Tag[] = []; - searchTags: string[] = []; + files: File[] = []; private openingLightbox = false; - searchInputSeparators = [ENTER, COMMA]; + + @ViewChild('filesearch') fileSearch!: FileSearchComponent; constructor( private errorBroker: ErrorBrokerService, private fileService: FileService, private tagService: TagService, private lightbox: Lightbox, - private lightboxEvent: LightboxEvent) { } + private lightboxEvent: LightboxEvent) { + } async ngOnInit() { this.fileService.displayedFiles.subscribe((files) => this.files = files); - await this.fileService.getFiles(); } async onFileMultiSelect(files: File[]) { @@ -58,32 +61,15 @@ export class HomeComponent implements OnInit { this.tags = await this.tagService.getTagsForFile(file.hash); } - async removeSearchTag(tag: string) { - const index = this.searchTags.indexOf(tag); - if (index >= 0) { - this.searchTags.splice(index, 1); - } - await this.fileService.findFiles(this.searchTags); - } - async addSearchTagFromList(event: MatSelectionListChange) { if (event.options.length > 0) { const tag = event.options[0].value; - this.searchTags.push(tag); - await this.fileService.findFiles(this.searchTags); + this.fileSearch.addSearchTag(tag); + await this.fileSearch.searchForFiles(); } event.source.deselectAll(); } - async addSearchTag(event: MatChipInputEvent) { - const tag = event.value.trim(); - if (tag.length > 0) { - this.searchTags.push(tag); - event.chipInput?.clear(); - await this.fileService.findFiles(this.searchTags); - } - } - async openFile(file: File) { if (this.openingLightbox) { return; @@ -91,7 +77,7 @@ export class HomeComponent implements OnInit { this.openingLightbox = true; try { await this.openLightbox(file); - } catch(err) { + } catch (err) { this.errorBroker.showError(err); } this.openingLightbox = false; diff --git a/mediarepo-ui/src/app/services/dataloader/dataloader.service.ts b/mediarepo-ui/src/app/services/dataloader/dataloader.service.ts index a21c7a2..ff3e901 100644 --- a/mediarepo-ui/src/app/services/dataloader/dataloader.service.ts +++ b/mediarepo-ui/src/app/services/dataloader/dataloader.service.ts @@ -2,17 +2,25 @@ import { Injectable } from '@angular/core'; import {RepositoryService} from "../repository/repository.service"; import {BehaviorSubject} from "rxjs"; import {ErrorBrokerService} from "../error-broker/error-broker.service"; +import {TagService} from "../tag/tag.service"; +import {FileService} from "../file/file.service"; @Injectable({ providedIn: 'root' }) export class DataloaderService { - constructor(private erroBroker: ErrorBrokerService, private repositoryService: RepositoryService) { } + constructor( + private erroBroker: ErrorBrokerService, + private repositoryService: RepositoryService, + private fileService: FileService, + private tagService: TagService) { } public async loadData() { try { await this.repositoryService.loadRepositories(); + await this.tagService.loadTags(); + await this.fileService.getFiles(); } catch (err) { this.erroBroker.showError(err); } diff --git a/mediarepo-ui/src/app/services/tag/tag.service.ts b/mediarepo-ui/src/app/services/tag/tag.service.ts index 4cb36e2..05f514a 100644 --- a/mediarepo-ui/src/app/services/tag/tag.service.ts +++ b/mediarepo-ui/src/app/services/tag/tag.service.ts @@ -1,14 +1,22 @@ import { Injectable } from '@angular/core'; import {invoke} from "@tauri-apps/api/tauri"; import {Tag} from "../../models/Tag"; +import {BehaviorSubject, Observable} from "rxjs"; @Injectable({ providedIn: 'root' }) export class TagService { + public tags: BehaviorSubject = new BehaviorSubject([]); + constructor() { } + public async loadTags() { + const tags = await invoke("plugin:mediarepo|get_all_tags"); + this.tags.next(tags.map(t => new Tag(t.id, t.name, t.namespace))); + } + public async getTagsForFile(hash: string): Promise { const tags = await invoke("plugin:mediarepo|get_tags_for_file", {hash}); return tags.map(t => new Tag(t.id, t.name, t.namespace));