- 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") ?? ""
|
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 }}
|
||||||
|
|||||||
Reference in New Issue
Block a user