Skip to content

Commit 8011197

Browse files
authored
Merge pull request #227 from r4topunk/feat/stake-morpheus-widget
feat(stake): floating Morpheus widget — subnet pitch + staking milestones
2 parents 7a5418d + f2fd1a8 commit 8011197

2 files changed

Lines changed: 137 additions & 1 deletion

File tree

src/app/[locale]/stake/page.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { CharacterSelector } from "@/components/stake/CharacterSelector";
44
import { StakeAdminPanel } from "@/components/stake/StakeAdminPanel";
55
import { StakeOrbit } from "@/components/stake/StakeOrbit";
66
import { MorLootbox } from "@/components/stake/MorLootbox";
7+
import { MorpheusStakeWidget } from "@/components/stake/MorpheusStakeWidget";
78
import { STAKE_MINIAPP_EMBED_CONFIG } from "@/lib/miniapp-config";
89

910
export async function generateMetadata({
@@ -62,11 +63,14 @@ export default async function StakePage({ params }: { params: Promise<{ locale:
6263
<p className="mx-auto max-w-xl text-muted-foreground">{t("intro")}</p>
6364
</div>
6465

65-
<CharacterSelector />
66+
<div id="stake-start" className="scroll-mt-24">
67+
<CharacterSelector />
68+
</div>
6669
<StakeOrbit />
6770
<StakeAdminPanel />
6871
</div>
6972
<MorLootbox />
73+
<MorpheusStakeWidget />
7074
</div>
7175
);
7276
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
"use client";
2+
3+
// Floating Morpheus button on the stake page (prospecting side of the loop —
4+
// MorLootbox handles collecting; this invites a NEW backer to stake).
5+
// button → "Gnars subnet" panel (pitch + Stake) → staking milestones → stake.
6+
// Sits above the MiniTV (bottom-left); MorLootbox owns bottom-right.
7+
//
8+
// NOTE: milestone copy + targets are placeholders — swap for the real program.
9+
// Strings are hardcoded EN for now (not yet wired to next-intl stake namespace).
10+
11+
import Image from "next/image";
12+
import { useState } from "react";
13+
import { AnimatePresence, motion } from "framer-motion";
14+
import { ArrowRight, Check, ChevronLeft, Lock, X } from "lucide-react";
15+
import { Button } from "@/components/ui/button";
16+
17+
type View = "closed" | "intro" | "milestones";
18+
19+
const MILESTONES: { label: string; done: boolean }[] = [
20+
{ label: "Subnet live — Gnars is a Morpheus Builder", done: true },
21+
{ label: "First 10 backers on board", done: false },
22+
{ label: "$10k staked — featured stakers wall", done: false },
23+
{ label: "$50k staked — community drop", done: false },
24+
{ label: "$100k staked — IRL Gnars event", done: false },
25+
];
26+
27+
export function MorpheusStakeWidget() {
28+
const [view, setView] = useState<View>("closed");
29+
30+
const goStake = () => {
31+
document.getElementById("stake-start")?.scrollIntoView({ behavior: "smooth", block: "start" });
32+
setView("closed");
33+
};
34+
35+
return (
36+
<div className="fixed left-4 bottom-[150px] z-40 flex flex-col items-start gap-3">
37+
<AnimatePresence>
38+
{view !== "closed" && (
39+
<motion.div
40+
initial={{ opacity: 0, y: 12, scale: 0.96 }}
41+
animate={{ opacity: 1, y: 0, scale: 1 }}
42+
exit={{ opacity: 0, y: 12, scale: 0.96 }}
43+
transition={{ duration: 0.18 }}
44+
className="w-[min(360px,calc(100vw-2rem))] max-h-[65vh] overflow-y-auto rounded-2xl border border-violet-500/30 bg-background/95 p-5 shadow-2xl backdrop-blur"
45+
>
46+
<div className="mb-3 flex items-center justify-between">
47+
<div className="flex items-center gap-2">
48+
{view === "milestones" ? (
49+
<button
50+
type="button"
51+
onClick={() => setView("intro")}
52+
className="text-muted-foreground transition-colors hover:text-foreground"
53+
aria-label="Back"
54+
>
55+
<ChevronLeft className="h-4 w-4" />
56+
</button>
57+
) : (
58+
<Image src="/logos/morpheus.webp" alt="Morpheus" width={22} height={22} className="rounded" />
59+
)}
60+
<span className="text-xs font-bold uppercase tracking-wider text-violet-400">Gnars subnet</span>
61+
</div>
62+
<button
63+
type="button"
64+
onClick={() => setView("closed")}
65+
className="text-muted-foreground transition-colors hover:text-foreground"
66+
aria-label="Close"
67+
>
68+
<X className="h-4 w-4" />
69+
</button>
70+
</div>
71+
72+
{view === "intro" ? (
73+
<div className="space-y-4">
74+
<div>
75+
<h3 className="text-lg font-black tracking-tight">Stake on the Gnars subnet</h3>
76+
<p className="mt-1 text-sm text-muted-foreground">
77+
Back Gnars on Morpheus and put decentralized AI behind a real action-sports culture.
78+
Self-custody on Ethereum mainnet — your keys the whole way.
79+
</p>
80+
</div>
81+
<Button
82+
onClick={() => setView("milestones")}
83+
className="w-full bg-violet-500 text-white hover:bg-violet-600"
84+
>
85+
Stake <ArrowRight className="ml-1 h-4 w-4" />
86+
</Button>
87+
</div>
88+
) : (
89+
<div className="space-y-4">
90+
<div>
91+
<h3 className="text-base font-black tracking-tight">Staking milestones</h3>
92+
<p className="mt-1 text-xs text-muted-foreground">What the subnet unlocks as backing grows.</p>
93+
</div>
94+
<ol className="space-y-2.5">
95+
{MILESTONES.map((m, i) => (
96+
<li key={i} className="flex items-start gap-2.5">
97+
<span
98+
className={`mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full ${
99+
m.done ? "bg-violet-500 text-white" : "border border-border text-muted-foreground"
100+
}`}
101+
>
102+
{m.done ? <Check className="h-3 w-3" /> : <Lock className="h-3 w-3" />}
103+
</span>
104+
<span className={`text-sm ${m.done ? "font-semibold text-foreground" : "text-muted-foreground"}`}>
105+
{m.label}
106+
</span>
107+
</li>
108+
))}
109+
</ol>
110+
<Button onClick={goStake} className="w-full bg-violet-500 text-white hover:bg-violet-600">
111+
Stake now <ArrowRight className="ml-1 h-4 w-4" />
112+
</Button>
113+
</div>
114+
)}
115+
</motion.div>
116+
)}
117+
</AnimatePresence>
118+
119+
<button
120+
type="button"
121+
onClick={() => setView(view === "closed" ? "intro" : "closed")}
122+
aria-label="Stake on the Gnars subnet"
123+
className="group relative flex h-14 w-14 items-center justify-center rounded-full border border-violet-400/40 bg-background/90 shadow-lg backdrop-blur transition hover:scale-105"
124+
>
125+
{view === "closed" && (
126+
<span className="absolute inset-0 animate-ping rounded-full bg-violet-500/20" aria-hidden />
127+
)}
128+
<Image src="/logos/morpheus.webp" alt="Morpheus" width={34} height={34} className="relative rounded-full" />
129+
</button>
130+
</div>
131+
);
132+
}

0 commit comments

Comments
 (0)