Add loading indicator to file search

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent 1120d61cf1
commit 46d823720c

@ -1581,7 +1581,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]] [[package]]
name = "mediarepo-api" name = "mediarepo-api"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=476b9d152457f78c73f6f6a36c2421cbce9c9194#476b9d152457f78c73f6f6a36c2421cbce9c9194" source = "git+https://github.com/Trivernis/mediarepo-api.git?rev=c944cf5d770ac895bd01464c92d41005055c0580#c944cf5d770ac895bd01464c92d41005055c0580"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"chrono", "chrono",

@ -30,7 +30,7 @@ features = ["env-filter"]
[dependencies.mediarepo-api] [dependencies.mediarepo-api]
git = "https://github.com/Trivernis/mediarepo-api.git" git = "https://github.com/Trivernis/mediarepo-api.git"
rev = "476b9d152457f78c73f6f6a36c2421cbce9c9194" rev = "c944cf5d770ac895bd01464c92d41005055c0580"
features = ["tauri-plugin"] features = ["tauri-plugin"]
[features] [features]

@ -37,6 +37,9 @@ import {MatDialogModule} from "@angular/material/dialog";
import {MatSelectModule} from "@angular/material/select"; import {MatSelectModule} from "@angular/material/select";
import { FileGalleryComponent } from './components/file-gallery/file-gallery.component'; import { FileGalleryComponent } from './components/file-gallery/file-gallery.component';
import { FileGalleryEntryComponent } from './components/file-gallery/file-gallery-entry/file-gallery-entry.component'; import { FileGalleryEntryComponent } from './components/file-gallery/file-gallery-entry/file-gallery-entry.component';
import {MatProgressSpinnerModule} from "@angular/material/progress-spinner";
import {BlockUIModule} from "primeng/blockui";
import {PanelModule} from "primeng/panel";
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -80,6 +83,9 @@ import { FileGalleryEntryComponent } from './components/file-gallery/file-galler
MatRippleModule, MatRippleModule,
MatDialogModule, MatDialogModule,
MatSelectModule, MatSelectModule,
MatProgressSpinnerModule,
BlockUIModule,
PanelModule,
], ],
providers: [], providers: [],
bootstrap: [AppComponent] bootstrap: [AppComponent]

@ -1,3 +1,4 @@
<div class="image-wrapper" (click)="fileSelectEvent.emit(this.file)" [class.selected]="this.file.selected"> <div class="image-wrapper" (click)="fileSelectEvent.emit(this.file)" [class.selected]="this.file.selected">
<mat-progress-spinner *ngIf="!contentUrl"></mat-progress-spinner>
<img [src]="contentUrl"> <img [src]="contentUrl">
</div> </div>

@ -1,5 +1,6 @@
img { img {
max-height: 100%; max-height: 100%;
max-width: 100%;
width: auto; width: auto;
margin: auto; margin: auto;
} }

@ -2,13 +2,16 @@
<button mat-icon-button class="close-button" (click)="this.closeEvent.emit()"> <button mat-icon-button class="close-button" (click)="this.closeEvent.emit()">
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
<div class="file-full-view" fxFlex="80%" (dblclick)="this.selectedFile? this.fileDblClickEvent.emit(this.selectedFile.data) : null"> <div class="file-full-view" fxFlex="80%"
(dblclick)="this.selectedFile? this.fileDblClickEvent.emit(this.selectedFile.data) : null">
<div class="file-full-view-inner"> <div class="file-full-view-inner">
<img [src]="this.fileContentUrl"/> <img [src]="this.fileContentUrl"/>
</div> </div>
</div> </div>
<mat-divider fxFlex></mat-divider>
<div class="file-scroll-view" fxFlex="20%"> <div class="file-scroll-view" fxFlex="20%">
<cdk-virtual-scroll-viewport #virtualScroll orientation="horizontal" itemSize="250" minBufferPx="1000" maxBufferPx="3000" class="file-scroll-viewport"> <cdk-virtual-scroll-viewport #virtualScroll orientation="horizontal" itemSize="250" minBufferPx="1000"
maxBufferPx="3000" class="file-scroll-viewport">
<div *cdkVirtualFor="let entry of entries" class="file-item"> <div *cdkVirtualFor="let entry of entries" class="file-item">
<app-file-gallery-entry [file]="entry" (fileSelectEvent)="onEntrySelect($event)"></app-file-gallery-entry> <app-file-gallery-entry [file]="entry" (fileSelectEvent)="onEntrySelect($event)"></app-file-gallery-entry>
</div> </div>

