diff --git a/package.json b/package.json
index 132954387e..815d1b6dd5 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
"express-http-proxy": "^2.0.0",
"fork-ts-checker-webpack-plugin": "^9.0.0",
"globals": "^15.8.0",
- "goban": "=8.3.64",
+ "goban": "=8.3.68",
"gulp": "^5.0.0",
"gulp-clean-css": "^4.3.0",
"gulp-eslint-new": "^2.2.0",
diff --git a/src/views/Play/AvailableQuickMatches.tsx b/src/views/Play/AvailableQuickMatches.tsx
new file mode 100644
index 0000000000..3fefe3e84a
--- /dev/null
+++ b/src/views/Play/AvailableQuickMatches.tsx
@@ -0,0 +1,138 @@
+/*
+ * 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 { socket } from "@/lib/sockets";
+import { useRefresh, useUser } from "@/lib/hooks";
+import { rankString } from "@/lib/rank_utils";
+import { _, llm_pgettext, pgettext } from "@/lib/translate";
+import { shortDurationString } from "goban";
+import { SPEED_OPTIONS } from "./SPEED_OPTIONS";
+/*
+import * as data from "@/lib/data";
+import * as preferences from "@/lib/preferences";
+import moment from "moment";
+*/
+
+export function AvailableQuickMatches(): JSX.Element {
+ const available = React.useRef<{ [uuid: string]: any }>({});
+ const refresh = useRefresh();
+ const user = useUser();
+
+ React.useEffect(() => {
+ socket.send("automatch/available/subscribe", undefined);
+
+ function onAdd(entry: any) {
+ console.log("onAdd", entry);
+ available.current[entry.uuid] = entry;
+ refresh();
+ }
+
+ function onRemove(uuid: string) {
+ console.log("onRemove", uuid);
+ delete available.current[uuid];
+ refresh();
+ }
+
+ socket.on("automatch/available/add", onAdd);
+ socket.on("automatch/available/remove", onRemove);
+
+ return () => {
+ socket.send("automatch/available/unsubscribe", undefined);
+ socket.off("automatch/available/add", onAdd);
+ socket.off("automatch/available/remove", onRemove);
+ };
+ }, []);
+
+ const available_list = Object.values(available.current).filter(
+ (entry) => entry.player.id !== user.id,
+ );
+ available_list.sort((a, b) => {
+ const a_speed = a.preferences.size_speed_options[0].speed;
+ const b_speed = b.preferences.size_speed_options[0].speed;
+ const a_speed_value =
+ a_speed === "blitz" ? 0 : a_speed === "rapid" ? 1 : a_speed === "live" ? 2 : 3;
+ const b_speed_value =
+ b_speed === "blitz" ? 0 : b_speed === "rapid" ? 1 : b_speed === "live" ? 2 : 3;
+ if (a_speed_value !== b_speed_value) {
+ return a_speed_value - b_speed_value;
+ }
+ return a.player.bounded_rank - b.player.bounded_rank;
+ });
+
+ return (
+
+
+ {llm_pgettext(
+ "Active automatch searches",
+ "Active quick match searches by other players",
+ )}
+
+
+
+
{_("Size")}
+
+
+
{pgettext("Clock settings header for a new game", "Clock")}
+
+ {pgettext("Cancel automatch", "Searching for game...")}
+
+
-
- )}
-
- {correspondence_spinner && (
-
-
{_("Finding you a game...")}
-
- {_(
- 'This can take several minutes. You will be notified when your match has been found. To view or cancel your automatch requests, please see the list below labeled "Your Automatch Requests".',
- )}
+ )}
+
+ {bot_spinner && (
+