4 changed files with 169 additions and 65 deletions
@ -0,0 +1,90 @@ |
|||
"use client"; |
|||
|
|||
import { useEffect, useMemo, useState } from "react"; |
|||
import type { PopiserverTaskPricePayload } from "@/utils/generationConfig"; |
|||
|
|||
type TaskPriceResponse = { |
|||
success?: boolean; |
|||
data?: { |
|||
taskPrice?: unknown; |
|||
}; |
|||
}; |
|||
|
|||
const taskPriceCache = new Map<string, number>(); |
|||
const taskPriceInflight = new Map<string, Promise<number | null>>(); |
|||
|
|||
function finiteNumberFromUnknown(value: unknown): number | null { |
|||
if (typeof value === "number" && Number.isFinite(value)) return value; |
|||
if (typeof value === "string") { |
|||
const parsed = Number.parseFloat(value.trim()); |
|||
return Number.isFinite(parsed) ? parsed : null; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
function getTaskPricePointAmount(response: TaskPriceResponse): number | null { |
|||
const amount = finiteNumberFromUnknown(response.data?.taskPrice); |
|||
return amount !== null && amount > 0 ? amount : null; |
|||
} |
|||
|
|||
export function usePopiserverTaskPrice(payload: PopiserverTaskPricePayload | null): number | null { |
|||
const [pointAmount, setPointAmount] = useState<number | null>(null); |
|||
const body = useMemo(() => payload ? JSON.stringify(payload) : null, [payload]); |
|||
|
|||
useEffect(() => { |
|||
if (!body) { |
|||
setPointAmount(null); |
|||
return; |
|||
} |
|||
|
|||
const cachedAmount = taskPriceCache.get(body); |
|||
if (cachedAmount !== undefined) { |
|||
setPointAmount(cachedAmount); |
|||
return; |
|||
} |
|||
|
|||
let cancelled = false; |
|||
setPointAmount(null); |
|||
|
|||
let request = taskPriceInflight.get(body); |
|||
if (!request) { |
|||
request = fetch("/api/points/calculate-task-price", { |
|||
method: "POST", |
|||
headers: { |
|||
"Content-Type": "application/json", |
|||
}, |
|||
body, |
|||
}) |
|||
.then(async (response) => { |
|||
if (!response.ok) return null; |
|||
|
|||
const data = (await response.json()) as TaskPriceResponse; |
|||
const taskPrice = getTaskPricePointAmount(data); |
|||
if (taskPrice !== null) { |
|||
taskPriceCache.set(body, taskPrice); |
|||
} |
|||
return taskPrice; |
|||
}) |
|||
.catch(() => null) |
|||
.finally(() => { |
|||
taskPriceInflight.delete(body); |
|||
}); |
|||
taskPriceInflight.set(body, request); |
|||
} |
|||
|
|||
void request.then((taskPrice) => { |
|||
if (!cancelled) setPointAmount(taskPrice); |
|||
}); |
|||
|
|||
return () => { |
|||
cancelled = true; |
|||
}; |
|||
}, [body]); |
|||
|
|||
return pointAmount; |
|||
} |
|||
|
|||
export function clearPopiserverTaskPriceCacheForTests() { |
|||
taskPriceCache.clear(); |
|||
taskPriceInflight.clear(); |
|||
} |
|||
Loading…
Reference in new issue