star-chart-search-enhancer/src/content/market/full-scan-controller.ts

95 lines
2.4 KiB
TypeScript

import type {
MarketApiFailureReason,
MarketApiResult,
MarketRowSnapshot
} from "./types";
import type { AfterSearchRates } from "./types";
interface ResultStoreLike {
setAuthorFailed(authorId: string, reason: MarketApiFailureReason): void;
setAuthorLoading(authorId: string): void;
setAuthorSuccess(authorId: string, rates: AfterSearchRates): void;
upsertMarketRow(row: MarketRowSnapshot): void;
}
interface FullScanControllerOptions {
goToNextPage(): Promise<boolean>;
hasNextPage(): boolean;
loadAuthorMetrics(authorId: string): Promise<MarketApiResult>;
readCurrentPageRows(): MarketRowSnapshot[];
resultStore: ResultStoreLike;
}
export function createFullScanController(options: FullScanControllerOptions) {
let completedScan = false;
let scanPromise: Promise<void> | null = null;
return {
ensureScanForExport() {
return ensureScan();
},
ensureScanForFilter() {
return ensureScan();
},
ensureScanForSort() {
return ensureScan();
}
};
function ensureScan(): Promise<void> {
if (completedScan) {
return Promise.resolve();
}
if (scanPromise) {
return scanPromise;
}
scanPromise = runScan().finally(() => {
scanPromise = null;
});
return scanPromise;
}
async function runScan(): Promise<void> {
do {
await scanCurrentPage();
if (!options.hasNextPage()) {
completedScan = true;
return;
}
} while (await options.goToNextPage());
completedScan = true;
}
async function scanCurrentPage(): Promise<void> {
const rows = options.readCurrentPageRows();
for (const row of rows) {
options.resultStore.upsertMarketRow(row);
if (row.hasDirectRatesSource) {
const directRates = row.rates ?? {};
const hasAllRates =
Boolean(directRates.singleVideoAfterSearchRate) &&
Boolean(directRates.personalVideoAfterSearchRate);
options.resultStore.setAuthorSuccess(row.authorId, directRates);
if (hasAllRates) {
continue;
}
}
options.resultStore.setAuthorLoading(row.authorId);
const metricsResult = await options.loadAuthorMetrics(row.authorId);
if (metricsResult.success) {
options.resultStore.setAuthorSuccess(row.authorId, metricsResult.rates);
continue;
}
options.resultStore.setAuthorFailed(row.authorId, metricsResult.reason);
}
}
}