From 50507a13793d076f782f97d8a7fda70d34d3a416 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 19 Jul 2022 00:36:18 +0200 Subject: [PATCH] Add a navbar --- .../error-snackbar.tsx} | 32 ++--- front/src/components/navbar.tsx | 121 ++++++++++++++++++ front/src/global.css | 3 + front/src/pages/_app.tsx | 10 +- front/src/utils/link.tsx | 116 +++-------------- front/src/utils/query.ts | 33 ++++- 6 files changed, 198 insertions(+), 117 deletions(-) rename front/src/{pages/toto.tsx => components/error-snackbar.tsx} (56%) create mode 100644 front/src/components/navbar.tsx create mode 100644 front/src/global.css diff --git a/front/src/pages/toto.tsx b/front/src/components/error-snackbar.tsx similarity index 56% rename from front/src/pages/toto.tsx rename to front/src/components/error-snackbar.tsx index 3ef0929d..aaf121b6 100644 --- a/front/src/pages/toto.tsx +++ b/front/src/components/error-snackbar.tsx @@ -18,26 +18,22 @@ * along with Kyoo. If not, see . */ -import { QueryPage, useFetch } from "~/utils/query"; -import useTranslation from "next-translate/useTranslation"; +import { Alert, Snackbar, SnackbarCloseReason } from "@mui/material"; +import { SyntheticEvent, useState } from "react"; +import { KyooErrors } from "~/models"; -const Toto: QueryPage = ({}) => { - const libraries = useFetch("libraries"); - const { t } = useTranslation("common"); - - if (libraries.error) return

oups

; - if (!libraries.data) return

loading

; +export const ErrorSnackbar = ({ error }: { error: KyooErrors }) => { + const [isOpen, setOpen] = useState(true); + const close = (_: Event | SyntheticEvent, reason?: SnackbarCloseReason) => { + if (reason !== "clickaway") setOpen(false); + }; + if (!isOpen) return null; return ( - <> -

{t("navbar.home")}

- {libraries.data.items.map((x: any) => ( -

{x.name}

- ))} - + + + {error.errors[0]} + + ); }; - -Toto.getFetchUrls = () => [["libraries"]]; - -export default Toto; diff --git a/front/src/components/navbar.tsx b/front/src/components/navbar.tsx new file mode 100644 index 00000000..60d8e242 --- /dev/null +++ b/front/src/components/navbar.tsx @@ -0,0 +1,121 @@ +/* + * 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 { + AppBar, + Toolbar, + Typography, + SxProps, + Theme, + Avatar, + IconButton, + Tooltip, + Box, + Skeleton, +} from "@mui/material"; +import MenuIcon from "@mui/icons-material/Menu"; +import logo from "../../public/icons/icon.svg"; +import useTranslation from "next-translate/useTranslation"; +import Image from "next/image"; +import { ButtonLink } from "~/utils/link"; +import { Library, Page } from "~/models"; +import { useFetch } from "~/utils/query"; +import { ErrorSnackbar } from "./error-snackbar"; + +export const KyooTitle = (props: { sx: SxProps }) => { + const { t } = useTranslation("common"); + + return ( + + + + + Kyoo + + + + ); +}; + +export const Navbar = () => { + const { t } = useTranslation("common"); + const { data, error, isSuccess, isError } = useFetch>("libraries"); + + return ( + + + + + + + + + + {isSuccess + ? data.items.map((library) => ( + + {library.name} + + )) + : [...Array(4)].map((_, i) => ( + + + + ))} + + + + + + + + {isError && } + + ); +}; diff --git a/front/src/global.css b/front/src/global.css new file mode 100644 index 00000000..fffb5a5d --- /dev/null +++ b/front/src/global.css @@ -0,0 +1,3 @@ +body { + margin: 0 !important; +} diff --git a/front/src/pages/_app.tsx b/front/src/pages/_app.tsx index daf7a40d..37a52654 100755 --- a/front/src/pages/_app.tsx +++ b/front/src/pages/_app.tsx @@ -26,6 +26,8 @@ import type { AppProps } from "next/app"; import { Hydrate, QueryClientProvider } from "react-query"; import { createQueryClient, fetchQuery } from "~/utils/query"; import { defaultTheme } from "~/utils/themes/default-theme"; +import { Navbar } from "~/components/navbar"; +import "../global.css" const App = ({ Component, pageProps }: AppProps) => { const [queryClient] = useState(() => createQueryClient()); @@ -33,6 +35,9 @@ const App = ({ Component, pageProps }: AppProps) => { + + {/* TODO: add a container to allow the component to be scrolled without the navbar */} + {/* TODO: add an option to disable the navbar in the component */} @@ -44,7 +49,10 @@ App.getInitialProps = async (ctx: AppContext) => { const appProps = await NextApp.getInitialProps(ctx); const getUrl = (ctx.Component as any).getFetchUrls; - if (getUrl) appProps.pageProps.queryState = await fetchQuery(getUrl(ctx.router.query)); + const urls: [[string]] = getUrl ? getUrl(ctx.router.query) : []; + // TODO: check if the navbar is needed for this + urls.push(["libraries"]); + appProps.pageProps.queryState = await fetchQuery(urls); return appProps; }; diff --git a/front/src/utils/link.tsx b/front/src/utils/link.tsx index 37ee297f..00b2542c 100644 --- a/front/src/utils/link.tsx +++ b/front/src/utils/link.tsx @@ -18,106 +18,30 @@ * along with Kyoo. If not, see . */ -// This file was shamelessly taken from: -// https://github.com/mui/material-ui/tree/master/examples/nextjs +import React, { forwardRef, Ref } from "react"; +import NLink, { LinkProps as NLinkProps} from "next/link"; +import { Button as MButton, ButtonProps, Link as MLink, LinkProps as MLinkProps} from "@mui/material"; -import * as React from "react"; -import clsx from "clsx"; -import { useRouter } from "next/router"; -import NextLink, { LinkProps as NextLinkProps } from "next/link"; -import MuiLink, { LinkProps as MuiLinkProps } from "@mui/material/Link"; -import { styled } from "@mui/material/styles"; +type ButtonRef = HTMLButtonElement; +type ButtonLinkProps = Omit & + Pick; -// Add support for the sx prop for consistency with the other branches. -const Anchor = styled("a")({}); - -interface NextLinkComposedProps - extends Omit, "href">, - Omit { - to: NextLinkProps["href"]; - linkAs?: NextLinkProps["as"]; -} - -export const NextLinkComposed = React.forwardRef( - function NextLinkComposed(props, ref) { - const { to, linkAs, replace, scroll, shallow, prefetch, locale, ...other } = props; - - return ( - - - - ); - }, +const NextButton = ({ href, as, prefetch, locale, ...props }: ButtonLinkProps, ref: Ref) => ( + + + ); -export type LinkProps = { - activeClassName?: string; - as?: NextLinkProps["as"]; - href: NextLinkProps["href"]; - linkAs?: NextLinkProps["as"]; // Useful when the as prop is shallow by styled(). - noLinkStyle?: boolean; -} & Omit & - Omit; +export const ButtonLink = forwardRef(NextButton); -// A styled version of the Next.js Link component: -// https://nextjs.org/docs/api-reference/next/link -export const Link = React.forwardRef(function Link(props, ref) { - const { - activeClassName = "active", - as, - className: classNameProps, - href, - linkAs: linkAsProp, - locale, - noLinkStyle, - prefetch, - replace, - role, // Link don't have roles. - scroll, - shallow, - ...other - } = props; +type LinkRef = HTMLAnchorElement; +type LinkProps = Omit & + Pick; - const router = useRouter(); - const pathname = typeof href === "string" ? href : href.pathname; - const className = clsx(classNameProps, { - [activeClassName]: router.pathname === pathname && activeClassName, - }); +const NextLink = ({ href, as, prefetch, locale, ...props }: LinkProps, ref: Ref) => ( + + + +); - const isExternal = - typeof href === "string" && (href.indexOf("http") === 0 || href.indexOf("mailto:") === 0); - - if (isExternal) { - if (noLinkStyle) { - return ; - } - - return ; - } - - const linkAs = linkAsProp || as; - const nextjsProps = { to: href, linkAs, replace, scroll, shallow, prefetch, locale }; - - if (noLinkStyle) { - return ; - } - - return ( - - ); -}); +export const Link = forwardRef(NextLink); diff --git a/front/src/utils/query.ts b/front/src/utils/query.ts index f1333885..1c4def36 100644 --- a/front/src/utils/query.ts +++ b/front/src/utils/query.ts @@ -19,7 +19,14 @@ */ import { ComponentType } from "react"; -import { dehydrate, QueryClient, QueryFunctionContext, useQuery } from "react-query"; +import { + dehydrate, + QueryClient, + QueryFunctionContext, + useInfiniteQuery, + useQuery, +} from "react-query"; +import { imageList, KyooErrors, Page } from "~/models"; const queryFn = async (context: QueryFunctionContext): Promise => { try { @@ -55,8 +62,30 @@ export type QueryPage = ComponentType & { getFetchUrls?: (route: { [key: string]: string }) => [[string]]; }; +const imageSelector = (obj: T): T => { + for (const img of imageList) { + // @ts-ignore + if (img in obj && !obj[img].startWith("/api")) { + // @ts-ignore + obj[img] = `/api/${obj[img]}`; + } + } + return obj; +}; + export const useFetch = (...params: [string]) => { - return useQuery(params); + return useQuery(params, { + select: imageSelector, + }); +}; + +export const useInfiniteFetch = (...params: [string]) => { + return useInfiniteQuery, KyooErrors>(params, { + select: (pages) => { + pages.pages.map((x) => x.items.map(imageSelector)); + return pages; + }, + }); }; export const fetchQuery = async (queries: [[string]]) => {