From 58d1d874926e45343d2c2beb17f58019a21891c9 Mon Sep 17 00:00:00 2001 From: Andrew Casal Date: Thu, 24 Oct 2024 13:44:41 -0400 Subject: [PATCH] add registry, separate files --- .../ChallengeModal/ChallengeModal.test.tsx | 11 ++--- src/components/ChallengeModal/ForkModal.tsx | 2 +- .../LanguagePicker/LanguagePicker.tsx | 10 ++--- src/components/Modal/ModalContext.tsx | 36 ++++++++++++++++ src/components/Modal/ModalProvider.tsx | 42 +++++-------------- src/components/Modal/ModalRegistry.ts | 38 +++++++++++++++++ src/views/Game/GameDock.tsx | 6 +-- src/views/Play/Play.tsx | 6 +-- 8 files changed, 102 insertions(+), 49 deletions(-) create mode 100644 src/components/Modal/ModalContext.tsx create mode 100644 src/components/Modal/ModalRegistry.ts diff --git a/src/components/ChallengeModal/ChallengeModal.test.tsx b/src/components/ChallengeModal/ChallengeModal.test.tsx index b59d8ba946..b95c98ad7e 100644 --- a/src/components/ChallengeModal/ChallengeModal.test.tsx +++ b/src/components/ChallengeModal/ChallengeModal.test.tsx @@ -20,7 +20,8 @@ import * as React from "react"; import * as ChallengeModal from "./ChallengeModal"; import { fireEvent, render, screen } from "@testing-library/react"; -import { ModalConsumer, ModalProvider, ModalTypes } from "../Modal/ModalProvider"; +import { ModalProvider } from "../Modal/ModalProvider"; +import { ModalContext, ModalTypes } from "../Modal/ModalContext"; import * as DynamicHelp from "react-dynamic-help"; jest.mock("./../Modal", () => { @@ -37,12 +38,12 @@ describe("ChallengeModal", () => { render( - + {({ showModal }) => { showModal(ModalTypes.Challenge); return null; }} - + , ); @@ -71,12 +72,12 @@ describe("ChallengeModal", () => { render( - + {({ showModal }) => { showModal(ModalTypes.Challenge); return null; }} - + , ); diff --git a/src/components/ChallengeModal/ForkModal.tsx b/src/components/ChallengeModal/ForkModal.tsx index cd8ce80614..2d4f9a4342 100644 --- a/src/components/ChallengeModal/ForkModal.tsx +++ b/src/components/ChallengeModal/ForkModal.tsx @@ -23,7 +23,7 @@ import { PlayerAutocomplete } from "@/components/PlayerAutocomplete"; import { MiniGoban } from "@/components/MiniGoban"; import { challenge } from "@/components/ChallengeModal"; import { PlayerCacheEntry } from "@/lib/player_cache"; -import { ModalContext } from "../Modal/ModalProvider"; +import { ModalContext } from "../Modal/ModalContext"; interface ForkModalProperties { goban: GobanRenderer; diff --git a/src/components/LanguagePicker/LanguagePicker.tsx b/src/components/LanguagePicker/LanguagePicker.tsx index cf0473b8b7..19b18407b6 100644 --- a/src/components/LanguagePicker/LanguagePicker.tsx +++ b/src/components/LanguagePicker/LanguagePicker.tsx @@ -18,7 +18,7 @@ import * as React from "react"; import { _, setCurrentLanguage, current_language, languages } from "@/lib/translate"; import * as preferences from "@/lib/preferences"; -import { ModalConsumer, ModalContext, ModalTypes } from "../Modal/ModalProvider"; +import { ModalContext, ModalTypes } from "../Modal/ModalContext"; function language_sorter(a: string, b: string) { if (a === "auto") { @@ -37,17 +37,17 @@ function language_sorter(a: string, b: string) { } export const LanguagePicker = () => ( - - {(value) => ( + + {({ showModal }) => ( value.showModal(ModalTypes.LanguagePicker)} + onClick={() => showModal(ModalTypes.LanguagePicker)} > {languages[current_language]} )} - + ); export const LanguagePickerModal = () => { diff --git a/src/components/Modal/ModalContext.tsx b/src/components/Modal/ModalContext.tsx new file mode 100644 index 0000000000..c244a05225 --- /dev/null +++ b/src/components/Modal/ModalContext.tsx @@ -0,0 +1,36 @@ +/* + * 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 { createContext } from "react"; + +export enum ModalTypes { + Challenge = "challenge", + LanguagePicker = "languagePicker", + Fork = "fork", +} + +type ModalContextProps = { + showModal: (type: ModalTypes, props?: any) => void; + hideModal: () => void; +}; + +const defaultModalContext: ModalContextProps = { + showModal: () => {}, + hideModal: () => {}, +}; + +export const ModalContext = createContext(defaultModalContext); diff --git a/src/components/Modal/ModalProvider.tsx b/src/components/Modal/ModalProvider.tsx index 8b92c1b668..b41c99d01c 100644 --- a/src/components/Modal/ModalProvider.tsx +++ b/src/components/Modal/ModalProvider.tsx @@ -18,22 +18,11 @@ /* cspell: words groupadmin cotsen */ import * as React from "react"; -import { ChallengeModal, ChallengeModes } from "../ChallengeModal"; +import { ChallengeModes } from "../ChallengeModal"; import { createPortal } from "react-dom"; -import { LanguagePickerModal } from "../LanguagePicker"; -import { ForkModal } from "../ChallengeModal/ForkModal"; import { GobanRenderer } from "goban"; - -type ModalProviderType = { - showModal: (type: ModalTypes, props?: ModalTypesProps) => void; - hideModal: () => void; -}; - -export enum ModalTypes { - Challenge = "challenge", - LanguagePicker = "languagePicker", - Fork = "fork", -} +import { ModalContext, ModalTypes } from "./ModalContext"; +import { modalRegistry } from "./ModalRegistry"; interface Modals { challenge: { @@ -50,16 +39,11 @@ type ModalTypesProps = { [key: string]: any; }; -export const ModalContext = React.createContext({} as ModalProviderType); -const { Provider, Consumer } = ModalContext; - -export const ModalConsumer = Consumer; - export const ModalProvider = ({ children }: React.PropsWithChildren): JSX.Element => { const [modalType, setModalType] = React.useState(null as ModalTypes | null); const [modalProps, setModalProps] = React.useState({} as ModalTypesProps); - const showModal = (type: ModalTypes, props?: ModalTypesProps) => { + const showModal = (type: ModalTypes, props?: any) => { setModalType(type); switch (type) { @@ -99,24 +83,18 @@ export const ModalProvider = ({ children }: React.PropsWithChildren): JSX.Elemen }, [modalType, hideModal]); return ( - + {modalType && createPortal(
- {modalType === ModalTypes.Challenge && ( - - )} - {modalType === ModalTypes.LanguagePicker && } - {modalType === ModalTypes.Fork && ( - - )} + {React.createElement(modalRegistry[modalType], { + ...modalProps, + onClose: hideModal, + })}
, document.body, )} {children} -
+ ); }; diff --git a/src/components/Modal/ModalRegistry.ts b/src/components/Modal/ModalRegistry.ts new file mode 100644 index 0000000000..70cfb66555 --- /dev/null +++ b/src/components/Modal/ModalRegistry.ts @@ -0,0 +1,38 @@ +/* + * 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 { ForkModal } from "../ChallengeModal/ForkModal"; +import { ChallengeModal } from "../ChallengeModal/ChallengeModal"; +import { LanguagePickerModal } from "../LanguagePicker/LanguagePicker"; +import { ModalTypes } from "./ModalContext"; + +interface ModalRegistry { + [key: string]: React.ComponentType; +} + +export const modalRegistry: ModalRegistry = { + [ModalTypes.Fork]: ForkModal, + [ModalTypes.Challenge]: ChallengeModal, + [ModalTypes.LanguagePicker]: LanguagePickerModal, +}; + +export const registerModal = (modalType: string, component: React.ComponentType) => { + modalRegistry[modalType] = component; +}; + +export const unregisterModal = (modalType: string) => { + delete modalRegistry[modalType]; +}; diff --git a/src/views/Game/GameDock.tsx b/src/views/Game/GameDock.tsx index 63d6f88136..3092d9216b 100644 --- a/src/views/Game/GameDock.tsx +++ b/src/views/Game/GameDock.tsx @@ -38,7 +38,7 @@ import { openGameInfoModal } from "./GameInfoModal"; import { useUserIsParticipant } from "./GameHooks"; import { useGoban } from "./goban_context"; import { Tooltip } from "../../components/Tooltip"; -import { ModalConsumer, ModalTypes } from "@/components/Modal/ModalProvider"; +import { ModalContext, ModalTypes } from "@/components/Modal/ModalContext"; interface DockProps { annulled: boolean; @@ -441,7 +441,7 @@ export function GameDock({ - + {({ showModal }) => ( { @@ -468,7 +468,7 @@ export function GameDock({ {_("Fork game")} )} - + diff --git a/src/views/Play/Play.tsx b/src/views/Play/Play.tsx index abaaf9f6b1..4cd0787d40 100644 --- a/src/views/Play/Play.tsx +++ b/src/views/Play/Play.tsx @@ -57,7 +57,7 @@ import { ChallengeFilterKey, shouldDisplayChallenge, } from "@/lib/challenge_utils"; -import { ModalConsumer, ModalTypes } from "../../components/Modal/ModalProvider"; +import { ModalContext, ModalTypes } from "@/components/Modal/ModalContext"; const CHALLENGE_LIST_FREEZE_PERIOD = 1000; // Freeze challenge list for this period while they move their mouse on it @@ -821,7 +821,7 @@ export class Play extends React.Component<{}, PlayState> {
- + {({ showModal }) => { return ( ); }} - +