-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathJoinButton.tsx
More file actions
129 lines (116 loc) · 3.73 KB
/
JoinButton.tsx
File metadata and controls
129 lines (116 loc) · 3.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
"use client";
import { useState } from "react";
import { createClient } from "../../../lib/supabase/client";
import { useRouter } from "next/navigation";
import { toast } from "react-toastify";
import Link from "next/link";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faArrowRightToBracket,
faCheck,
faSpinner,
faUserPlus,
} from "@fortawesome/free-solid-svg-icons";
export default function JoinButton({
code,
leaderboardSlug,
isLoggedIn,
alreadyMember,
}: {
code: string;
leaderboardSlug: string;
isLoggedIn: boolean;
alreadyMember: boolean;
}) {
const router = useRouter();
const [joining, setJoining] = useState(false);
if (alreadyMember) {
return (
<Link
href={`/leaderboard/${leaderboardSlug}`}
className="btn-primary inline-flex items-center justify-center gap-2 w-full py-4 text-sm font-bold rounded-xl shadow-lg shadow-indigo-500/20"
>
<FontAwesomeIcon icon={faCheck} className="w-5 h-5" />
<span className="sm:hidden">View</span>
<span className="hidden sm:inline">View Leaderboard</span>
</Link>
);
}
if (!isLoggedIn) {
return (
<div className="space-y-3">
<Link
href={`/login?redirect=${encodeURIComponent(`/join?id=${code}`)}`}
className="btn-primary inline-flex items-center justify-center gap-2 w-full py-4 text-sm font-bold rounded-xl shadow-lg shadow-indigo-500/20"
>
<FontAwesomeIcon icon={faArrowRightToBracket} className="w-5 h-5" />
Log In to Join
</Link>
<p className="text-xs text-gray-500">
Don't have an account?{" "}
<Link href={`/signup?redirect=${encodeURIComponent(`/join?id=${code}`)}`} className="text-indigo-400 hover:text-indigo-300 transition-colors">
Sign up free
</Link>
</p>
</div>
);
}
const handleJoin = async () => {
setJoining(true);
const supabase = createClient();
const joinPromise = (async () => {
const { data: userData } = await supabase.auth.getUser();
const user = userData.user;
if (!user) throw new Error("Not authenticated");
const { data: board } = await supabase
.from("leaderboards")
.select("id")
.eq("join_code", code)
.single();
if (!board) throw new Error("Invalid invite code");
const { error } = await supabase.from("leaderboard_members").insert({
leaderboard_id: board.id,
user_id: user.id,
});
if (error) throw error;
return board;
})();
try {
await toast.promise(joinPromise, {
pending: "Joining leaderboard...",
success: "You're in! Welcome to the leaderboard.",
error: {
render({ data }) {
const err = data as Error;
if (err?.code === "23505") {
return "You are already a member of this leaderboard.";
}
return err?.message || "Failed to join. Please try again.";
},
},
});
router.push(`/leaderboard/${leaderboardSlug}`);
} finally {
setJoining(false);
}
};
return (
<button
onClick={handleJoin}
disabled={joining}
className="btn-primary inline-flex items-center justify-center gap-2 w-full py-4 text-sm font-bold rounded-xl shadow-lg shadow-indigo-500/20 hover:shadow-indigo-500/30 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{joining ? (
<>
<FontAwesomeIcon icon={faSpinner} className="w-5 h-5 animate-spin" />
Joining...
</>
) : (
<>
<FontAwesomeIcon icon={faUserPlus} className="w-5 h-5" />
Accept Invite & Join
</>
)}
</button>
);
}