mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-26 23:48:52 -07:00
[www] discord per-role token drop
This commit is contained in:
+104
-1
@@ -3306,7 +3306,19 @@ function Dashboard({
|
||||
: []),
|
||||
// Narrower than the two above: the drop mints tokens, and www's route is developer-only.
|
||||
...(isDeveloper()
|
||||
? [{ id: 'tokens', label: 'Token drop', render: () => <TokenDropForm /> }]
|
||||
? [
|
||||
{
|
||||
id: 'tokens',
|
||||
label: 'Token drop',
|
||||
// Two audiences for one gift: whoever is online now, or whoever holds a Discord role.
|
||||
render: () => (
|
||||
<>
|
||||
<TokenDropForm />
|
||||
<RoleTokenDropForm />
|
||||
</>
|
||||
),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
const [active, setActive] = useState(sections[0].id)
|
||||
@@ -3682,6 +3694,97 @@ function TokenDropForm() {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Developer-only: give every player whose Discord link holds a role the same number of
|
||||
* tokens, in a gift box carrying the operator's message — the token drop's sibling, with a
|
||||
* different audience. Offline is the point: the box waits for whoever isn't signed in.
|
||||
*
|
||||
* The audience is the roles the website recorded at each player's benefits claim and
|
||||
* refreshes once a day, so it trails Discord by up to a day and never includes a member who
|
||||
* hasn't claimed; the copy says so. Same cap as the online drop, repeated on the input.
|
||||
*/
|
||||
function RoleTokenDropForm() {
|
||||
const [roleId, setRoleId] = useState('')
|
||||
const [message, setMessage] = useState('')
|
||||
const [tokens, setTokens] = useState('')
|
||||
const { pending, error, done, run } = useAction()
|
||||
|
||||
return (
|
||||
<section className="card">
|
||||
<h2>Discord role drop</h2>
|
||||
<p className="muted">
|
||||
Give everyone holding a Discord role the same number of tokens, whether or not they are
|
||||
online — a player who is signed out finds the gift box waiting. Only players who have
|
||||
claimed benefits on this site are counted, and their roles are refreshed once a day, so a
|
||||
role given or taken since then isn't seen yet. Up to {MAX_TOKEN_DROP.toLocaleString()}{' '}
|
||||
tokens each.
|
||||
</p>
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault()
|
||||
void run(async () => {
|
||||
const amount = Number(tokens)
|
||||
const role = roleId.trim()
|
||||
const res = await call<{ paid: number[] }>(
|
||||
`/api/staff/discord-roles/${encodeURIComponent(role)}/gift-tokens`,
|
||||
{ authed: true, json: { amount, message: message.trim() } }
|
||||
)
|
||||
setTokens('')
|
||||
setMessage('')
|
||||
const n = res.paid.length
|
||||
return `Sent ${amount.toLocaleString()} tokens to ${n} player${n === 1 ? '' : 's'} holding role ${role}.`
|
||||
})
|
||||
}}
|
||||
>
|
||||
<label>
|
||||
Discord role id
|
||||
<input
|
||||
type="text"
|
||||
inputMode="numeric"
|
||||
pattern="[0-9]+"
|
||||
value={roleId}
|
||||
placeholder="1077000000000000001"
|
||||
required
|
||||
onChange={(e) => setRoleId(e.target.value)}
|
||||
/>
|
||||
<span className="hint">
|
||||
The role's numeric id, from Discord's role settings (Copy Role ID), not its
|
||||
name.
|
||||
</span>
|
||||
</label>
|
||||
<label>
|
||||
Gift box message
|
||||
<textarea
|
||||
value={message}
|
||||
rows={3}
|
||||
maxLength={256}
|
||||
placeholder="Thanks for supporting the server!"
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Tokens per player
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
max={MAX_TOKEN_DROP}
|
||||
step={1}
|
||||
value={tokens}
|
||||
required
|
||||
onChange={(e) => setTokens(e.target.value)}
|
||||
/>
|
||||
</label>
|
||||
{error && <p className="error">{error}</p>}
|
||||
{done && <p className="ok">{done}</p>}
|
||||
<button type="submit" disabled={pending}>
|
||||
{pending ? 'Sending…' : 'Send to everyone with the role'}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the account's username — the name used to sign in, here and in the game.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user