From a922dd12c0c52f3bdf09cce0f12fd470860db069 Mon Sep 17 00:00:00 2001 From: gahusb Date: Wed, 15 Apr 2026 08:30:45 +0900 Subject: [PATCH] =?UTF-8?q?feat(lotto):=20useBriefing=C2=B7useCuratorUsage?= =?UTF-8?q?=20=ED=9B=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/lotto/hooks/useBriefing.js | 56 ++++++++++++++++++++++++ src/pages/lotto/hooks/useCuratorUsage.js | 17 +++++++ 2 files changed, 73 insertions(+) create mode 100644 src/pages/lotto/hooks/useBriefing.js create mode 100644 src/pages/lotto/hooks/useCuratorUsage.js diff --git a/src/pages/lotto/hooks/useBriefing.js b/src/pages/lotto/hooks/useBriefing.js new file mode 100644 index 0000000..a405623 --- /dev/null +++ b/src/pages/lotto/hooks/useBriefing.js @@ -0,0 +1,56 @@ +import { useState, useEffect, useCallback, useRef } from 'react'; +import { getLatestBriefing, triggerLottoCurate } from '../../../api'; + +export default function useBriefing() { + const [briefing, setBriefing] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(''); + const [regenerating, setRegenerating] = useState(false); + const pollingRef = useRef(null); + + const load = useCallback(async () => { + setLoading(true); setError(''); + try { + const data = await getLatestBriefing(); + setBriefing(data); + } catch (e) { + setError(e.message); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { load(); }, [load]); + + const regenerate = useCallback(async () => { + setRegenerating(true); setError(''); + try { + const prevGen = briefing?.generated_at; + await triggerLottoCurate(); + let attempts = 0; + pollingRef.current = setInterval(async () => { + attempts += 1; + try { + const data = await getLatestBriefing(); + if (data && data.generated_at !== prevGen) { + setBriefing(data); + setRegenerating(false); + clearInterval(pollingRef.current); + } + } catch {} + if (attempts >= 40) { + clearInterval(pollingRef.current); + setRegenerating(false); + setError('재생성 타임아웃 (2분)'); + } + }, 3000); + } catch (e) { + setError(e.message); + setRegenerating(false); + } + }, [briefing?.generated_at]); + + useEffect(() => () => { if (pollingRef.current) clearInterval(pollingRef.current); }, []); + + return { briefing, loading, error, regenerating, reload: load, regenerate }; +} diff --git a/src/pages/lotto/hooks/useCuratorUsage.js b/src/pages/lotto/hooks/useCuratorUsage.js new file mode 100644 index 0000000..14616e5 --- /dev/null +++ b/src/pages/lotto/hooks/useCuratorUsage.js @@ -0,0 +1,17 @@ +import { useState, useEffect } from 'react'; +import { getCuratorUsage } from '../../../api'; + +export default function useCuratorUsage(days = 30) { + const [usage, setUsage] = useState(null); + const [error, setError] = useState(''); + + useEffect(() => { + let alive = true; + getCuratorUsage(days) + .then(d => { if (alive) setUsage(d); }) + .catch(e => { if (alive) setError(e.message); }); + return () => { alive = false; }; + }, [days]); + + return { usage, error }; +}