From 918f4b3bb8ab0510bd2e3837b56d4a2aa98548c1 Mon Sep 17 00:00:00 2001 From: trivernis Date: Sat, 15 Jan 2022 18:15:23 +0100 Subject: [PATCH] Add status filter checkboxes Signed-off-by: trivernis --- mediarepo-ui/src/api/models/SearchFilters.ts | 30 ++++--- .../filter-input/filter-input.component.ts | 20 ++++- .../file-search/file-search.component.html | 9 ++- .../file-search/file-search.component.scss | 9 +++ .../file-search/file-search.component.ts | 80 ++++++++++++++++++- 5 files changed, 133 insertions(+), 15 deletions(-) diff --git a/mediarepo-ui/src/api/models/SearchFilters.ts b/mediarepo-ui/src/api/models/SearchFilters.ts index dbe1fa7..7d3b727 100644 --- a/mediarepo-ui/src/api/models/SearchFilters.ts +++ b/mediarepo-ui/src/api/models/SearchFilters.ts @@ -27,16 +27,26 @@ export class SearchFilters { return !!this.filters.find(f => deepEqual(f, expression)); } + public hasSubfilter(query: FilterQuery): boolean { + return !!this.filters.find(f => { + if ("OrExpression" in f) { + return !!f.OrExpression.find(q => deepEqual(q, query)); + } else { + return deepEqual(f.Query, query); + } + }); + } + public addFilterExpression(filter: FilterExpression) { this.filters.push(filter); this.processChangesToOrExpressions(); } - public addFilter(filter: FilterQuery, index: number) { + public addFilter(filter: FilterExpression, index: number) { this.filters = [...this.filters.slice( 0, index - ), { Query: filter }, ...this.filters.slice(index)]; + ), filter, ...this.filters.slice(index)]; } public appendFilter(filter: FilterQuery) { @@ -59,7 +69,7 @@ export class SearchFilters { } else { const otherQuery = expressionEntry["Query"]!; let entry = expressionEntry as unknown as { OrExpression: FilterQuery[], Query: undefined }; - entry["Query"] = undefined; + delete entry["Query"]; entry["OrExpression"] = [otherQuery, filter]; } } @@ -70,10 +80,12 @@ export class SearchFilters { return false; } else { f["OrExpression"] = f["OrExpression"]!.filter(q => !deepEqual(q, queryToRemove)); - return (f["OrExpression"]!.length === 0); + return (!f["OrExpression"] || f["OrExpression"]!.length === 0); } }); - this.filters.splice(index); + if (index >= 0) { + this.filters.splice(index, 1); + } this.processChangesToOrExpressions(); } @@ -94,13 +106,13 @@ export class SearchFilters { const filters_to_remove: FilterExpression[] = []; for (const filter of this.filters) { - if ("OrExpression" in filter) { - if (filter.OrExpression.length === 1) { + if ("OrExpression" in filter && !("Query" in filter)) { + if (filter.OrExpression && filter.OrExpression.length === 1) { const query = filter.OrExpression[0]; let newFilter = filter as unknown as FilterExpressionQuery & { OrExpression: undefined }; - newFilter["OrExpression"] = undefined; + delete newFilter["OrExpression"]; newFilter.Query = query; - } else if (filter.OrExpression.length === 0) { + } else if (!filter.OrExpression || filter.OrExpression.length === 0) { filters_to_remove.push(filter); } } diff --git a/mediarepo-ui/src/app/components/shared/input/filter-input/filter-input.component.ts b/mediarepo-ui/src/app/components/shared/input/filter-input/filter-input.component.ts index e235df8..e52ce67 100644 --- a/mediarepo-ui/src/app/components/shared/input/filter-input/filter-input.component.ts +++ b/mediarepo-ui/src/app/components/shared/input/filter-input/filter-input.component.ts @@ -167,9 +167,25 @@ export class FilterInputComponent implements OnChanges { } if (validComparators.length == 1) { - return validProperties.map(p => validComparators.map(c => this.propertyQueriesWithValues[p].map(v => `${p} ${c} ${v ?? value}`.trim())).flat()).flat(); + return validProperties.map(p => validComparators.filter(c => this.filterComparatorsForProperty( + c, + p + )).map(c => this.propertyQueriesWithValues[p].map(v => `${p} ${c} ${v ?? value}`.trim())).flat()).flat(); } else { - return validProperties.map(p => validComparators.map(c => `${p} ${c} ${value}`.trim())).flat(); + return validProperties.map(p => validComparators.filter((c) => this.filterComparatorsForProperty(c, p)).map( + c => `${p} ${c} ${value}`.trim())).flat(); + } + } + + private filterComparatorsForProperty(comparator: string, property: string): boolean { + console.log(comparator, property); + switch (property) { + case ".status": + case ".fileId": + case ".contentDescriptor": + return comparator === "="; + default: + return true; } } } diff --git a/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.html b/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.html index 9910822..7a02467 100644 --- a/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.html +++ b/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.html @@ -1,6 +1,13 @@
- +
+ Imported + + Archived + + Deleted + +
diff --git a/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.scss b/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.scss index 1a0ebcb..e4273c7 100644 --- a/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.scss +++ b/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.scss @@ -123,3 +123,12 @@ mat-divider { .file-tag-list { position: relative; } + +.status-selector { + display: flex; + align-items: center; + justify-content: space-between; + width: calc(100% - 2em); + margin-left: 1em; + margin-right: 1em; +} diff --git a/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.ts b/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.ts index 399025d..2eb3422 100644 --- a/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.ts +++ b/mediarepo-ui/src/app/components/shared/sidebar/file-search/file-search.component.ts @@ -9,8 +9,9 @@ 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"; +import {FileStatus, FilterExpression,} from "../../../../../api/api-types/files"; import {filterExpressionToString} from "../../../../utils/filter-utils"; +import {MatCheckboxChange} from "@angular/material/checkbox"; @Component({ @@ -36,6 +37,12 @@ export class FileSearchComponent implements AfterViewChecked, OnInit { public contextMenuFilter: FilterExpression | undefined = undefined; public initialFilterInputValue: string | undefined; + public displayImported = true; + public displayArchived = true; + public displayDeleted = false; + + private needsScroll = false; + constructor( private errorBroker: ErrorBrokerService, public dialog: MatDialog @@ -45,11 +52,14 @@ export class FileSearchComponent implements AfterViewChecked, OnInit { public async ngOnInit() { this.state.filters.subscribe(f => this.filters = f); this.state.sortKeys.subscribe(s => this.sortExpression = s); + this.applyStatusFromFilters(); await this.searchForFiles(); } public ngAfterViewChecked(): void { - this.inputList.nativeElement.scrollLeft = this.inputList.nativeElement.scrollWidth; + if (this.needsScroll) { + this.inputList.nativeElement.scrollLeft = this.inputList.nativeElement.scrollWidth; + } } public async searchForFiles() { @@ -67,6 +77,7 @@ export class FileSearchComponent implements AfterViewChecked, OnInit { this.filters.addFilterExpression(filter); this.state.setTagFilters(this.filters); + this.needsScroll = true; } public addTagFilter(filterString: string) { @@ -93,6 +104,7 @@ export class FileSearchComponent implements AfterViewChecked, OnInit { public async removeFilterExpression(expr: FilterExpression) { this.filters.removeFilter(expr); this.state.setTagFilters(this.filters); + this.needsScroll = true; } public openSortDialog() { @@ -118,7 +130,7 @@ export class FileSearchComponent implements AfterViewChecked, OnInit { public openFilterDialog(): void { const filterEntries = new SearchFilters(JSON.parse(JSON.stringify(this.filters.getFilters()))); - + const filterDialog = this.dialog.open(FilterDialogComponent, { minWidth: "25vw", maxHeight: "80vh", @@ -132,6 +144,8 @@ export class FileSearchComponent implements AfterViewChecked, OnInit { if (filterExpression !== undefined || filterExpression?.length > 0) { this.filters = filterExpression; this.state.setTagFilters(this.filters); + this.applyStatusFromFilters(); + this.needsScroll = true; } }); } @@ -143,4 +157,64 @@ export class FileSearchComponent implements AfterViewChecked, OnInit { public addFilterToInput(param: FilterExpression): void { this.initialFilterInputValue = filterExpressionToString(param); } + + public setDisplayDeleted(event: MatCheckboxChange) { + this.displayDeleted = event.checked; + this.updateStatusFilters(); + } + + public setDisplayArchived(event: MatCheckboxChange) { + this.displayArchived = event.checked; + this.updateStatusFilters(); + } + + public setDisplayImported(event: MatCheckboxChange) { + this.displayImported = event.checked; + this.updateStatusFilters(); + } + + private applyStatusFromFilters() { + const filterImported = FilterQueryBuilder.status("Imported"); + const filterArchived = FilterQueryBuilder.status("Archived"); + const filterDeleted = FilterQueryBuilder.status("Deleted"); + this.displayImported = this.filters.hasSubfilter(filterImported); + this.displayArchived = this.filters.hasSubfilter(filterArchived); + this.displayDeleted = this.filters.hasSubfilter(filterDeleted); + + if (!this.displayImported && !this.displayDeleted && !this.displayArchived) { + this.displayImported = true; + this.displayArchived = true; + this.updateStatusFilters(); + } + } + + private updateStatusFilters() { + this.deleteAllStatusFilters(); + const filter = this.buildFilterForDisplayProperty(); + this.filters.addFilter(filter, 0); + this.state.setTagFilters(this.filters); + } + + private deleteAllStatusFilters() { + for (const status of ["Imported", "Archived", "Deleted"]) { + const query = FilterQueryBuilder.status(status as FileStatus); + this.filters.removeSubfilter(query); + this.filters.removeFilter({ Query: query }); + } + this.state.setTagFilters(this.filters); + } + + private buildFilterForDisplayProperty(): FilterExpression { + const filters = []; + if (this.displayImported) { + filters.push(FilterQueryBuilder.status("Imported")); + } + if (this.displayArchived) { + filters.push(FilterQueryBuilder.status("Archived")); + } + if (this.displayDeleted) { + filters.push(FilterQueryBuilder.status("Deleted")); + } + return { OrExpression: filters }; + } }