From c4ebec6b0718af61eb5206d950492f3e5bc40824 Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Wed, 11 Dec 2024 07:41:00 -0700 Subject: [PATCH] Disable Play and custom buttons when any active game searching mechanism is active --- src/views/Play/CustomGames.tsx | 13 +++--- src/views/Play/QuickMatch.tsx | 32 ++++++++------ src/views/Play/hooks.ts | 81 ++++++++++++++++++++++++++++++++++ src/views/Play/index.ts | 1 + 4 files changed, 107 insertions(+), 20 deletions(-) create mode 100644 src/views/Play/hooks.ts diff --git a/src/views/Play/CustomGames.tsx b/src/views/Play/CustomGames.tsx index a7e43ca095..f8f4a3dbae 100644 --- a/src/views/Play/CustomGames.tsx +++ b/src/views/Play/CustomGames.tsx @@ -50,6 +50,7 @@ import { RengoManagementPane } from "@/components/RengoManagementPane"; import { RengoTeamManagementPane } from "@/components/RengoTeamManagementPane"; import { PlayContext } from "./PlayContext"; import { challenge } from "@/components/ChallengeModal"; +import { active_challenges_emitter, useHaveActiveGameSearch } from "./hooks"; const CHALLENGE_LIST_FREEZE_PERIOD = 1000; // Freeze challenge list for this period while they move their mouse on it @@ -75,6 +76,8 @@ export function CustomGames(): JSX.Element { const canvas: HTMLCanvasElement = React.useMemo(() => allocateCanvasOrError(), []); const seekgraph = React.useRef(); + const disable_challenge_buttons = useHaveActiveGameSearch(); + const [seekGraphVisible, setSeekGraphVisible] = usePreference("show-seek-graph"); const toggleSeekGraph = () => { @@ -363,6 +366,9 @@ export function CustomGames(): JSX.Element { filter: default_filter as ChallengeFilter, }); seekgraph.current.on("challenges", updateChallenges); + seekgraph.current.on("challenges", (args) => { + active_challenges_emitter.emit("challenges", args); + }); const [match, id] = window.location.hash.match(/#rengo:(\d+)/) || []; if (match) { @@ -371,6 +377,7 @@ export function CustomGames(): JSX.Element { return () => { seekgraph.current?.destroy(); + active_challenges_emitter.emit("clear"); if (list_freeze_timeout.current) { clearTimeout(list_freeze_timeout.current); list_freeze_timeout.current = undefined; @@ -398,12 +405,6 @@ export function CustomGames(): JSX.Element { }); }, [live_list, cancelOpenChallenge]); - const disable_challenge_buttons = !!( - liveOwnChallengePending() || - live_rengo_challenge_to_show || - automatch_manager.active_live_automatcher - ); - if (!show_custom_games) { return (
diff --git a/src/views/Play/QuickMatch.tsx b/src/views/Play/QuickMatch.tsx index 490dda79fe..24dc303b61 100644 --- a/src/views/Play/QuickMatch.tsx +++ b/src/views/Play/QuickMatch.tsx @@ -51,6 +51,7 @@ import { Link } from "react-router-dom"; import Select, { components } from "react-select"; import { SPEED_OPTIONS } from "./SPEED_OPTIONS"; import { PlayerIcon } from "@/components/PlayerIcon"; +import { useHaveActiveGameSearch } from "./hooks"; moment.relativeTimeThreshold("m", 56); export interface SelectOption { @@ -225,6 +226,7 @@ export function QuickMatch(): JSX.Element { const [bot_spinner, setBotSpinner] = React.useState(false); const cancel_bot_game = React.useRef<() => void>(() => {}); const [game_clock, setGameClock] = preferences.usePreference("automatch.game-clock"); + const have_active_game_search = useHaveActiveGameSearch(); const [multiple_sizes, setMultipleSizes] = preferences.usePreference( "automatch.multiple-sizes", @@ -568,7 +570,7 @@ export function QuickMatch(): JSX.Element { }, []); */ - const search_active = + const automatch_search_active = !!automatch_manager.active_live_automatcher || correspondence_spinner || bot_spinner; function isSizeActive(size: Size) { @@ -832,7 +834,7 @@ export function QuickMatch(): JSX.Element { getActivityClass(s) } key={s} - disabled={search_active} + disabled={automatch_search_active} onClick={() => { toggleSize(s); }} @@ -933,7 +935,7 @@ export function QuickMatch(): JSX.Element { getActivityClass(board_size, speed, "fischer") } disabled={ - search_active || + automatch_search_active || (game_clock === "multiple" && speed === "correspondence") } @@ -982,7 +984,7 @@ export function QuickMatch(): JSX.Element { onClick={() => { toggleSpeedSystem(speed, "byoyomi"); }} - disabled={search_active} + disabled={automatch_search_active} > {selected_size_count > 1 && speed !== "correspondence" @@ -1023,10 +1025,10 @@ export function QuickMatch(): JSX.Element { className={ "opponent-option-container " + (opponent === "human" ? "active" : "") + - (search_active ? " disabled" : "") + (automatch_search_active ? " disabled" : "") } onClick={() => { - if (search_active) { + if (automatch_search_active) { return; } setOpponent("human"); @@ -1039,7 +1041,7 @@ export function QuickMatch(): JSX.Element { setUpperRankDiff(parseInt(ev.target.value))} - disabled={search_active} + disabled={automatch_search_active} > {user.anonymous ? ( @@ -1074,10 +1076,12 @@ export function QuickMatch(): JSX.Element { className={ "opponent-option-container " + (opponent === "bot" ? "active" : "") + - (search_active || game_clock === "multiple" ? " disabled" : "") + (automatch_search_active || game_clock === "multiple" + ? " disabled" + : "") } onClick={() => { - if (search_active || game_clock === "multiple") { + if (automatch_search_active || game_clock === "multiple") { return; } setOpponent("bot"); @@ -1106,7 +1110,7 @@ export function QuickMatch(): JSX.Element { minMenuHeight={400} maxMenuHeight={400} menuPlacement="auto" - isDisabled={search_active} + isDisabled={automatch_search_active} onChange={(opt) => { if (opt) { setSelectedBot(opt.id); @@ -1142,7 +1146,7 @@ export function QuickMatch(): JSX.Element { minMenuHeight={400} maxMenuHeight={400} menuPlacement="auto" - isDisabled={search_active} + isDisabled={automatch_search_active} onChange={(opt) => { if (opt) { setHandicaps(opt.value as "enabled" | "standard" | "disabled"); @@ -1225,11 +1229,11 @@ export function QuickMatch(): JSX.Element {
)} - {!search_active && !user.anonymous && ( + {!automatch_search_active && !user.anonymous && ( diff --git a/src/views/Play/hooks.ts b/src/views/Play/hooks.ts new file mode 100644 index 0000000000..54fa06fedd --- /dev/null +++ b/src/views/Play/hooks.ts @@ -0,0 +1,81 @@ +/* + * Copyright (C) Online-Go.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import * as React from "react"; +import { automatch_manager } from "@/lib/automatch_manager"; +import { Challenge } from "@/lib/challenge_utils"; +import { EventEmitter } from "eventemitter3"; + +interface Events { + challenges: (challenges: { [id: string]: Challenge } | undefined) => void; + clear: () => void; +} + +/* This emitter is called by the CustomGames component which creates the + * SeekGraph instance which does the work of subscribing to this data. */ +export const active_challenges_emitter = new EventEmitter(); + +export function useHaveActiveGameSearch(): boolean { + const [have_active_automatch, setHaveActiveAutomatch] = React.useState( + automatch_manager.active_live_automatcher, + ); + const [have_live_user_challenge, setHaveLiveUserChallenge] = React.useState(false); + + React.useEffect(() => { + function updateLiveAutomatcherStatus() { + setHaveActiveAutomatch(automatch_manager.active_live_automatcher); + } + + automatch_manager.on("entry", updateLiveAutomatcherStatus); + automatch_manager.on("start", updateLiveAutomatcherStatus); + automatch_manager.on("cancel", updateLiveAutomatcherStatus); + + return () => { + automatch_manager.removeListener("entry", updateLiveAutomatcherStatus); + automatch_manager.removeListener("start", updateLiveAutomatcherStatus); + automatch_manager.removeListener("cancel", updateLiveAutomatcherStatus); + }; + }, []); + + React.useEffect(() => { + function updateChallenges(args?: { [id: string]: Challenge }) { + let found_live_user_challenge = false; + + for (const challenge of Object.values(args || {})) { + if ( + challenge.user_challenge && + challenge.time_per_move > 0 && + challenge.time_per_move < 3600 + ) { + found_live_user_challenge = true; + } + } + + setHaveLiveUserChallenge(found_live_user_challenge); + } + + active_challenges_emitter.on("challenges", updateChallenges); + active_challenges_emitter.on("clear", updateChallenges); + + return () => { + active_challenges_emitter.removeListener("challenges", updateChallenges); + active_challenges_emitter.removeListener("clear", updateChallenges); + }; + }, []); + + return !!(have_active_automatch || have_live_user_challenge); +} diff --git a/src/views/Play/index.ts b/src/views/Play/index.ts index 571c05c284..b74f4f7463 100644 --- a/src/views/Play/index.ts +++ b/src/views/Play/index.ts @@ -18,3 +18,4 @@ export * from "./Play"; export * from "./ChallengeLists"; export * from "./AvailableQuickMatches"; +export * from "./hooks";