@ -67,10 +67,13 @@ export class FileGalleryComponent implements OnChanges, OnInit {
} }
public async ngOnChanges(changes: SimpleChanges): Promise<void> { public async ngOnChanges(changes: SimpleChanges): Promise<void> {
this.entries = this.files.map(f => new Selectable(f, f == this.selectedFile?.data)); this.entries = this.files.map(f => new Selectable(f, f.hash == this.selectedFile?.data.hash));
const selectedIndex = this.files.findIndex(f => f.hash === this.selectedFile?.data.hash);
if (!this.selectedFile || this.files.indexOf(this.selectedFile.data) < 0) { if (!this.selectedFile || selectedIndex < 0) {
await this.onEntrySelect(this.getPreselectedEntry() ?? this.entries[0]) await this.onEntrySelect(this.getPreselectedEntry() ?? this.entries[0])
} else {
await this.onEntrySelect(this.entries[selectedIndex])
} }
} }

@ -5,7 +5,7 @@
(click)="removeSearchTag(tag)">{{tag.getNormalizedTag()}}</div> (click)="removeSearchTag(tag)">{{tag.getNormalizedTag()}}</div>
</div> </div>
</div> </div>
<button id="delete-all-tags-button" mat-button (click)="removeAllSearchTags()"> <button id="delete-all-tags-button" mat-icon-button (click)="removeAllSearchTags()">
<mat-icon>delete-sweep</mat-icon> <mat-icon>delete-sweep</mat-icon>
</button> </button>
</div> </div>

