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