135 lines
5.8 KiB
TypeScript
135 lines
5.8 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useEffect } from "react";
|
||
import Link from "next/link";
|
||
import { COMMUNITY_CONFIG } from "@/config/course";
|
||
import { isPaid } from "@/app/lib/payment";
|
||
|
||
export function CommunityButton() {
|
||
const [open, setOpen] = useState(false);
|
||
const [paid, setPaid] = useState(false);
|
||
|
||
useEffect(() => {
|
||
setPaid(isPaid());
|
||
const onPay = () => setPaid(isPaid());
|
||
window.addEventListener("pay:updated", onPay);
|
||
return () => window.removeEventListener("pay:updated", onPay);
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
const onKeyDown = (e: KeyboardEvent) => {
|
||
if (e.key === "Escape") setOpen(false);
|
||
};
|
||
if (open) {
|
||
window.addEventListener("keydown", onKeyDown);
|
||
document.body.style.overflow = "hidden";
|
||
}
|
||
return () => {
|
||
window.removeEventListener("keydown", onKeyDown);
|
||
document.body.style.overflow = "";
|
||
};
|
||
}, [open]);
|
||
|
||
if (!COMMUNITY_CONFIG.enabled || !COMMUNITY_CONFIG.wechat) return null;
|
||
|
||
return (
|
||
<>
|
||
<button
|
||
type="button"
|
||
onClick={() => setOpen(true)}
|
||
className="text-sm text-[var(--muted-foreground)] transition hover:text-[var(--foreground)]"
|
||
>
|
||
{COMMUNITY_CONFIG.wechat}
|
||
</button>
|
||
|
||
{open && (
|
||
<div
|
||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm p-4"
|
||
onClick={() => setOpen(false)}
|
||
>
|
||
<div
|
||
className="relative w-full max-w-[420px] overflow-hidden rounded-2xl border border-[var(--border)] bg-[var(--background)] shadow-2xl"
|
||
onClick={(e) => e.stopPropagation()}
|
||
>
|
||
<div className="absolute inset-x-0 top-0 h-1 bg-gradient-to-r from-[var(--accent)] via-cyan-400 to-[var(--accent)]" />
|
||
<button
|
||
type="button"
|
||
onClick={() => setOpen(false)}
|
||
className="absolute right-3 top-3 rounded-full p-2 text-[var(--muted-foreground)] transition hover:bg-[var(--muted)] hover:text-[var(--foreground)]"
|
||
aria-label="关闭"
|
||
>
|
||
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||
</svg>
|
||
</button>
|
||
|
||
{paid ? (
|
||
<div className="p-8 pt-10">
|
||
<div className="mb-6 text-center">
|
||
<div className="mb-3 inline-flex h-14 w-14 items-center justify-center rounded-2xl bg-gradient-to-br from-[var(--accent)]/30 to-cyan-500/20 text-3xl shadow-lg">
|
||
💬
|
||
</div>
|
||
<h3 className="text-xl font-semibold">加入数字游民社群</h3>
|
||
<p className="mt-2 text-sm text-[var(--muted-foreground)]">
|
||
与导师、同学一起交流,获取持续支持与灵感
|
||
</p>
|
||
</div>
|
||
<div className="flex justify-center">
|
||
<div className="relative flex h-56 w-56 items-center justify-center rounded-2xl bg-gradient-to-br from-[var(--card)] to-[var(--muted)]/30 p-5 shadow-inner ring-2 ring-[var(--accent)]/20">
|
||
<img
|
||
src={COMMUNITY_CONFIG.qrCodeUrl}
|
||
alt="社群二维码"
|
||
className="h-full w-full object-contain"
|
||
onError={(e) => {
|
||
const target = e.target as HTMLImageElement;
|
||
target.style.display = "none";
|
||
const fallback = target.parentElement?.querySelector(".qr-fallback");
|
||
if (fallback) (fallback as HTMLElement).classList.remove("hidden");
|
||
}}
|
||
/>
|
||
<div className="qr-fallback hidden absolute inset-0 flex flex-col items-center justify-center gap-2 rounded-2xl bg-[var(--muted)]/40 p-4 text-center text-sm text-[var(--muted-foreground)]">
|
||
<span className="text-4xl">📱</span>
|
||
<span>请配置社群二维码</span>
|
||
<span className="text-xs">config/course.ts → qrCodeUrl</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p className="mt-5 text-center text-sm text-[var(--muted-foreground)]">
|
||
长按识别二维码加入
|
||
</p>
|
||
</div>
|
||
) : (
|
||
<div className="p-8 pt-10">
|
||
<div className="mb-6 text-center">
|
||
<div className="mb-3 inline-flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-amber-500/20 to-orange-500/10 text-4xl shadow-lg">
|
||
🔒
|
||
</div>
|
||
<h3 className="text-xl font-semibold">报名后解锁社群</h3>
|
||
<p className="mt-2 text-sm leading-relaxed text-[var(--muted-foreground)]">
|
||
完成课程报名后,即可加入专属社群,与导师、同学交流答疑
|
||
</p>
|
||
</div>
|
||
<div className="flex flex-col gap-3">
|
||
<Link
|
||
href="/pay"
|
||
className="rounded-xl bg-[var(--accent)] px-6 py-4 text-center font-medium text-[var(--accent-foreground)] transition hover:opacity-90"
|
||
onClick={() => setOpen(false)}
|
||
>
|
||
立即报名
|
||
</Link>
|
||
<button
|
||
type="button"
|
||
onClick={() => setOpen(false)}
|
||
className="rounded-xl border border-[var(--border)] px-6 py-3 text-sm transition hover:bg-[var(--muted)]"
|
||
>
|
||
稍后再说
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</>
|
||
);
|
||
} |