import {AfterViewChecked, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from "@angular/core"; import {SortKey} from "../../../../models/SortKey"; import {MatDialog} from "@angular/material/dialog"; import {SortDialogComponent} from "./sort-dialog/sort-dialog.component"; import {ErrorBrokerService} from "../../../../services/error-broker/error-broker.service"; import {FilterDialogComponent} from "./filter-dialog/filter-dialog.component"; import {Tag} from "../../../../../api/models/Tag"; import {clipboard} from "@tauri-apps/api"; import {TabState} from "../../../../models/TabState"; import {FilterQueryBuilder} from "../../../../../api/models/FilterQueryBuilder"; import {SearchFilters} from "../../../../../api/models/SearchFilters"; import {FilterExpression} from "../../../../../api/api-types/files"; @Component({ selector: "app-file-search", templateUrl: "./file-search.component.html", styleUrls: ["./file-search.component.scss"] }) export class FileSearchComponent implements AfterViewChecked, OnInit { public sortExpression: SortKey[] = []; public filters: SearchFilters = new SearchFilters([]); @Input() availableTags: Tag[] = []; @Input() contextTags: Tag[] = []; @Input() state!: TabState; @Input() tagsLoading = false; @Output() searchStartEvent = new EventEmitter(); @Output() searchEndEvent = new EventEmitter(); @ViewChild("tagInput") tagInput!: ElementRef; @ViewChild("tagInputList") inputList!: ElementRef; public contextMenuTag: Tag | undefined; constructor( private errorBroker: ErrorBrokerService, public dialog: MatDialog ) { } public async ngOnInit() { this.state.filters.subscribe(f => this.filters = f); this.state.sortKeys.subscribe(s => this.sortExpression = s); await this.searchForFiles(); } public ngAfterViewChecked(): void { this.inputList.nativeElement.scrollLeft = this.inputList.nativeElement.scrollWidth; } public async searchForFiles() { this.searchStartEvent.emit(); try { await this.state.findFiles(); } catch (err) { this.errorBroker.showError(err); } this.searchEndEvent.emit(); } public addSearchQuery(queryStr: string) { let filter = FilterQueryBuilder.buildFilterFromString(queryStr); if (filter) { this.filters.removeFilter({ Query: filter }); this.filters.appendFilter(filter); } queryStr = queryStr.replace(/^-/g, ""); this.state.setTagFilters(this.filters); } public getValidSearchTags(): Tag[] { return this.availableTags.filter(t => !this.filters.hasFilter({ Query: FilterQueryBuilder.tag( t.getNormalizedOutput(), false ) })); } public async removeAllSearchTags() { this.filters = new SearchFilters([]); this.state.setTagFilters(this.filters); } public async removeFilterExpression(expr: FilterExpression) { this.filters.removeFilter(expr); this.state.setTagFilters(this.filters); } public openSortDialog() { const sortEntries = this.sortExpression.map( key => JSON.parse(JSON.stringify(key))).map( key => new SortKey(key.sortType, key.sortDirection, key.namespaceName )); const openedDialog = this.dialog.open(SortDialogComponent, { minWidth: "40vw", data: { sortEntries, }, disableClose: true, }); openedDialog.afterClosed().subscribe(async (sortExpression) => { if (sortExpression) { this.sortExpression = sortExpression; this.state.setSortKeys(this.sortExpression); } }); } public openFilterDialog(): void { const filterEntries = this.filters; const filterDialog = this.dialog.open(FilterDialogComponent, { minWidth: "25vw", maxHeight: "80vh", data: { filterEntries, availableTags: this.availableTags, }, disableClose: true, }); filterDialog.afterClosed().subscribe(async (filterExpression) => { if (filterExpression !== undefined || filterExpression?.length > 0) { this.filters = filterExpression; this.state.setTagFilters(this.filters); } }); } public async copyToClipboard(text: string) { await clipboard.writeText(text); } }