- 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:
@@ -21,6 +21,7 @@ export default function Player({
|
||||
localStorage.getItem("username") ?? ""
|
||||
);
|
||||
const [selectedMode, setSelectedMode] = useState("classic");
|
||||
const [isQueueing, setIsQueueing] = useState(false);
|
||||
|
||||
// ------------------------------------------
|
||||
// CONNECT
|
||||
@@ -36,8 +37,21 @@ export default function Player({
|
||||
// MATCHMAKING
|
||||
// ------------------------------------------
|
||||
async function startQueue(selectedMode: string) {
|
||||
setIsQueueing(true);
|
||||
|
||||
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(() => {
|
||||
@@ -148,6 +162,7 @@ export default function Player({
|
||||
<option value="blitz">Blitz</option>
|
||||
</select>
|
||||
|
||||
{!isQueueing && (
|
||||
<motion.button
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={() => startQueue(selectedMode)}
|
||||
@@ -164,6 +179,58 @@ export default function Player({
|
||||
>
|
||||
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
|
||||
whileTap={{ scale: 0.95 }}
|
||||
|
||||
Reference in New Issue
Block a user