feat: add filter and sort controller
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import {
|
||||
compareRateValues,
|
||||
parseRateLowerBound
|
||||
} from "../../shared/rate-normalizer";
|
||||
import type {
|
||||
MarketFilterState,
|
||||
MarketRecord,
|
||||
MarketSortState
|
||||
} from "./types";
|
||||
|
||||
interface ApplyFilterAndSortOptions {
|
||||
filters?: MarketFilterState;
|
||||
sort?: MarketSortState;
|
||||
}
|
||||
|
||||
export function applyFilterAndSort(
|
||||
records: MarketRecord[],
|
||||
options: ApplyFilterAndSortOptions = {}
|
||||
): MarketRecord[] {
|
||||
const filteredRecords = records.filter((record) =>
|
||||
matchesFilters(record, options.filters)
|
||||
);
|
||||
|
||||
if (!options.sort) {
|
||||
return filteredRecords;
|
||||
}
|
||||
|
||||
return [...filteredRecords].sort((leftRecord, rightRecord) =>
|
||||
compareRecords(leftRecord, rightRecord, options.sort as MarketSortState)
|
||||
);
|
||||
}
|
||||
|
||||
function matchesFilters(
|
||||
record: MarketRecord,
|
||||
filters: MarketFilterState | undefined
|
||||
): boolean {
|
||||
if (!filters) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
meetsThreshold(
|
||||
record.rates?.singleVideoAfterSearchRate,
|
||||
filters.singleVideoAfterSearchRateMin
|
||||
) &&
|
||||
meetsThreshold(
|
||||
record.rates?.personalVideoAfterSearchRate,
|
||||
filters.personalVideoAfterSearchRateMin
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function meetsThreshold(
|
||||
rateValue: string | undefined,
|
||||
minValue: number | undefined
|
||||
): boolean {
|
||||
if (minValue == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const lowerBound = parseRateLowerBound(rateValue ?? null);
|
||||
return lowerBound != null && lowerBound >= minValue;
|
||||
}
|
||||
|
||||
function compareRecords(
|
||||
leftRecord: MarketRecord,
|
||||
rightRecord: MarketRecord,
|
||||
sort: MarketSortState
|
||||
): number {
|
||||
const leftValue = leftRecord.rates?.[sort.field];
|
||||
const rightValue = rightRecord.rates?.[sort.field];
|
||||
const leftLowerBound = parseRateLowerBound(leftValue ?? null);
|
||||
const rightLowerBound = parseRateLowerBound(rightValue ?? null);
|
||||
|
||||
if (leftLowerBound == null && rightLowerBound == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (leftLowerBound == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rightLowerBound == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (leftLowerBound !== rightLowerBound) {
|
||||
return sort.direction === "asc"
|
||||
? leftLowerBound - rightLowerBound
|
||||
: rightLowerBound - leftLowerBound;
|
||||
}
|
||||
|
||||
const tieBreak = compareRateValues(leftValue, rightValue);
|
||||
return sort.direction === "asc" ? tieBreak : -tieBreak;
|
||||
}
|
||||
@@ -18,6 +18,16 @@ export interface MarketRecord extends MarketRowSnapshot {
|
||||
rates?: Required<AfterSearchRates>;
|
||||
}
|
||||
|
||||
export interface MarketFilterState {
|
||||
personalVideoAfterSearchRateMin?: number;
|
||||
singleVideoAfterSearchRateMin?: number;
|
||||
}
|
||||
|
||||
export interface MarketSortState {
|
||||
direction: "asc" | "desc";
|
||||
field: keyof Required<AfterSearchRates>;
|
||||
}
|
||||
|
||||
export type MarketApiFailureReason =
|
||||
| "bad-response"
|
||||
| "missing-rate"
|
||||
|
||||
Reference in New Issue
Block a user