Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "plotlink",
"version": "1.40.5",
"version": "1.41.0",
"private": true,
"workspaces": [
"packages/*"
Expand Down
87 changes: 54 additions & 33 deletions src/app/airdrop/AirdropStateMachine.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import { useEffect, useState, useCallback } from "react";
import { useEffect, useState, useCallback, useRef } from "react";
import { useAccount } from "wagmi";
import Link from "next/link";
import { useConnectModal } from "@rainbow-me/rainbowkit";
import { CampaignHero } from "../../components/airdrop/CampaignHero";
import { ActivationFlow } from "../../components/airdrop/ActivationFlow";
import { ContributionPanel } from "../../components/airdrop/ContributionPanel";
Expand All @@ -23,10 +23,7 @@ const MERKLE_CLAIM_ADDRESS = process.env.NEXT_PUBLIC_MERKLE_CLAIM_ADDRESS;
const FINAL_BURN_TX = process.env.NEXT_PUBLIC_AIRDROP_FINAL_BURN_TX;
const FINAL_STATE = process.env.NEXT_PUBLIC_AIRDROP_FINAL_STATE as "sub_bronze" | "zero_recipient" | undefined;

function deriveState(
isConnected: boolean,
activatedAt: string | null,
): AirdropState {
function deriveState(isConnected: boolean, activatedAt: string | null): AirdropState {
if (IS_PAUSED) return "paused";
if (MERKLE_CLAIM_ADDRESS) return "settlement-normal";
if (FINAL_BURN_TX) return "settlement-final-burn";
Expand All @@ -36,14 +33,50 @@ function deriveState(

const needsFetch = !IS_PAUSED && !MERKLE_CLAIM_ADDRESS && !FINAL_BURN_TX;

function HeroBlock({ isConnected, onActivate }: { isConnected: boolean; onActivate: () => void }) {
const { openConnectModal } = useConnectModal();

return (
<div className="py-10 text-center space-y-5">
<h1 className="font-[var(--font-display)] text-3xl leading-tight sm:text-4xl">
The airdrop pool<br />
<span className="bg-gradient-to-r from-[var(--gold-1)] to-[var(--gold-2)] bg-clip-text text-transparent">grows with us.</span><br />
<span className="text-[var(--burn)] italic">Or it burns.</span>
</h1>
<p className="text-[var(--muted)] text-sm">Buy storyline tokens &middot; Bring friends &middot; Push PLOT together</p>
{!isConnected ? (
<button
onClick={() => openConnectModal?.()}
className="bg-[var(--accent)] text-[var(--bg)] rounded px-6 py-2.5 text-sm font-medium transition-opacity hover:opacity-80"
>
Activate to join the Sprint
</button>
) : (
<button
onClick={onActivate}
className="bg-[var(--accent)] text-[var(--bg)] rounded px-6 py-2.5 text-sm font-medium transition-opacity hover:opacity-80"
>
Activate to join the Sprint
</button>
)}
<p className="text-[var(--muted)] text-xs">One signature + a few clicks. ~2 min.</p>
</div>
);
}

export function AirdropStateMachine() {
const { address, isConnected } = useAccount();
const [fetchResult, setFetchResult] = useState<{ activatedAt: string | null; done: boolean }>({ activatedAt: null, done: !needsFetch });
const activationRef = useRef<HTMLDivElement>(null);

const onActivated = useCallback(() => {
setFetchResult({ activatedAt: new Date().toISOString(), done: true });
}, []);

const scrollToActivation = useCallback(() => {
activationRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
}, []);

useEffect(() => {
if (!needsFetch || !isConnected || !address) return;

Expand All @@ -57,14 +90,13 @@ export function AirdropStateMachine() {

const activatedAt = (needsFetch && isConnected) ? fetchResult.activatedAt : null;
const loading = needsFetch && isConnected && !fetchResult.done;

const state = deriveState(isConnected, activatedAt);

if (state === "paused") {
return (
<div className="border-border mt-8 rounded border p-8 text-center">
<h2 className="text-accent mb-2 text-sm font-bold uppercase tracking-wider">Campaign Paused</h2>
<p className="text-muted text-sm">Campaign temporarily paused. Will resume shortly.</p>
<div className="border-[var(--border)] mt-8 rounded border p-8 text-center">
<h2 className="text-[var(--accent)] mb-2 text-sm font-bold uppercase tracking-wider">Campaign Paused</h2>
<p className="text-[var(--muted)] text-sm">Campaign temporarily paused. Will resume shortly.</p>
</div>
);
}
Expand All @@ -73,9 +105,7 @@ export function AirdropStateMachine() {
return (
<>
<CampaignHero />
<div className="mt-8">
<ClaimPanel />
</div>
<div className="mt-8"><ClaimPanel /></div>
</>
);
}
Expand All @@ -84,9 +114,7 @@ export function AirdropStateMachine() {
return (
<>
<CampaignHero />
<div className="mt-8">
<ClaimCard mode="final-burn" finalState={FINAL_STATE ?? null} />
</div>
<div className="mt-8"><ClaimCard mode="final-burn" finalState={FINAL_STATE ?? null} /></div>
</>
);
}
Expand All @@ -95,9 +123,7 @@ export function AirdropStateMachine() {
return (
<>
<CampaignHero />
<div className="mt-8 text-center">
<p className="text-muted text-sm">Loading...</p>
</div>
<div className="mt-8 text-center"><p className="text-[var(--muted)] text-sm">Loading...</p></div>
</>
);
}
Expand All @@ -106,32 +132,27 @@ export function AirdropStateMachine() {
return (
<>
<CampaignHero />
<div className="mt-8 space-y-4">
{!isConnected ? (
<div className="border-border rounded border p-8 text-center space-y-3">
<p className="text-muted text-sm">Connect your wallet to get started.</p>
<Link href="/" className="text-accent text-xs hover:underline">
Browse storylines &rarr;
</Link>
</div>
) : (
<HeroBlock isConnected={isConnected} onActivate={scrollToActivation} />
{isConnected && (
<div ref={activationRef}>
<ActivationFlow onActivated={onActivated} />
)}
</div>
)}
<div className="mt-6">
<MilestoneClimb dimmed />
</div>
</>
);
}

// mining
return (
<>
<CampaignHero />
<div className="mt-8 space-y-4">
<ContributionPanel />
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<ReferralCTA />
<MilestoneClimb />
</div>
<MilestoneClimb />
<ReferralCTA />
</div>
</>
);
Expand Down
2 changes: 2 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
--success: oklch(45% 0.14 145);
--burn: oklch(50% 0.20 25);
--dist: oklch(45% 0.16 145);
--gold-1: #FFE07A;
--gold-2: #D9A847;

--font-display: 'Newsreader', 'Iowan Old Style', Georgia, serif;
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
Expand Down
Loading
Loading