122 lines
3.3 KiB
TypeScript
122 lines
3.3 KiB
TypeScript
import type {
|
|
MarketApiFailureReason,
|
|
MarketRecord,
|
|
MarketRowSnapshot
|
|
} from "./types";
|
|
import type { AfterSearchRates } from "./types";
|
|
|
|
export function createMarketResultStore() {
|
|
const records = new Map<string, MarketRecord>();
|
|
|
|
return {
|
|
getRecord(authorId: string) {
|
|
return records.get(authorId) ?? null;
|
|
},
|
|
listRecords() {
|
|
return Array.from(records.values());
|
|
},
|
|
setAuthorFailed(authorId: string, reason: MarketApiFailureReason) {
|
|
const existingRecord = ensureRecord(authorId);
|
|
existingRecord.status = "failed";
|
|
existingRecord.failureReason = reason;
|
|
},
|
|
setAuthorLoading(authorId: string) {
|
|
const existingRecord = ensureRecord(authorId);
|
|
existingRecord.status = "loading";
|
|
delete existingRecord.failureReason;
|
|
},
|
|
setAuthorSuccess(authorId: string, rates: AfterSearchRates) {
|
|
const existingRecord = ensureRecord(authorId);
|
|
existingRecord.status = "success";
|
|
existingRecord.rates = {
|
|
...existingRecord.rates,
|
|
...rates
|
|
};
|
|
delete existingRecord.failureReason;
|
|
},
|
|
upsertMarketRow(row: MarketRowSnapshot) {
|
|
const existingRecord = records.get(row.authorId);
|
|
if (existingRecord) {
|
|
existingRecord.authorName =
|
|
mergeStringValue(existingRecord.authorName, row.authorName) ??
|
|
existingRecord.authorName;
|
|
existingRecord.location = mergeStringValue(
|
|
existingRecord.location,
|
|
row.location
|
|
);
|
|
existingRecord.price21To60s = mergeStringValue(
|
|
existingRecord.price21To60s,
|
|
row.price21To60s
|
|
);
|
|
existingRecord.exportFields = mergeFieldMap(
|
|
existingRecord.exportFields,
|
|
row.exportFields
|
|
);
|
|
existingRecord.hasDirectRatesSource =
|
|
existingRecord.hasDirectRatesSource || row.hasDirectRatesSource;
|
|
existingRecord.rates = mergeFieldMap(existingRecord.rates, row.rates);
|
|
return existingRecord;
|
|
}
|
|
|
|
const nextRecord: MarketRecord = {
|
|
...row,
|
|
status: "idle"
|
|
};
|
|
records.set(row.authorId, nextRecord);
|
|
return nextRecord;
|
|
}
|
|
};
|
|
|
|
function ensureRecord(authorId: string): MarketRecord {
|
|
const existingRecord = records.get(authorId);
|
|
if (existingRecord) {
|
|
return existingRecord;
|
|
}
|
|
|
|
const nextRecord: MarketRecord = {
|
|
authorId,
|
|
authorName: authorId,
|
|
status: "idle"
|
|
};
|
|
records.set(authorId, nextRecord);
|
|
return nextRecord;
|
|
}
|
|
}
|
|
|
|
function mergeFieldMap<T extends Record<string, string | undefined>>(
|
|
current: T | undefined,
|
|
incoming: T | undefined
|
|
): T | undefined {
|
|
if (!current && !incoming) {
|
|
return undefined;
|
|
}
|
|
|
|
const merged = {
|
|
...(current ?? {})
|
|
} as Record<string, string | undefined>;
|
|
|
|
Object.entries(incoming ?? {}).forEach(([key, value]) => {
|
|
const currentValue = merged[key];
|
|
if (!hasTextValue(currentValue)) {
|
|
merged[key] = value;
|
|
}
|
|
});
|
|
|
|
return merged as T;
|
|
}
|
|
|
|
function mergeStringValue(
|
|
current: string | undefined,
|
|
incoming: string | undefined
|
|
): string | undefined {
|
|
if (!hasTextValue(current)) {
|
|
return incoming ?? current;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
function hasTextValue(value: string | undefined): boolean {
|
|
return typeof value === "string" && value.trim().length > 0;
|
|
}
|