Files

130 lines
4.0 KiB
TypeScript

'use client'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { api } from '@hanzo/app/lib/api'
import { scope, superAdmin } from '@hanzo/app/lib/session'
import { mine, updateCode, type Referee } from '@/lib/hanzo/referrals'
import { ask, getLocalRequestedBetas, standing } from '@/lib/hanzo/beta'
import { board, github, listing, me as read, setListing, type Board, type Connection, type Listing, type Me } from '@/lib/hanzo/rewards'
/**
* Everything the portal's four steps read about the member, once: their link
* and who came through it, their points and place, the betas asked for and the
* flags the cloud holds for them, and the board. `signed`
* is a full session: nothing is read before it.
*
* Points, place and the board are the rewards ledger's (GET /v1/rewards/me and
* GET /v1/rewards): the same figures /leaderboard draws. Nothing here adds a
* point of its own.
*/
export function useStanding(signed: boolean) {
const [code, setCode] = useState('')
const [link, setLink] = useState('')
const [referred, setReferred] = useState(0)
const [referees, setReferees] = useState<Referee[]>([])
const [me, setMe] = useState<Me | null>(null)
const [gh, setGh] = useState<Connection | null>(null)
const [asked, setAsked] = useState<string[]>(getLocalRequestedBetas)
const [flags, setFlags] = useState<Record<string, boolean>>({})
const staff = useMemo(() => (signed ? superAdmin() : false), [signed])
const [ranked, setRanked] = useState<Board | null>(null)
const [listed, setListed] = useState<Listing | null>(null)
useEffect(() => {
if (!signed) return
let live = true
mine()
.then((r) => {
if (!live) return
if (r.code) {
setCode(r.code)
setLink(`https://hanzo.ai/beta?ref=${r.code}`)
} else if (r.link) setLink(r.link)
setReferred(r.counts?.total ?? 0)
if (Array.isArray(r.referrals)) setReferees(r.referrals)
})
.catch(() => {})
read().then((m) => live && setMe(m))
board().then((b) => live && setRanked(b))
listing().then((l) => live && setListed(l))
github().then((c) => live && setGh(c))
return () => {
live = false
}
}, [signed])
// The record and the flags, and any request made signed out, sent now.
useEffect(() => {
if (!signed) return
let live = true
Promise.all([
standing(),
fetch(`${api()}/v1/flag/platform`, { headers: scope() })
.then((r) => (r.ok ? (r.json() as Promise<{ flags?: Record<string, boolean> }>) : null))
.catch(() => null),
]).then(async ([st, f]) => {
if (!live) return
if (f?.flags) setFlags(f.flags)
const kept = getLocalRequestedBetas()
if (!st) {
if (kept.length) await ask(kept)
return
}
setAsked(Array.from(new Set([...st.betas, ...kept])))
const unsent = kept.filter((id) => !st.betas.includes(id))
if (unsent.length) {
const sent = await ask(unsent)
if (live && sent) setAsked(sent.betas)
}
})
return () => {
live = false
}
}, [signed])
/** Ask for betas: the rows say so at once, and the cloud's answer settles them. */
const request = useCallback(async (ids: string[]) => {
setAsked((now) => Array.from(new Set([...now, ...ids])))
const sent = await ask(ids)
if (sent) setAsked(sent.betas)
}, [])
const rename = useCallback(async (next: string) => {
const r = await updateCode(next)
const saved = r.code || next.toUpperCase()
setCode(saved)
setLink(`https://hanzo.ai/beta?ref=${saved}`)
return saved
}, [])
const show = useCallback(async (on: boolean) => {
const l = await setListing(on)
if (!l) return
setListed(l)
board().then(setRanked)
}, [])
return {
code,
link,
referred,
referees,
rename,
me,
place: me?.rank ?? 0,
of: me?.of ?? 0,
total: me?.points ?? 0,
github: gh,
asked,
request,
flags,
staff,
ranked,
listed,
show,
}
}