- Added isQueueing state to Player component to track matchmaking state.

- Implemented animated "Finding opponent…" UI with pulsing dots using Framer Motion.
- Added cancelQueue() to allow players to cancel matchmaking mid-search.
- Updated startQueue() to set queueing state immediately for instant feedback.
- Improved player experience by clearly showing matchmaking progress instead of silent waiting.
This commit is contained in:
2025-11-29 02:44:54 +05:30
parent a9e2d50b16
commit 8555675740

View File

@@ -21,6 +21,7 @@ export default function Player({
localStorage.getItem("username") ?? "" localStorage.getItem("username") ?? ""
); );
const [selectedMode, setSelectedMode] = useState("classic"); const [selectedMode, setSelectedMode] = useState("classic");
const [isQueueing, setIsQueueing] = useState(false);
// ------------------------------------------ // ------------------------------------------
// CONNECT // CONNECT
@@ -36,8 +37,21 @@ export default function Player({
// MATCHMAKING // MATCHMAKING
// ------------------------------------------ // ------------------------------------------
async function startQueue(selectedMode: string) { async function startQueue(selectedMode: string) {
const ticket = await joinMatchmaker(selectedMode); setIsQueueing(true);
console.log("Queued:", ticket);
try {
const ticket = await joinMatchmaker(selectedMode);
console.log("Queued:", ticket);
} catch (err) {
console.error("Matchmaking failed:", err);
setIsQueueing(false);
}
}
function cancelQueue() {
setIsQueueing(false);
// Nakama matchmaker tickets auto-expire by default in your setup.
// If you later add manual ticket cancel RPC, call it here.
} }
useEffect(() => { useEffect(() => {
@@ -148,22 +162,75 @@ export default function Player({
<option value="blitz">Blitz</option> <option value="blitz">Blitz</option>
</select> </select>
<motion.button {!isQueueing && (
whileTap={{ scale: 0.95 }} <motion.button
onClick={() => startQueue(selectedMode)} whileTap={{ scale: 0.95 }}
style={{ onClick={() => startQueue(selectedMode)}
padding: "10px 20px", style={{
borderRadius: "12px", padding: "10px 20px",
background: "#3498db", borderRadius: "12px",
color: "white", background: "#3498db",
border: "none", color: "white",
marginRight: "10px", border: "none",
cursor: "pointer", marginRight: "10px",
fontWeight: 600, cursor: "pointer",
}} fontWeight: 600,
> }}
Join Matchmaking >
</motion.button> Join Matchmaking
</motion.button>
)}
{/* Queueing animation */}
{isQueueing && (
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3 }}
style={{
marginTop: "10px",
marginBottom: "10px",
padding: "12px 16px",
borderRadius: "12px",
background: "#222",
color: "white",
display: "inline-block",
fontSize: "14px",
border: "1px solid #333",
}}
>
<div style={{ marginBottom: "6px", fontWeight: 600 }}>
Finding an opponent
</div>
{/* Animated pulsing dots */}
<motion.div
animate={{ opacity: [0.3, 1, 0.3] }}
transition={{ duration: 1.2, repeat: Infinity }}
style={{ letterSpacing: "2px", fontSize: "18px" }}
>
</motion.div>
{/* Cancel button */}
<button
onClick={cancelQueue}
style={{
marginTop: "10px",
padding: "6px 12px",
borderRadius: "8px",
background: "#e74c3c",
color: "white",
border: "none",
cursor: "pointer",
fontSize: "12px",
fontWeight: 600,
}}
>
Cancel
</button>
</motion.div>
)}
<motion.button <motion.button
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}