Create a usePopup to simplify popup creation

This commit is contained in:
Zoe Roux 2024-01-22 18:13:47 +01:00
parent d9f4a6ff8d
commit 53ac4a2050
4 changed files with 97 additions and 101 deletions

View File

@ -18,10 +18,10 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
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<ReactNode> }) => {
return (
<Portal>
<ContrastArea mode="user">
<SwitchVariant>
{({ css, theme }) => (
<View
@ -60,6 +60,19 @@ export const Popup = ({ children, ...props }: { children: ReactNode | YoshikiFun
</View>
)}
</SwitchVariant>
</Portal>
</ContrastArea>
);
};
export const usePopup = () => {
const { addPortal, removePortal } = usePortal();
const [current, setPopup] = useState<ReactNode>();
const close = useCallback(() => setPopup(undefined), []);
useEffect(() => {
addPortal("popup", current);
return () => removePortal("popup");
}, [current, addPortal, removePortal]);
return [setPopup, close];
};

View File

@ -18,7 +18,7 @@
* along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
*/
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<ReactElement | undefined>(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(
<MediaInfoPopup
mediaType={type}
mediaSlug={slug}
close={() => setPopup(undefined)}
/>,
)
setPopup(<MediaInfoPopup mediaType={type} mediaSlug={slug} close={close} />)
}
/>
</>
)}
</Menu>
{popup}
</>
);
};

View File

@ -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<ReactElement | undefined>(undefined);
const [setPopup, close] = usePopup();
return (
<Container
@ -246,30 +246,22 @@ export const TitleLine = ({
/>
)}
{type === "movie" && slug && (
<IconButton
icon={Download}
onPress={() => downloader(type, slug)}
color={{ xs: theme.user.contrast, md: theme.colors.white }}
{...tooltip(t("home.episodeMore.download"))}
/>
<>
<IconButton
icon={Download}
onPress={() => downloader(type, slug)}
color={{ xs: theme.user.contrast, md: theme.colors.white }}
{...tooltip(t("home.episodeMore.download"))}
/>
<IconButton
icon={MovieInfo}
color={{ xs: theme.user.contrast, md: theme.colors.white }}
onPress={() =>
setPopup(<MediaInfoPopup mediaType={"movie"} mediaSlug={slug!} close={close} />)
}
/>
</>
)}
{type === "movie" && (
<IconButton
icon={MovieInfo}
color={{ xs: theme.user.contrast, md: theme.colors.white }}
onPress={() =>
slug &&
setPopup(
<MediaInfoPopup
mediaType={type}
mediaSlug={slug}
close={() => setPopup(undefined)}
/>,
)
}
/>
)}
{popup}
{rating !== null && (
<>
<DottedSeparator

View File

@ -45,6 +45,7 @@ import {
SwitchVariant,
imageBorderRadius,
ts,
usePopup,
} from "@kyoo/primitives";
import { DefaultLayout } from "../layout";
import { Children, ComponentProps, ReactElement, ReactNode, useState } from "react";
@ -257,10 +258,11 @@ const ChangePasswordPopup = ({
);
};
const AccountSettings = ({ setPopup }: { setPopup: (e?: ReactElement) => 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<ReactElement | undefined>(undefined);
const theme = useUserTheme("auto");
return (
<>
<ScrollView contentContainerStyle={{ gap: ts(4), paddingBottom: ts(4) }}>
<SettingsContainer title={t("settings.general.label")}>
<Preference
icon={Theme}
<ScrollView contentContainerStyle={{ gap: ts(4), paddingBottom: ts(4) }}>
<SettingsContainer title={t("settings.general.label")}>
<Preference
icon={Theme}
label={t("settings.general.theme.label")}
description={t("settings.general.theme.description")}
>
<Select
label={t("settings.general.theme.label")}
description={t("settings.general.theme.description")}
>
<Select
label={t("settings.general.theme.label")}
value={theme}
onValueChange={(value) => setUserTheme(value)}
values={["auto", "light", "dark"]}
getLabel={(key) => t(`settings.general.theme.${key}`)}
/>
</Preference>
<Preference
icon={Language}
value={theme}
onValueChange={(value) => setUserTheme(value)}
values={["auto", "light", "dark"]}
getLabel={(key) => t(`settings.general.theme.${key}`)}
/>
</Preference>
<Preference
icon={Language}
label={t("settings.general.language.label")}
description={t("settings.general.language.description")}
>
<Select
label={t("settings.general.language.label")}
description={t("settings.general.language.description")}
>
<Select
label={t("settings.general.language.label")}
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
}
/>
</Preference>
</SettingsContainer>
<AccountSettings setPopup={setPopup} />
<SettingsContainer title={t("settings.about.label")}>
<Link
href="https://github.com/zoriya/kyoo/releases/latest/download/kyoo.apk"
target="_blank"
>
<Preference
icon={Android}
label={t("settings.about.android-app.label")}
description={t("settings.about.android-app.description")}
/>
</Link>
<Link href="https://github.com/zoriya/kyoo" target="_blank">
<Preference
icon={Public}
label={t("settings.about.git.label")}
description={t("settings.about.git.description")}
/>
</Link>
</SettingsContainer>
</ScrollView>
{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
}
/>
</Preference>
</SettingsContainer>
<AccountSettings />
<SettingsContainer title={t("settings.about.label")}>
<Link
href="https://github.com/zoriya/kyoo/releases/latest/download/kyoo.apk"
target="_blank"
>
<Preference
icon={Android}
label={t("settings.about.android-app.label")}
description={t("settings.about.android-app.description")}
/>
</Link>
<Link href="https://github.com/zoriya/kyoo" target="_blank">
<Preference
icon={Public}
label={t("settings.about.git.label")}
description={t("settings.about.git.description")}
/>
</Link>
</SettingsContainer>
</ScrollView>
);
};