mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
## Summary - add database-backed v2 invite links with optional maximum-use limits and atomic final-slot redemption - preserve v1 invite compatibility while adding exhausted/expired/invalid client handling across desktop, web, and mobile - emit structured claim-outcome logs with community, invite ID, outcome, maximum uses, and post-claim count ## Verification - `cargo fmt --all -- --check` - `cargo test -p buzz-db` (85 passed, 134 Postgres-dependent ignored) - `cargo clippy -p buzz-db --all-targets -- -D warnings` - desktop `npm run typecheck` - push hook: desktop checks/tests, desktop Tauri tests, Rust tests, and branch-skew passed - Postgres integration tests were previously reviewed green at the pre-rebase tree; local rerun on this session was unavailable because Postgres/Docker were not running - mobile push-hook check could not start because Flutter is unavailable locally --------- Signed-off-by: Kalvin Chau <kalvin@block.xyz> Signed-off-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz> Co-authored-by: npub1c4alndp82zyt9veaklm5d965quss79vlhk9awv7qu5erwhmf42qqlvc25c <c57bf9b4275088b2b33db7f746975407210f159fbd8bd733c0e532375f69aa80@buzz.block.builderlab.xyz> Co-authored-by: npub122y0pqkertljmedu303rl0aqrj3w8pvu43t6jxm6875lzg6f2pwqegc3xc <5288f082d91aff2de5bc8be23fbfa01ca2e3859cac57a91b7a3fa9f12349505c@buzz.block.builderlab.xyz> Co-authored-by: npub1dccv64krpcpse5cmkzfeh998cftungyatw3djt8jwdw6g43f7fyqzzmrf7 <6e30cd56c30e030cd31bb0939b94a7c257c9a09d5ba2d92cf2735da45629f248@buzz.block.builderlab.xyz>
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import * as React from "react";
|
|
|
|
import { useCommunityOnboarding } from "@/features/onboarding/communityOnboarding";
|
|
import {
|
|
inviteErrorMessage,
|
|
isInviteExhaustedError,
|
|
isInviteExpiredError,
|
|
} from "@/shared/api/inviteHelpers";
|
|
import { claimInvite } from "@/shared/api/invites";
|
|
|
|
/**
|
|
* Drive the `claiming` stage after machine onboarding completes: claim the
|
|
* invite with the user's final identity, then advance to `connecting`.
|
|
* Completion is fenced by transaction ID so cancelling or replacing the
|
|
* transaction while the request is pending cannot mutate the replacement.
|
|
*
|
|
* The error guard keeps a failed claim parked on the caller's Retry
|
|
* affordance — without it the effect refires on the error-bearing transaction
|
|
* and re-claims in a loop.
|
|
*/
|
|
export function useClaimInvite() {
|
|
const { transaction, update } = useCommunityOnboarding();
|
|
const [isPending, setIsPending] = React.useState(false);
|
|
|
|
React.useEffect(() => {
|
|
if (transaction?.stage !== "claiming" || transaction.error || isPending) {
|
|
return;
|
|
}
|
|
setIsPending(true);
|
|
void claimInvite(
|
|
transaction.relayUrl,
|
|
transaction.inviteCode ?? "",
|
|
transaction.policyReceipt,
|
|
)
|
|
.then(() => {
|
|
update({ stage: "connecting", error: undefined }, transaction.id);
|
|
})
|
|
.catch((error: unknown) =>
|
|
update(
|
|
{
|
|
error: isInviteExpiredError(error)
|
|
? "This invite code has expired — ask for a new one."
|
|
: isInviteExhaustedError(error)
|
|
? "This invite has reached its use limit. Ask for a new invite."
|
|
: inviteErrorMessage(error),
|
|
},
|
|
transaction.id,
|
|
),
|
|
)
|
|
.finally(() => setIsPending(false));
|
|
}, [isPending, transaction, update]);
|
|
}
|