new: usr: add timer for the acceptance of the challenge #4
This commit is contained in:
@@ -47,6 +47,7 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
||||
const [snapshotLoaded, setSnapshotLoaded] = useState(false);
|
||||
const [challengingGameAssoc, setChallengingGameAssoc] = useState(null);
|
||||
const [declinedMsg, setDeclinedMsg] = useState('');
|
||||
const [waitingCountdown, setWaitingCountdown] = useState(0);
|
||||
const declinedTimerRef = useRef(null);
|
||||
|
||||
const addPlayer = useCallback(entry => {
|
||||
@@ -111,6 +112,7 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
||||
setChallengingGameAssoc(null);
|
||||
clearTimeout(declinedTimerRef.current);
|
||||
setDeclinedMsg('Challenge was not accepted.');
|
||||
setWaitingCountdown(0);
|
||||
declinedTimerRef.current = setTimeout(() => setDeclinedMsg(''), 3500);
|
||||
};
|
||||
window.addEventListener('challenge-declined', handler);
|
||||
@@ -120,15 +122,30 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!waitingCountdown) return;
|
||||
const interval = setInterval(() => {
|
||||
setWaitingCountdown(prev => {
|
||||
if (1 >= prev) return 0;
|
||||
return prev - 1;
|
||||
});
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, [waitingCountdown]);
|
||||
|
||||
const handleChallenge = player => {
|
||||
if (challengingGameAssoc) return;
|
||||
setChallengingGameAssoc(player.gameAssoc);
|
||||
setDeclinedMsg('');
|
||||
setWaitingCountdown(30);
|
||||
fetch('/api/game/challenge/' + player.gameAssoc, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ challengerGameAssoc: currentGameAssoc }),
|
||||
}).catch(() => setChallengingGameAssoc(null));
|
||||
}).catch(() => {
|
||||
setChallengingGameAssoc(null);
|
||||
setWaitingCountdown(0);
|
||||
});
|
||||
};
|
||||
|
||||
const visible = players
|
||||
@@ -147,7 +164,12 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} sx={DIALOG_SX}>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={waitingCountdown > 0 ? undefined : onClose}
|
||||
disableEscapeKeyDown={waitingCountdown > 0}
|
||||
sx={DIALOG_SX}
|
||||
>
|
||||
<div className="opd">
|
||||
<div className="opd-header">
|
||||
<div className="opd-header-text">
|
||||
@@ -160,32 +182,44 @@ const OnlinePlayersDialog = ({ open, onClose, currentGameAssoc }) => {
|
||||
<div className="opd-header-actions">
|
||||
<button
|
||||
className={`opd-refresh${loading ? ' opd-refresh--spin' : ''}`}
|
||||
onClick={() => setRefreshKey(k => k + 1)}
|
||||
disabled={loading}
|
||||
onClick={() => { if (waitingCountdown === 0) setRefreshKey(k => k + 1); }}
|
||||
disabled={loading || waitingCountdown > 0}
|
||||
aria-label="Refresh"
|
||||
title="Refresh list"
|
||||
>
|
||||
<i className="fa fa-refresh" />
|
||||
</button>
|
||||
<button className="opd-close" onClick={onClose} aria-label="Close">
|
||||
<button
|
||||
className="opd-close"
|
||||
onClick={() => { if (waitingCountdown === 0) onClose(); }}
|
||||
disabled={waitingCountdown > 0}
|
||||
aria-label="Close"
|
||||
>
|
||||
<i className="fa fa-times" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="opd-search-wrap">
|
||||
<i className="fa fa-search opd-search-icon" />
|
||||
<input
|
||||
className="opd-search"
|
||||
placeholder="Search by username…"
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
/>
|
||||
{search && (
|
||||
<button className="opd-search-clear" onClick={() => setSearch('')}>
|
||||
<i className="fa fa-times" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{0 < waitingCountdown ? (
|
||||
<div className="opd-waiting">
|
||||
<i className="fa fa-hourglass-start" />
|
||||
<p>Waiting {waitingCountdown} second{1 === waitingCountdown ? '' : 's'} for opponent's answer...</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="opd-search-wrap">
|
||||
<i className="fa fa-search opd-search-icon" />
|
||||
<input
|
||||
className="opd-search"
|
||||
placeholder="Search by username…"
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
/>
|
||||
{search && (
|
||||
<button className="opd-search-clear" onClick={() => setSearch('')}>
|
||||
<i className="fa fa-times" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="opd-list">
|
||||
{loading && (
|
||||
<div className="opd-empty">
|
||||
|
||||
Reference in New Issue
Block a user