@ -1,7 +1,7 @@
import { import {
AfterViewChecked, AfterViewChecked,
Component, Component,
ElementRef, ElementRef, EventEmitter, Output,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import {TagService} from "../../services/tag/tag.service"; import {TagService} from "../../services/tag/tag.service";
@ -14,6 +14,7 @@ import {TagQuery} from "../../models/TagQuery";
import {SortKey} from "../../models/SortKey"; import {SortKey} from "../../models/SortKey";
import {MatDialog} from "@angular/material/dialog"; import {MatDialog} from "@angular/material/dialog";
import {FilterDialogComponent} from "./filter-dialog/filter-dialog.component"; import {FilterDialogComponent} from "./filter-dialog/filter-dialog.component";
import {ErrorBrokerService} from "../../services/error-broker/error-broker.service";
@Component({ @Component({
selector: 'app-file-search', selector: 'app-file-search',
@ -30,12 +31,16 @@ export class FileSearchComponent implements AfterViewChecked {
public formControl = new FormControl(); public formControl = new FormControl();
public searchTags: TagQuery[] = []; public searchTags: TagQuery[] = [];
public suggestionTags: Observable<string[]>; public suggestionTags: Observable<string[]>;
@Output() searchStartEvent = new EventEmitter<void>();
@Output() searchEndEvent = new EventEmitter<void>();
private allTags: string[] = []; private allTags: string[] = [];
@ViewChild("tagInput") tagInput!: ElementRef<HTMLInputElement>; @ViewChild("tagInput") tagInput!: ElementRef<HTMLInputElement>;
@ViewChild("tagInputList") inputList!: ElementRef; @ViewChild("tagInputList") inputList!: ElementRef;
constructor(private tagService: TagService, private fileService: FileService, public dialog: MatDialog) { constructor(private errorBroker: ErrorBrokerService, private tagService: TagService, private fileService: FileService, public dialog: MatDialog) {
this.tagService.tags.subscribe( this.tagService.tags.subscribe(
(tag) => this.allTags = tag.map(t => t.getNormalizedOutput())); (tag) => this.allTags = tag.map(t => t.getNormalizedOutput()));
@ -48,7 +53,13 @@ export class FileSearchComponent implements AfterViewChecked {
} }
public async searchForFiles() { public async searchForFiles() {
await this.fileService.findFiles(this.searchTags, this.sortExpression); this.searchStartEvent.emit();
try {
await this.fileService.findFiles(this.searchTags, this.sortExpression);
} catch (err) {
this.errorBroker.showError(err);
}
this.searchEndEvent.emit();
} }
public addSearchTag(tag: string) { public addSearchTag(tag: string) {

@ -18,7 +18,7 @@ export class SortKey {
public toBackendType(): any { public toBackendType(): any {
if (this.sortType == "Namespace") { if (this.sortType == "Namespace") {
return {"Namespace": {direction: this.sortDirection, tag: this.namespaceName}} return {"Namespace": {direction: this.sortDirection, name: this.namespaceName}}
} else { } else {
let returnObj: any = {}; let returnObj: any = {};
returnObj[this.sortType] = this.sortDirection; returnObj[this.sortType] = this.sortDirection;

@ -2,7 +2,8 @@
<mat-drawer mode="side" opened disableClose> <mat-drawer mode="side" opened disableClose>
<div fxLayout="column" class="drawer-sidebar-inner"> <div fxLayout="column" class="drawer-sidebar-inner">
<div id="file-search-input" fxFlex="220px"> <div id="file-search-input" fxFlex="220px">
<app-file-search #filesearch></app-file-search> <app-file-search #filesearch (searchStartEvent)="contentLoading = true"
(searchEndEvent)="contentLoading = false"></app-file-search>
</div> </div>
<div id="file-tag-list" fxFlex fxFlexAlign="start" fxFlexFill> <div id="file-tag-list" fxFlex fxFlexAlign="start" fxFlexFill>
<h1>Selection Tags</h1> <h1>Selection Tags</h1>
@ -16,13 +17,15 @@
</div> </div>
</mat-drawer> </mat-drawer>
<mat-drawer-content> <mat-drawer-content>
<app-file-grid *ngIf="!this.showGallery" (fileDblClickEvent)="openGallery($event)" [files]="files" <div *ngIf="contentLoading" class="spinner-overlay">
(fileSelectEvent)="onFileSelect($event)" <mat-progress-spinner color="primary" mode="indeterminate"></mat-progress-spinner>
(fileMultiselectEvent)="onFileMultiSelect($event)" </div>
></app-file-grid> <app-file-grid *ngIf="!this.showGallery" (fileDblClickEvent)="openGallery($event)" [files]="files"
<app-file-gallery *ngIf="this.showGallery" [files]="files" (fileSelectEvent)="onFileSelect($event)" (fileSelectEvent)="onFileSelect($event)"
(fileDblClickEvent)="openFile($event)" [preselectedFile]="this.preselectedFile" (fileMultiselectEvent)="onFileMultiSelect($event)"
(closeEvent)="this.showGallery = false"></app-file-gallery> ></app-file-grid>
<app-file-gallery *ngIf="this.showGallery" [files]="files" (fileSelectEvent)="onFileSelect($event)"
(fileDblClickEvent)="openFile($event)" [preselectedFile]="this.preselectedFile"
(closeEvent)="this.showGallery = false"></app-file-gallery>
</mat-drawer-content> </mat-drawer-content>
</mat-drawer-container> </mat-drawer-container>

@ -53,3 +53,21 @@ app-file-gallery {
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
.spinner-overlay {
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 998;
overflow: hidden;
display: flex;
backdrop-filter: blur(5px);
mat-progress-spinner {
z-index: 999;
margin: auto;
}
}

@ -21,6 +21,7 @@ export class SearchPageComponent implements OnInit {
private openingLightbox = false; private openingLightbox = false;
showGallery = false; showGallery = false;
preselectedFile: File | undefined; preselectedFile: File | undefined;
contentLoading = false;
@ViewChild('filesearch') fileSearch!: FileSearchComponent; @ViewChild('filesearch') fileSearch!: FileSearchComponent;
@ -34,7 +35,9 @@ export class SearchPageComponent implements OnInit {
async ngOnInit() { async ngOnInit() {
this.fileService.displayedFiles.subscribe((files) => this.files = files); this.fileService.displayedFiles.subscribe((files) => this.files = files);
this.contentLoading = true;
await this.fileService.findFiles([], [new SortKey("FileImportedTime", "Ascending", undefined)]) await this.fileService.findFiles([], [new SortKey("FileImportedTime", "Ascending", undefined)])
this.contentLoading = false;
} }
async onFileMultiSelect(files: File[]) { async onFileMultiSelect(files: File[]) {

Loading…
Cancel
Save