Add option to edit filters

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/4/head
trivernis 3 years ago
parent bab9782203
commit 2f5e6d8bcb

@ -143,7 +143,7 @@ export class FilterQueryBuilder {
} }
break; break;
case "FileSize": case "FileSize":
value = this.parsePropertyValue(compareValue, parseNumber); value = this.parsePropertyValue(compareValue, parseByteSize);
if (value != undefined) { if (value != undefined) {
return this.fileSize(value[0], comparator, value[1]); return this.fileSize(value[0], comparator, value[1]);
} }
@ -280,6 +280,35 @@ function parseNumber(value: string): number | undefined {
return isNaN(num) ? undefined : num; return isNaN(num) ? undefined : num;
} }
function parseByteSize(value: string): number | undefined {
const valueMappings: { [key: string]: number } = {
"TiB": 1024 ** 4,
"GiB": 1024 ** 3,
"MiB": 1024 ** 2,
"KiB": 1024,
"TB": 1000 ** 4,
"GB": 1000 ** 3,
"MB": 1000 ** 2,
"KB": 1000
};
const stringValue = value.replace(/TiB|GiB|MiB|KiB|TB|GB|MB|KB$/i, "");
let number = parseNumber(stringValue);
const checkUnit = (unit: string) => value.toLowerCase().includes(unit.toLowerCase());
if (number) {
for (const key of Object.keys(valueMappings)) {
if (checkUnit(key)) {
console.log("key", key, "valueMapping", valueMappings[key]);
number *= valueMappings[key];
console.log("number", number);
break;
}
}
}
return number;
}
function parseDate(value: string): Date | undefined { function parseDate(value: string): Date | undefined {
const date = Date.parse(value); const date = Date.parse(value);

@ -6,6 +6,7 @@
(keydown.enter)="addExpressionByInput()" (keydown.enter)="addExpressionByInput()"
[formControl]="formControl" [formControl]="formControl"
[matAutocomplete]="auto" [matAutocomplete]="auto"
[value]="this.value"
matInput> matInput>
<ng-content></ng-content> <ng-content></ng-content>
<mat-autocomplete #auto <mat-autocomplete #auto

@ -19,6 +19,7 @@ type AutocompleteEntry = {
}) })
export class FilterInputComponent implements OnChanges { export class FilterInputComponent implements OnChanges {
@Input() value: string | undefined;
@Input() availableTags: Tag[] = []; @Input() availableTags: Tag[] = [];
@Output() filterAdded = new EventEmitter<FilterExpression>(); @Output() filterAdded = new EventEmitter<FilterExpression>();
@ -57,6 +58,9 @@ export class FilterInputComponent implements OnChanges {
); );
this.tagsForAutocomplete = this.availableTags.map( this.tagsForAutocomplete = this.availableTags.map(
t => t.getNormalizedOutput()); t => t.getNormalizedOutput());
if (this.value) {
this.formControl.setValue(this.value);
}
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
@ -64,6 +68,9 @@ export class FilterInputComponent implements OnChanges {
this.tagsForAutocomplete = this.availableTags.map( this.tagsForAutocomplete = this.availableTags.map(
t => t.getNormalizedOutput()); t => t.getNormalizedOutput());
} }
if (changes["value"] && this.value) {
this.formControl.setValue(this.value);
}
} }
public addExpressionByInput(): void { public addExpressionByInput(): void {
@ -72,7 +79,6 @@ export class FilterInputComponent implements OnChanges {
return; return;
} }
const expressions = FilterQueryBuilder.buildFilterExpressionsFromString(this.formControl.value); const expressions = FilterQueryBuilder.buildFilterExpressionsFromString(this.formControl.value);
console.log(this.formControl.value, expressions);
let valid: boolean; let valid: boolean;

@ -4,7 +4,9 @@
<div class="tag-input-list-and-actions"> <div class="tag-input-list-and-actions">
<div #tagInputList class="tag-input-list"> <div #tagInputList class="tag-input-list">
<div class="tag-input-list-inner"> <div class="tag-input-list-inner">
<div (click)="removeFilterExpression(filter)" *ngFor="let filter of filters.getFilters()" <div (click)="removeFilterExpression(filter)"
(contextmenu)="this.contextMenuFilters.onContextMenu($event); this.contextMenuFilter = filter"
*ngFor="let filter of filters.getFilters()"
class="tag-input-item" class="tag-input-item"
mat-ripple> mat-ripple>
<app-filter-expression-item [filter]="filter"></app-filter-expression-item> <app-filter-expression-item [filter]="filter"></app-filter-expression-item>
@ -19,6 +21,7 @@
<app-filter-input (filterAdded)="addFilterExpression($event); searchForFiles()" <app-filter-input (filterAdded)="addFilterExpression($event); searchForFiles()"
[availableTags]="getValidSearchTags()" [availableTags]="getValidSearchTags()"
[value]="this.initialFilterInputValue"
class="full-width"> class="full-width">
<button (click)="openFilterDialog()" class="filter-dialog-button" mat-button> <button (click)="openFilterDialog()" class="filter-dialog-button" mat-button>
<ng-icon name="mat-filter-alt"></ng-icon> <ng-icon name="mat-filter-alt"></ng-icon>
@ -62,3 +65,12 @@
</button> </button>
</app-context-menu> </app-context-menu>
<app-context-menu #contextMenuFilters>
<button (click)="this.removeFilterExpression(this.contextMenuFilter!)" *ngIf="contextMenuFilter" mat-menu-item>
Remove
</button>
<button (click)="this.removeFilterExpression(this.contextMenuFilter!); this.addFilterToInput(this.contextMenuFilter!)"
*ngIf="contextMenuFilter"
mat-menu-item>Edit
</button>
</app-context-menu>

@ -9,7 +9,8 @@ import {clipboard} from "@tauri-apps/api";
import {TabState} from "../../../../models/TabState"; import {TabState} from "../../../../models/TabState";
import {FilterQueryBuilder} from "../../../../../api/models/FilterQueryBuilder"; import {FilterQueryBuilder} from "../../../../../api/models/FilterQueryBuilder";
import {SearchFilters} from "../../../../../api/models/SearchFilters"; import {SearchFilters} from "../../../../../api/models/SearchFilters";
import {FilterExpression} from "../../../../../api/api-types/files"; import {FilterExpression,} from "../../../../../api/api-types/files";
import {filterExpressionToString} from "../../../../utils/filter-utils";
@Component({ @Component({
@ -29,10 +30,11 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
@Output() searchStartEvent = new EventEmitter<void>(); @Output() searchStartEvent = new EventEmitter<void>();
@Output() searchEndEvent = new EventEmitter<void>(); @Output() searchEndEvent = new EventEmitter<void>();
@ViewChild("tagInput") tagInput!: ElementRef<HTMLInputElement>;
@ViewChild("tagInputList") inputList!: ElementRef; @ViewChild("tagInputList") inputList!: ElementRef;
public contextMenuTag: Tag | undefined; public contextMenuTag: Tag | undefined;
public contextMenuFilter: FilterExpression | undefined = undefined;
public initialFilterInputValue: string | undefined;
constructor( constructor(
private errorBroker: ErrorBrokerService, private errorBroker: ErrorBrokerService,
@ -136,4 +138,8 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
public async copyToClipboard(text: string) { public async copyToClipboard(text: string) {
await clipboard.writeText(text); await clipboard.writeText(text);
} }
public addFilterToInput(param: FilterExpression): void {
this.initialFilterInputValue = filterExpressionToString(param);
}
} }

@ -1,5 +1,6 @@
import {Component, Input, OnChanges, OnInit, SimpleChanges} from "@angular/core"; import {Component, Input, OnChanges, OnInit, SimpleChanges} from "@angular/core";
import {PropertyQuery, ValueComparator} from "../../../../../../../api/api-types/files"; import {PropertyQuery} from "../../../../../../../api/api-types/files";
import {propertyQueryToString} from "../../../../../../utils/filter-utils";
@Component({ @Component({
selector: "app-property-query-item", selector: "app-property-query-item",
@ -15,87 +16,13 @@ export class PropertyQueryItemComponent implements OnInit, OnChanges {
constructor() { constructor() {
} }
private static buildExpression(property: string, comparator: string, value: string): string {
return `.${property} ${comparator} ${value}`;
}
public ngOnInit(): void { public ngOnInit(): void {
this.stringExpression = this.getStringExpression(); this.stringExpression = propertyQueryToString(this.propertyQuery);
} }
public ngOnChanges(changes: SimpleChanges): void { public ngOnChanges(changes: SimpleChanges): void {
if (changes["propertyQuery"]) { if (changes["propertyQuery"]) {
this.stringExpression = this.getStringExpression(); this.stringExpression = propertyQueryToString(this.propertyQuery);
}
}
public getStringExpression(): string {
if ("Status" in this.propertyQuery) {
return PropertyQueryItemComponent.buildExpression("Status", "is", this.propertyQuery.Status);
} else if ("FileSize" in this.propertyQuery) {
return PropertyQueryItemComponent.buildExpression(
"FileSize",
this.getComparator(this.propertyQuery.FileSize),
this.getValue(this.propertyQuery.FileSize).toString()
);
} else if ("ImportedTime" in this.propertyQuery) {
return PropertyQueryItemComponent.buildExpression(
"ImportedTime",
this.getComparator(this.propertyQuery.ImportedTime),
this.getValue(this.propertyQuery.ImportedTime).toISOString()
);
} else if ("ChangedTime" in this.propertyQuery) {
return PropertyQueryItemComponent.buildExpression(
"ChangedTime",
this.getComparator(this.propertyQuery.ChangedTime),
this.getValue(this.propertyQuery.ChangedTime).toISOString()
);
} else if ("CreatedTime" in this.propertyQuery) {
return PropertyQueryItemComponent.buildExpression(
"CreatedTime",
this.getComparator(this.propertyQuery.CreatedTime),
this.getValue(this.propertyQuery.CreatedTime).toISOString()
);
} else if ("TagCount" in this.propertyQuery) {
return PropertyQueryItemComponent.buildExpression(
"TagCount",
this.getComparator(this.propertyQuery.TagCount),
this.getValue(this.propertyQuery.TagCount).toString()
);
} else if ("Cd" in this.propertyQuery) {
return PropertyQueryItemComponent.buildExpression("ContentDescriptor", "is", this.propertyQuery.Cd);
} else if ("Id" in this.propertyQuery) {
return PropertyQueryItemComponent.buildExpression("FileId", "is", this.propertyQuery.Id.toString());
} else {
return "Invalid Expression";
}
}
public getComparator(value: ValueComparator<any>): "=" | "<" | ">" | "between" {
if ("Greater" in value) {
return ">";
} else if ("Equal" in value) {
return "=";
} else if ("Less" in value) {
return "<";
} else {
return "between";
}
}
public getValue<T>(value: ValueComparator<T>): T {
const singleValueKeys: ("Greater" | "Equal" | "Less")[] = ["Greater", "Equal", "Less"];
for (const key of singleValueKeys) {
if (key in value) {
//@ts-ignore
return value[key];
}
}
if ("Between" in value) {
return value.Between[0];
} else {
return "" as unknown as T; // unreachable
} }
} }
} }

@ -0,0 +1,102 @@
import {FilterExpression, FilterQuery, PropertyQuery, TagQuery, ValueComparator} from "../../api/api-types/files";
export function filterExpressionToString(expression: FilterExpression) {
let stringExpression = "";
if ("OrExpression" in expression) {
for (const query of expression.OrExpression) {
stringExpression += filterQueryToString(query) + " OR ";
}
stringExpression = stringExpression.replace(/ OR $/, "");
} else {
stringExpression += filterQueryToString(expression.Query);
}
return stringExpression;
}
function filterQueryToString(query: FilterQuery): string {
if ("Tag" in query) {
return tagQueryToString(query.Tag);
} else {
return propertyQueryToString(query.Property);
}
}
function tagQueryToString(tagQuery: TagQuery): string {
return `${tagQuery.negate ? "-" : ""}${tagQuery.tag}`;
}
export function propertyQueryToString(propertyQuery: PropertyQuery): string {
if ("Status" in propertyQuery) {
return buildExpression("Status", "is", propertyQuery.Status);
} else if ("FileSize" in propertyQuery) {
return buildExpression(
"FileSize",
getComparator(propertyQuery.FileSize),
getValue(propertyQuery.FileSize).toString()
);
} else if ("ImportedTime" in propertyQuery) {
return buildExpression(
"ImportedTime",
getComparator(propertyQuery.ImportedTime),
getValue(propertyQuery.ImportedTime).toISOString()
);
} else if ("ChangedTime" in propertyQuery) {
return buildExpression(
"ChangedTime",
getComparator(propertyQuery.ChangedTime),
getValue(propertyQuery.ChangedTime).toISOString()
);
} else if ("CreatedTime" in propertyQuery) {
return buildExpression(
"CreatedTime",
getComparator(propertyQuery.CreatedTime),
getValue(propertyQuery.CreatedTime).toISOString()
);
} else if ("TagCount" in propertyQuery) {
return buildExpression(
"TagCount",
getComparator(propertyQuery.TagCount),
getValue(propertyQuery.TagCount).toString()
);
} else if ("Cd" in propertyQuery) {
return buildExpression("ContentDescriptor", "is", propertyQuery.Cd);
} else if ("Id" in propertyQuery) {
return buildExpression("FileId", "is", propertyQuery.Id.toString());
} else {
return "Invalid Expression";
}
}
function getComparator(value: ValueComparator<any>): "=" | "<" | ">" | "between" {
if ("Greater" in value) {
return ">";
} else if ("Equal" in value) {
return "=";
} else if ("Less" in value) {
return "<";
} else {
return "between";
}
}
function getValue<T>(value: ValueComparator<T>): T {
const singleValueKeys: ("Greater" | "Equal" | "Less")[] = ["Greater", "Equal", "Less"];
for (const key of singleValueKeys) {
if (key in value) {
//@ts-ignore
return value[key];
}
}
if ("Between" in value) {
return value.Between[0];
} else {
return "" as unknown as T; // unreachable
}
}
function buildExpression(property: string, comparator: string, value: string): string {
return `.${property} ${comparator} ${value}`;
}
Loading…
Cancel
Save