feat(lolstats): add data renewal — auto-refresh stale op.gg data
Uses op.gg REST API to trigger summoner data renewal before fetching stats via MCP. Adds Update button in profile header for manual refresh. Flow: lookup summoner_id → POST renewal → poll until finish → re-fetch. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
40c596fbfa
commit
f4c8cce2f9
4 changed files with 218 additions and 3 deletions
|
|
@ -111,8 +111,11 @@ export default function LolstatsTab({ data }: { data: any }) {
|
|||
const [expandedMatch, setExpandedMatch] = useState<string | null>(null);
|
||||
const [matchDetails, setMatchDetails] = useState<Record<string, MatchEntry>>({});
|
||||
const [loadingMore, setLoadingMore] = useState(false);
|
||||
const [renewing, setRenewing] = useState(false);
|
||||
const [lastUpdated, setLastUpdated] = useState<string | null>(null);
|
||||
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
const currentSearchRef = useRef<{ gameName: string; tagLine: string; region: string } | null>(null);
|
||||
|
||||
// Load regions
|
||||
useEffect(() => {
|
||||
|
|
@ -127,8 +130,24 @@ export default function LolstatsTab({ data }: { data: any }) {
|
|||
if (data.regions && !regions.length) setRegions(data.regions);
|
||||
}, [data]);
|
||||
|
||||
// ── Renewal ──
|
||||
const doRenew = useCallback(async (gameName: string, tagLine: string, searchRegion: string) => {
|
||||
setRenewing(true);
|
||||
try {
|
||||
const qs = `gameName=${encodeURIComponent(gameName)}&tagLine=${encodeURIComponent(tagLine)}®ion=${searchRegion}`;
|
||||
const res = await fetch(`/api/lolstats/renew?${qs}`, { method: 'POST' });
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
if (data.last_updated_at) setLastUpdated(data.last_updated_at);
|
||||
return data.renewed ?? false;
|
||||
}
|
||||
} catch {}
|
||||
setRenewing(false);
|
||||
return false;
|
||||
}, []);
|
||||
|
||||
// ── Search ──
|
||||
const doSearch = useCallback(async (gn?: string, tl?: string, rg?: string) => {
|
||||
const doSearch = useCallback(async (gn?: string, tl?: string, rg?: string, skipRenew = false) => {
|
||||
let gameName = gn ?? '';
|
||||
let tagLine = tl ?? '';
|
||||
const searchRegion = rg ?? region;
|
||||
|
|
@ -152,6 +171,14 @@ export default function LolstatsTab({ data }: { data: any }) {
|
|||
setExpandedMatch(null);
|
||||
setMatchDetails({});
|
||||
|
||||
// Track current search for update button
|
||||
currentSearchRef.current = { gameName, tagLine, region: searchRegion };
|
||||
|
||||
// Auto-renew on first search (trigger op.gg refresh in background)
|
||||
if (!skipRenew) {
|
||||
doRenew(gameName, tagLine, searchRegion).finally(() => setRenewing(false));
|
||||
}
|
||||
|
||||
try {
|
||||
const qs = `gameName=${encodeURIComponent(gameName)}&tagLine=${encodeURIComponent(tagLine)}®ion=${searchRegion}`;
|
||||
|
||||
|
|
@ -168,6 +195,7 @@ export default function LolstatsTab({ data }: { data: any }) {
|
|||
|
||||
const profileData = await profileRes.json();
|
||||
setProfile(profileData);
|
||||
if (profileData.updated_at) setLastUpdated(profileData.updated_at);
|
||||
|
||||
if (matchesRes.ok) {
|
||||
const matchesData = await matchesRes.json();
|
||||
|
|
@ -177,7 +205,23 @@ export default function LolstatsTab({ data }: { data: any }) {
|
|||
setError(e.message);
|
||||
}
|
||||
setLoading(false);
|
||||
}, [searchText, region]);
|
||||
}, [searchText, region, doRenew]);
|
||||
|
||||
// ── Manual Update (renew + re-fetch) ──
|
||||
const doUpdate = useCallback(async () => {
|
||||
const cur = currentSearchRef.current;
|
||||
if (!cur || renewing) return;
|
||||
setRenewing(true);
|
||||
try {
|
||||
await doRenew(cur.gameName, cur.tagLine, cur.region);
|
||||
// Wait a moment for op.gg MCP to pick up the renewed data
|
||||
await new Promise(r => setTimeout(r, 1500));
|
||||
// Re-fetch
|
||||
await doSearch(cur.gameName, cur.tagLine, cur.region, true);
|
||||
} finally {
|
||||
setRenewing(false);
|
||||
}
|
||||
}, [doRenew, doSearch, renewing]);
|
||||
|
||||
// ── Load more matches ──
|
||||
const loadMore = useCallback(async () => {
|
||||
|
|
@ -384,7 +428,19 @@ export default function LolstatsTab({ data }: { data: any }) {
|
|||
Ladder Rank #{profile.ladder_rank.rank.toLocaleString()} / {profile.ladder_rank.total?.toLocaleString()}
|
||||
</div>
|
||||
)}
|
||||
{lastUpdated && (
|
||||
<div className="lol-profile-updated">Updated {timeAgo(lastUpdated)} ago</div>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className={`lol-update-btn ${renewing ? 'renewing' : ''}`}
|
||||
onClick={doUpdate}
|
||||
disabled={renewing}
|
||||
title="Refresh data from Riot servers"
|
||||
>
|
||||
<span className="lol-update-icon">{renewing ? '⟳' : '↻'}</span>
|
||||
{renewing ? 'Updating...' : 'Update'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Ranked Cards */}
|
||||
|
|
|
|||
|
|
@ -122,6 +122,43 @@
|
|||
font-size: 11px;
|
||||
color: var(--text-faint);
|
||||
}
|
||||
.lol-profile-updated {
|
||||
font-size: 10px;
|
||||
color: var(--text-faint);
|
||||
margin-top: 2px;
|
||||
}
|
||||
.lol-profile-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* ── Update Button ── */
|
||||
.lol-update-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid var(--bg-tertiary);
|
||||
border-radius: 8px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-muted);
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s, background 0.2s;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.lol-update-btn:hover { border-color: var(--accent); color: var(--text-normal); }
|
||||
.lol-update-btn:disabled { opacity: 0.6; cursor: not-allowed; }
|
||||
.lol-update-btn.renewing { border-color: var(--accent); color: var(--accent); }
|
||||
.lol-update-icon {
|
||||
font-size: 16px;
|
||||
display: inline-block;
|
||||
}
|
||||
.lol-update-btn.renewing .lol-update-icon {
|
||||
animation: lol-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
/* ── Ranked Cards ── */
|
||||
.lol-ranked-row {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue