95 lines
2.4 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
}
|