mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-30 23:27:55 -05:00
Prevent double click to confirm result
This commit is contained in:
@@ -40,6 +40,8 @@ import styles from "./SendouQMatchActionTab.module.css";
|
||||
|
||||
const CONFIRM_COOLDOWN_MS = 5_000;
|
||||
const CONFIRM_LOSS_ARMED_MS = 5_000;
|
||||
/** Guards against an accidental double-click satisfying the loss "tap again". */
|
||||
const CONFIRM_LOSS_MIN_GAP_MS = 300;
|
||||
const MAP_CHANGED_COOLDOWN_MS = 10_000;
|
||||
|
||||
export function SendouQMatchActionTab({
|
||||
@@ -353,8 +355,13 @@ function ScoreConfirmerSection({
|
||||
const disputeScore = useActionSubmit(matchSchema);
|
||||
const [cooldownUntil] = useState(() => Date.now() + CONFIRM_COOLDOWN_MS);
|
||||
const cooldownSecondsLeft = useCooldown(cooldownUntil);
|
||||
const [lossArmedUntil, setLossArmedUntil] = useState<number | null>(null);
|
||||
const isLossArmed = useCooldown(lossArmedUntil) > 0;
|
||||
const [lossArmedAt, setLossArmedAt] = useState<number | null>(null);
|
||||
const isLossArmed =
|
||||
useCooldown(
|
||||
typeof lossArmedAt === "number"
|
||||
? lossArmedAt + CONFIRM_LOSS_ARMED_MS
|
||||
: null,
|
||||
) > 0;
|
||||
const [hasDisputed, setHasDisputed] = useState(false);
|
||||
|
||||
const decidingMap = [...data.match.mapList]
|
||||
@@ -432,9 +439,17 @@ function ScoreConfirmerSection({
|
||||
isDisabled={cooldownSecondsLeft > 0}
|
||||
isPending={confirmScore.state !== "idle"}
|
||||
onClick={() => {
|
||||
if (outcome === "loss" && !isLossArmed) {
|
||||
setLossArmedUntil(Date.now() + CONFIRM_LOSS_ARMED_MS);
|
||||
return;
|
||||
if (outcome === "loss") {
|
||||
if (!isLossArmed) {
|
||||
setLossArmedAt(Date.now());
|
||||
return;
|
||||
}
|
||||
if (
|
||||
typeof lossArmedAt === "number" &&
|
||||
Date.now() - lossArmedAt < CONFIRM_LOSS_MIN_GAP_MS
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
submitConfirmation();
|
||||
}}
|
||||
|
||||
@@ -22,6 +22,9 @@ type Tab = "action" | "result" | "rosters";
|
||||
|
||||
const MAPS_TO_WIN = Math.ceil(SENDOUQ_BEST_OF / 2);
|
||||
|
||||
/** The loss confirm ignores taps for `CONFIRM_LOSS_MIN_GAP_MS` after arming. */
|
||||
const LOSS_CONFIRM_MIN_GAP_MS = 400;
|
||||
|
||||
const TEAM_NAMES: Record<Side, string> = {
|
||||
ALPHA: "Group Alpha",
|
||||
BRAVO: "Group Bravo",
|
||||
@@ -161,8 +164,13 @@ export class SendouQMatchPage {
|
||||
hasText: "Tap again",
|
||||
});
|
||||
|
||||
const isArmed = await armedLossButton.isVisible();
|
||||
if (isArmed) {
|
||||
await waitOutLossConfirmMinGap(this.page);
|
||||
}
|
||||
|
||||
await waitForPOSTResponse(this.page, async () => {
|
||||
if (await armedLossButton.isVisible()) {
|
||||
if (isArmed) {
|
||||
await armedLossButton.click();
|
||||
}
|
||||
});
|
||||
@@ -241,3 +249,9 @@ export class SendouQMatchPage {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Waits out the gap the loss confirm keeps between arming and accepting the second tap. */
|
||||
export async function waitOutLossConfirmMinGap(page: Page) {
|
||||
// biome-ignore lint/nursery/noPlaywrightWaitForTimeout: the min gap after arming has no observable end
|
||||
await page.waitForTimeout(LOSS_CONFIRM_MIN_GAP_MS);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user