From 53ac4a2050c9ba04dea9e56764011098a1272360 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 22 Jan 2024 18:13:47 +0100 Subject: [PATCH] Create a usePopup to simplify popup creation --- front/packages/primitives/src/popup.tsx | 23 +++- .../ui/src/components/context-menus.tsx | 13 +- front/packages/ui/src/details/header.tsx | 44 +++---- front/packages/ui/src/settings/index.tsx | 118 +++++++++--------- 4 files changed, 97 insertions(+), 101 deletions(-) diff --git a/front/packages/primitives/src/popup.tsx b/front/packages/primitives/src/popup.tsx index 18800133..ae9963d0 100644 --- a/front/packages/primitives/src/popup.tsx +++ b/front/packages/primitives/src/popup.tsx @@ -18,10 +18,10 @@ * along with Kyoo. If not, see . */ -import { ReactNode } from "react"; +import { ReactElement, ReactNode, useCallback, useEffect, useState } from "react"; import { Container } from "./container"; -import { Portal } from "@gorhom/portal"; -import { SwitchVariant, YoshikiFunc } from "./themes"; +import { Portal, usePortal } from "@gorhom/portal"; +import { ContrastArea, SwitchVariant, YoshikiFunc } from "./themes"; import { View } from "react-native"; import { imageBorderRadius } from "./constants"; import { px } from "yoshiki/native"; @@ -29,7 +29,7 @@ import { ts } from "./utils"; export const Popup = ({ children, ...props }: { children: ReactNode | YoshikiFunc }) => { return ( - + {({ css, theme }) => ( )} - + ); }; + +export const usePopup = () => { + const { addPortal, removePortal } = usePortal(); + const [current, setPopup] = useState(); + const close = useCallback(() => setPopup(undefined), []); + + useEffect(() => { + addPortal("popup", current); + return () => removePortal("popup"); + }, [current, addPortal, removePortal]); + + return [setPopup, close]; +}; diff --git a/front/packages/ui/src/components/context-menus.tsx b/front/packages/ui/src/components/context-menus.tsx index 15ef6d8d..71ad0eac 100644 --- a/front/packages/ui/src/components/context-menus.tsx +++ b/front/packages/ui/src/components/context-menus.tsx @@ -18,7 +18,7 @@ * along with Kyoo. If not, see . */ -import { IconButton, Menu, tooltip } from "@kyoo/primitives"; +import { IconButton, Menu, tooltip, usePopup } from "@kyoo/primitives"; import { ComponentProps, ReactElement, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import MoreVert from "@material-symbols/svg-400/rounded/more_vert.svg"; @@ -51,7 +51,7 @@ export const EpisodesContext = ({ const downloader = useDownloader(); const { css } = useYoshiki(); const { t } = useTranslation(); - const [popup, setPopup] = useState(undefined); + const [setPopup, close] = usePopup(); const queryClient = useQueryClient(); const mutation = useMutation({ @@ -109,19 +109,12 @@ export const EpisodesContext = ({ label={t("home.episodeMore.mediainfo")} icon={MovieInfo} onSelect={() => - setPopup( - setPopup(undefined)} - />, - ) + setPopup() } /> )} - {popup} ); }; diff --git a/front/packages/ui/src/details/header.tsx b/front/packages/ui/src/details/header.tsx index 8193003f..4a1a8b4c 100644 --- a/front/packages/ui/src/details/header.tsx +++ b/front/packages/ui/src/details/header.tsx @@ -47,9 +47,9 @@ import { ts, Chip, DottedSeparator, - focusReset, + usePopup, } from "@kyoo/primitives"; -import { Fragment, ReactElement, useState } from "react"; +import { Fragment } from "react"; import { useTranslation } from "react-i18next"; import MovieInfo from "@material-symbols/svg-400/rounded/movie_info.svg"; import { ImageStyle, Platform, View } from "react-native"; @@ -112,7 +112,7 @@ export const TitleLine = ({ const { css, theme } = useYoshiki(); const { t } = useTranslation(); const downloader = useDownloader(); - const [popup, setPopup] = useState(undefined); + const [setPopup, close] = usePopup(); return ( )} {type === "movie" && slug && ( - downloader(type, slug)} - color={{ xs: theme.user.contrast, md: theme.colors.white }} - {...tooltip(t("home.episodeMore.download"))} - /> + <> + downloader(type, slug)} + color={{ xs: theme.user.contrast, md: theme.colors.white }} + {...tooltip(t("home.episodeMore.download"))} + /> + + setPopup() + } + /> + )} - {type === "movie" && ( - - slug && - setPopup( - setPopup(undefined)} - />, - ) - } - /> - )} - {popup} {rating !== null && ( <> void }) => { +const AccountSettings = () => { const account = useAccount(); const { css, theme } = useYoshiki(); const { t } = useTranslation(); + const [setPopup, close] = usePopup(); const queryClient = useQueryClient(); const { mutateAsync } = useMutation({ @@ -335,7 +337,7 @@ const AccountSettings = ({ setPopup }: { setPopup: (e?: ReactElement) => void }) label={t("settings.account.username.label")} inital={account.username} apply={async (v) => await mutateAsync({ username: v })} - close={() => setPopup(undefined)} + close={close} />, ) } @@ -356,7 +358,7 @@ const AccountSettings = ({ setPopup }: { setPopup: (e?: ReactElement) => void }) label={t("settings.account.email.label")} inital={account.email} apply={async (v) => await mutateAsync({ email: v })} - close={() => setPopup(undefined)} + close={close} />, ) } @@ -375,7 +377,7 @@ const AccountSettings = ({ setPopup }: { setPopup: (e?: ReactElement) => void }) icon={Password} label={t("settings.account.password.label")} apply={async (op, np) => await editPassword({ oldPassword: op, newPassword: np })} - close={() => setPopup(undefined)} + close={close} />, ) } @@ -389,67 +391,63 @@ const AccountSettings = ({ setPopup }: { setPopup: (e?: ReactElement) => void }) export const SettingsPage: QueryPage = () => { const { t, i18n } = useTranslation(); const languages = new Intl.DisplayNames([i18n.language ?? "en"], { type: "language" }); - const [popup, setPopup] = useState(undefined); const theme = useUserTheme("auto"); return ( - <> - - - + + + setUserTheme(value)} - values={["auto", "light", "dark"]} - getLabel={(key) => t(`settings.general.theme.${key}`)} - /> - - setUserTheme(value)} + values={["auto", "light", "dark"]} + getLabel={(key) => t(`settings.general.theme.${key}`)} + /> + + + - i18n.changeLanguage(value !== "system" ? value : (i18n.options.lng as string)) - } - values={["system", ...Object.keys(i18n.options.resources!)]} - getLabel={(key) => - key === "system" ? t("settings.general.language.system") : languages.of(key) ?? key - } - /> - - - - - - - - - - - - - {popup} - + value={i18n.resolvedLanguage!} + onValueChange={(value) => + i18n.changeLanguage(value !== "system" ? value : (i18n.options.lng as string)) + } + values={["system", ...Object.keys(i18n.options.resources!)]} + getLabel={(key) => + key === "system" ? t("settings.general.language.system") : languages.of(key) ?? key + } + /> + + + + + + + + + + + + ); };