diff --git a/front/packages/primitives/src/snackbar.tsx b/front/packages/primitives/src/snackbar.tsx new file mode 100644 index 00000000..81216d14 --- /dev/null +++ b/front/packages/primitives/src/snackbar.tsx @@ -0,0 +1,114 @@ +/* + * Kyoo - A portable and vast media library solution. + * Copyright (c) Kyoo. + * + * See AUTHORS.md and LICENSE file in the project root for full license information. + * + * Kyoo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * Kyoo 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Kyoo. If not, see . + */ + +import { usePortal } from "@gorhom/portal"; +import { ReactElement, createContext, useCallback, useContext, useRef } from "react"; +import { SwitchVariant } from "./themes"; +import { P } from "@expo/html-elements"; +import { View } from "react-native"; +import { min, percent, px } from "yoshiki/native"; +import { ts } from "./utils"; +import { Button } from "./button"; +import { imageBorderRadius } from "./constants"; + +export type Snackbar = { + key?: string; + label: string; + duration: number; + actions: Action[]; +}; + +export type Action = { + label: string; + icon: ReactElement; + action: () => void; +}; + +const SnackbarContext = createContext<(snackbar: Snackbar) => void>(null!); + +export const SnackbarProvider = ({ children }: { children: ReactElement | ReactElement[] }) => { + const { addPortal, removePortal } = usePortal(); + const snackbars = useRef([]); + const timeout = useRef(null); + + const createSnackbar = useCallback( + (snackbar: Snackbar) => { + if (snackbar.key) snackbars.current = snackbars.current.filter((x) => snackbar.key !== x.key); + snackbars.current.unshift(snackbar); + + if (timeout.current) return; + const updatePortal = () => { + const top = snackbars.current.pop(); + if (!top) { + timeout.current = null; + return; + } + + addPortal("snackbar", ); + timeout.current = setTimeout(() => { + removePortal("snackbar"); + updatePortal(); + }, snackbar.duration * 1000); + }; + updatePortal(); + }, + [addPortal, removePortal], + ); + + return {children}; +}; + +export const useSnackbar = () => { + return useContext(SnackbarContext); +}; + +const Snackbar = ({ label, actions }: Snackbar) => { + // TODO: take navbar height into account for setting the position of the snacbar. + return ( + + {({ css }) => ( + + theme.background, + maxWidth: { sm: percent(75), md: percent(45), lg: px(500) }, + margin: ts(1), + padding: ts(1), + flexDirection: "row", + borderRadius: imageBorderRadius, + })} + > +

{label}

+ {actions?.map((x, i) => ( +