Refactor SortKey to store backend representation internally

Signed-off-by: trivernis <trivernis@protonmail.com>
pull/12/head
trivernis 2 years ago
parent 5aadd9c245
commit c0dca663b0
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -161,9 +161,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95"
checksum = "03588e54c62ae6d763e2a80090d50353b785795361b4ff5b3bf0a5097fc31c0b"
dependencies = [
"generic-array",
]
@ -506,9 +506,9 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.6"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762"
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@ -519,9 +519,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.6"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120"
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
dependencies = [
"cfg-if 1.0.0",
"lazy_static",
@ -799,9 +799,9 @@ dependencies = [
[[package]]
name = "futf"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b"
checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
dependencies = [
"mac",
"new_debug_unreachable",
@ -1416,9 +1416,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.116"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74"
checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
[[package]]
name = "lock_api"
@ -1499,7 +1499,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "mediarepo-api"
version = "0.28.0"
version = "0.28.1"
dependencies = [
"async-trait",
"bromine",
@ -1507,7 +1507,7 @@ dependencies = [
"directories",
"futures",
"mime_guess",
"parking_lot",
"parking_lot 0.12.0",
"pathsearch",
"serde",
"serde_json",
@ -1865,7 +1865,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
"parking_lot_core 0.8.5",
]
[[package]]
name = "parking_lot"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"lock_api",
"parking_lot_core 0.9.0",
]
[[package]]
@ -1882,6 +1892,19 @@ dependencies = [
"winapi",
]
[[package]]
name = "parking_lot_core"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2f4f894f3865f6c0e02810fc597300f34dc2510f66400da262d8ae10e75767d"
dependencies = [
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "pathdiff"
version = "0.2.1"
@ -2377,7 +2400,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver 1.0.4",
"semver 1.0.5",
]
[[package]]
@ -2425,9 +2448,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "2.6.0"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fed7948b6c68acbb6e20c334f55ad635dc0f75506963de4464289fbd3b051ac"
checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
dependencies = [
"bitflags",
"core-foundation 0.9.2",
@ -2438,9 +2461,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.6.0"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a57321bf8bc2362081b2599912d2961fe899c0efadf1b4b2f8d48b3e253bb96c"
checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556"
dependencies = [
"core-foundation-sys 0.8.3",
"libc",
@ -2477,9 +2500,9 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.4"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7"
[[package]]
name = "semver-parser"
@ -2654,14 +2677,14 @@ dependencies = [
[[package]]
name = "string_cache"
version = "0.8.2"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6"
checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26"
dependencies = [
"lazy_static",
"new_debug_unreachable",
"parking_lot",
"phf_shared 0.8.0",
"parking_lot 0.11.2",
"phf_shared 0.10.0",
"precomputed-hash",
"serde",
]
@ -2811,7 +2834,7 @@ dependencies = [
"ndk-glue",
"ndk-sys",
"objc",
"parking_lot",
"parking_lot 0.11.2",
"raw-window-handle 0.3.4",
"scopeguard",
"serde",
@ -2857,7 +2880,7 @@ dependencies = [
"rand 0.8.4",
"raw-window-handle 0.3.4",
"rfd",
"semver 1.0.4",
"semver 1.0.5",
"serde",
"serde_json",
"serde_repr",
@ -3088,9 +3111,9 @@ dependencies = [
[[package]]
name = "tracing"
version = "0.1.29"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
@ -3100,9 +3123,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
version = "0.1.18"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e"
checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716"
dependencies = [
"proc-macro2 1.0.36",
"quote 1.0.15",
@ -3111,11 +3134,12 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.21"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23"
dependencies = [
"lazy_static",
"valuable",
]
[[package]]
@ -3131,9 +3155,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.3.7"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22"
checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa"
dependencies = [
"ansi_term",
"lazy_static",
@ -3235,6 +3259,12 @@ dependencies = [
"getrandom 0.2.4",
]
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
@ -3476,6 +3506,49 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceb069ac8b2117d36924190469735767f0990833935ab430155e71a44bafe148"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b"
[[package]]
name = "windows_i686_gnu"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58"
[[package]]
name = "windows_i686_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4"
[[package]]
name = "windows_x86_64_gnu"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354"
[[package]]
name = "windows_x86_64_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561"
[[package]]
name = "winres"
version = "0.1.12"

@ -10,20 +10,20 @@ edition = "2018"
build = "src/build.rs"
[build-dependencies]
tauri-build = { version = "^1.0.0-beta.4" }
tauri-build = "1.0.0-beta.4"
[dependencies]
serde_json = "^1.0"
serde = { version = "^1.0", features = ["derive"] }
thiserror = "^1.0.30"
typemap_rev = "^0.1.5"
serde_json = "1.0.78"
serde = { version = "1.0.136", features = ["derive"] }
thiserror = "1.0.30"
typemap_rev = "0.1.5"
[dependencies.tauri]
version = "^1.0.0-beta.8"
version = "1.0.0-beta.8"
features = ["dialog-all", "path-all", "shell-all"]
[dependencies.tracing-subscriber]
version = "^0.3.0"
version = "0.3.8"
features = ["env-filter"]
[dependencies.mediarepo-api]

@ -4,12 +4,14 @@ import {ApiFunction} from "./api-types/functions";
import {
AddLocalFileREquest,
AddRepositoryRequest,
AddSortingPresetRequest,
ChangeFileTagsRequest,
CheckDaemonRunningRequest,
CheckLocalRepositoryExistsRequest,
CreateTagsRequest,
DeleteFileRequest,
DeleteRepositoryRequest,
DeleteSortingPresetRequest,
DeleteThumbnailsRequest,
FindFilesRequest,
GetFileMetadataRequest,
@ -31,6 +33,7 @@ import {
import {RepositoryData, RepositoryMetadata, SizeMetadata} from "./api-types/repo";
import {CdTagMappings, NamespaceData, TagData} from "./api-types/tags";
import {ShortCache} from "./ShortCache";
import {SortingPresetData} from "./api-types/presets";
export class MediarepoApi {
@ -184,6 +187,18 @@ export class MediarepoApi {
return this.invokePlugin(ApiFunction.RunJob, request);
}
public static async getAllSortingPresets(): Promise<SortingPresetData[]> {
return ShortCache.cached("sorting-presets", () => this.invokePlugin(ApiFunction.GetAllSortingPresets), 1000);
}
public static async addSortingPreset(request: AddSortingPresetRequest): Promise<SortingPresetData> {
return this.invokePlugin(ApiFunction.AddSortingPreset, request);
}
public static async deleteSortingPreset(request: DeleteSortingPresetRequest): Promise<void> {
return this.invokePlugin(ApiFunction.DeleteSortingPreset, request);
}
private static async invokePlugin<T>(fn: ApiFunction, args?: any): Promise<T> {
return invoke<T>(`plugin:mediarepo|${fn}`, args);
}

@ -41,7 +41,7 @@ export type ValueComparator<T> =
| { Greater: T }
| { Between: T[] }
export type SortKey = { Namespace: SortNamespace }
export type SortKeyData = { Namespace: SortNamespace }
| { FileName: SortDirection }
| { FileSize: SortDirection }
| { FileImportedTime: SortDirection }

@ -40,4 +40,8 @@ export enum ApiFunction {
SetFrontendState = "set_frontend_state",
// jobs
RunJob = "run_job",
// presets
GetAllSortingPresets = "all_sorting_presets",
AddSortingPreset = "add_sorting_preset",
DeleteSortingPreset = "delete_sorting_preset",
}

@ -0,0 +1,6 @@
import {SortKeyData} from "./files";
export type SortingPresetData = {
id: number,
keys: SortKeyData[],
}

@ -1,4 +1,4 @@
import {FileOsMetadata, FileStatus, FilterExpression, SortKey} from "./files";
import {FileOsMetadata, FileStatus, FilterExpression, SortKeyData} from "./files";
import {RepositoryData, SizeType} from "./repo";
import {JobType} from "./job";
@ -40,7 +40,7 @@ export type GetSizeRequest = {
export type FindFilesRequest = {
filters: FilterExpression[],
sortBy: SortKey[]
sortBy: SortKeyData[]
};
export type UpdateFileNameRequest = {
@ -107,4 +107,12 @@ export type SetFrontendStateRequest = {
export type RunJobRequest = {
jobType: JobType,
}
};
export type AddSortingPresetRequest = {
sort_keys: SortKeyData[]
};
export type DeleteSortingPresetRequest = {
id: number
};

@ -0,0 +1,106 @@
import {SortDirection, SortKeyData} from "../api-types/files";
export type SortType =
"Namespace"
| "FileName"
| "FileSize"
| "FileImportedTime"
| "FileCreatedTime"
| "FileChangeTime"
| "FileType"
| "NumTags";
export class SortKey {
constructor(private data: SortKeyData) {
this.data = data;
}
public get sortType(): SortType {
return Reflect.ownKeys(this.data)[0] as SortType;
}
public set sortType(value: SortType) {
if (value == "Namespace") {
this.data = {
Namespace: {
direction: this.sortDirection,
name: ""
}
};
} else {
this.data = {
[value]: {
direction: this.sortDirection
}
} as SortKeyData;
}
}
public get sortDirection(): SortDirection {
if ("Namespace" in this.data) {
return this.data.Namespace.direction;
} else {
// @ts-ignore
return this.data[this.sortType];
}
}
public set sortDirection(value: SortDirection) {
const sortType = this.sortType;
if ("Namespace" in this.data) {
this.data.Namespace.direction = value;
} else {
// @ts-ignore
this.data[this.sortType] = value;
}
}
public get namespaceName(): string | undefined {
if ("Namespace" in this.data) {
return this.data.Namespace.name;
}
return undefined;
}
public set namespaceName(value: string | undefined) {
if (value && "Namespace" in this.data) {
this.data.Namespace.name = value;
}
}
public static fromValues(
sortType: SortType,
sortDirection: SortDirection,
namespaceName: string | undefined
) {
let data;
if (sortType === "Namespace") {
data = {
Namespace: {
name: namespaceName!,
direction: sortDirection
}
};
} else {
data = {
[sortType]: sortDirection
} as SortKeyData;
}
return new SortKey(data);
}
public toString(): string {
if (this.sortType == "Namespace") {
return `${this.sortType} '${this.namespaceName}' ${this.sortDirection}`;
} else {
return `${this.sortType} ${this.sortDirection}`;
}
}
public rawData(): SortKeyData {
return this.data;
}
}

@ -0,0 +1,20 @@
import {SortKey} from "./SortKey";
import {SortingPresetData} from "../api-types/presets";
export class SortingPreset {
private readonly _id: number;
private keys: SortKey[];
constructor(presetData: SortingPresetData) {
this._id = presetData.id;
this.keys = presetData.keys.map(SortKey.fromRawData);
}
public get id(): number {
return this._id;
}
public get sortKeys(): SortKey[] {
return this.sortKeys;
}
}

@ -9,7 +9,7 @@ import {
Output,
ViewChild
} from "@angular/core";
import {SortKey} from "../../../../models/SortKey";
import {SortKey} from "../../../../../api/models/SortKey";
import {MatDialog} from "@angular/material/dialog";
import {SortDialogComponent} from "./sort-dialog/sort-dialog.component";
import {LoggingService} from "../../../../services/logging/logging.service";
@ -128,10 +128,8 @@ export class FileSearchComponent implements AfterViewChecked, OnInit {
public openSortDialog() {
const sortEntries = this.sortExpression.map(
key => JSON.parse(JSON.stringify(key))).map(
key => new SortKey(key.sortType, key.sortDirection,
key.namespaceName
));
key => JSON.parse(JSON.stringify(key.rawData()))).map(
data => new SortKey(data));
const openedDialog = this.dialog.open(SortDialogComponent, {
minWidth: "40vw",
data: {

@ -1,6 +1,6 @@
import {ChangeDetectionStrategy, Component, Inject} from "@angular/core";
import {MAT_DIALOG_DATA, MatDialogRef} from "@angular/material/dialog";
import {SortKey} from "../../../../../models/SortKey";
import {SortKey} from "../../../../../../api/models/SortKey";
import {CdkDragDrop, moveItemInArray} from "@angular/cdk/drag-drop";
import {Namespace} from "../../../../../../api/models/Namespace";
import {TagService} from "../../../../../services/tag/tag.service";
@ -22,12 +22,13 @@ export class SortDialogComponent {
constructor(public tagService: TagService, public dialogRef: MatDialogRef<SortDialogComponent>, @Inject(
MAT_DIALOG_DATA) data: any) {
this.sortEntries = data.sortEntries;
console.debug(this.sortEntries);
tagService.namespaces.subscribe(
namespaces => this.namespaces = namespaces);
}
addNewSortKey() {
const sortKey = new SortKey("FileName", "Ascending", undefined);
const sortKey = SortKey.fromValues("FileName", "Ascending", undefined);
this.sortEntries.push(sortKey);
}

@ -1,35 +0,0 @@
export class SortKey {
constructor(
public sortType: "Namespace" | "FileName" | "FileSize" | "FileImportedTime" | "FileCreatedTime" | "FileChangeTime" | "FileType" | "NumTags",
public sortDirection: "Ascending" | "Descending",
public namespaceName: string | undefined
) {
}
public toString(): string {
if (this.sortType == "Namespace") {
return `${this.sortType} '${this.namespaceName}' ${this.sortDirection}`;
} else {
return `${this.sortType} ${this.sortDirection}`;
}
}
public toBackendType(): any {
if (this.sortType == "Namespace") {
return {
"Namespace": {
direction: this.sortDirection,
name: this.namespaceName
}
};
} else {
let returnObj: any = {};
returnObj[this.sortType] = this.sortDirection;
return returnObj;
}
}
}

@ -2,10 +2,11 @@ import {BehaviorSubject} from "rxjs";
import {TabCategory} from "./TabCategory";
import {FileService} from "../services/file/file.service";
import {File} from "../../api/models/File";
import {SortKey} from "./SortKey";
import {SortKey} from "../../api/models/SortKey";
import {debounceTime} from "rxjs/operators";
import {mapNew} from "../../api/models/adaptors";
import {SearchFilters} from "../../api/models/SearchFilters";
import {SortKeyData} from "../../api/api-types/files";
export class TabState {
public uuid: number;
@ -17,7 +18,7 @@ export class TabState {
public files = new BehaviorSubject<File[]>([]);
public filters = new BehaviorSubject<SearchFilters>(new SearchFilters([]));
public sortKeys = new BehaviorSubject<SortKey[]>(
[new SortKey(
[SortKey.fromValues(
"FileImportedTime",
"Ascending",
undefined
@ -50,14 +51,7 @@ export class TabState {
dto.category,
fileService
);
const sortKeys = dto.sortKeys.map(
(s: { sortType: any, sortDirection: any, namespaceName: any }) =>
new SortKey(
s.sortType,
s.sortDirection,
s.namespaceName
)
);
const sortKeys = dto.sortKeys.map((data: SortKeyData) => new SortKey(data));
state.filters.next(new SearchFilters(dto.filters ?? []));
state.sortKeys.next(sortKeys);
state.mode.next(dto.mode ?? "grid");
@ -90,7 +84,7 @@ export class TabState {
uuid: this.uuid,
category: this.category,
filters: this.filters.value.getFilters(),
sortKeys: this.sortKeys.value,
sortKeys: this.sortKeys.value.map(key => key.rawData()),
mode: this.mode.value,
selectedFileHash: this.selectedCD.value,
files: this.category === TabCategory.Import ? this.files.value.map(

@ -1,7 +1,7 @@
import {Inject, Injectable} from "@angular/core";
import {File} from "../../../api/models/File";
import {DomSanitizer, SafeResourceUrl} from "@angular/platform-browser";
import {SortKey} from "../../models/SortKey";
import {SortKey} from "../../../api/models/SortKey";
import {MediarepoApi} from "../../../api/Api";
import {mapMany, mapNew} from "../../../api/models/adaptors";
import {FileMetadata, FileStatus} from "../../../api/api-types/files";
@ -26,7 +26,7 @@ export class FileService {
return MediarepoApi.findFiles(
{
filters: filters.getFilters(),
sortBy: sortBy.map(k => k.toBackendType())
sortBy: sortBy.map(k => k.rawData())
})
.then(mapMany(mapNew(File)));
}

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { PresetService } from './preset.service';
describe('PresetService', () => {
let service: PresetService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(PresetService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

@ -0,0 +1,17 @@
import {Injectable} from "@angular/core";
import {SortingPreset} from "../../../api/models/SortingPreset";
import {MediarepoApi} from "../../../api/Api";
import {mapMany, mapNew} from "../../../api/models/adaptors";
@Injectable({
providedIn: "root"
})
export class PresetService {
constructor() {
}
public async getAllSortingPresets(): Promise<SortingPreset[]> {
return MediarepoApi.getAllSortingPresets().then(mapMany(mapNew(SortingPreset)));
}
}
Loading…
Cancel
Save