From 39ccbcd763779bf34be05fad7aa2276d91f82e2c Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Thu, 27 Apr 2023 19:11:40 +0200 Subject: [PATCH] Fix/separate appstate (#33) * add: appState component for wrapping the state * add: viewMode to pageState * add: wrapp everything with the pageState at top level --- website/components/appState/appState.tsx | 73 +++++++++++++++++ website/components/appState/index.ts | 1 + website/components/basicList/basicList.tsx | 24 +++--- .../components/pageContext/pageContext.tsx | 21 ++++- .../components/searchInput/searchInput.tsx | 2 - website/models/internals.ts | 16 ++-- website/pages/_app.tsx | 10 ++- website/pages/index.tsx | 79 ++----------------- 8 files changed, 128 insertions(+), 98 deletions(-) create mode 100644 website/components/appState/appState.tsx create mode 100644 website/components/appState/index.ts diff --git a/website/components/appState/appState.tsx b/website/components/appState/appState.tsx new file mode 100644 index 0000000..4421823 --- /dev/null +++ b/website/components/appState/appState.tsx @@ -0,0 +1,73 @@ +import React, { useEffect, useState } from "react"; + +import { NextRouter, useRouter } from "next/router"; +import { LinearProgress } from "@mui/material"; +import { + InitialPageState, + initialPageState, + PageState, +} from "../../models/internals"; +import { PageContextProvider } from "../pageContext"; + +const getInitialProps = async (context: NextRouter) => { + const { query } = context; + const initialProps = { ...initialPageState }; + + Object.entries(query).forEach(([key, value]) => { + if (value && !Array.isArray(value)) { + try { + const parsedValue = JSON.parse(value) as never; + const initialValue = initialPageState[key as keyof InitialPageState]; + + if (!initialValue || typeof parsedValue === typeof initialValue) { + initialProps[key as keyof InitialPageState] = JSON.parse( + value + ) as never; + } else { + throw "Type of query param does not match the initial values type"; + } + } catch (error) { + console.error("Invalid query:", { key, value, error }); + } + } + }); + const FOTD = Object.entries(query).length === 0; + + return { + props: { + ...initialProps, + FOTD, + }, + }; +}; + +interface AppStateProps { + children: React.ReactNode; +} +export function AppState(props: AppStateProps) { + const { children } = props; + const router = useRouter(); + const [initialProps, setInitialProps] = useState(null); + useEffect(() => { + if (router.isReady && initialProps === null) { + getInitialProps(router).then((r) => { + const { props } = r; + console.info("Url Query changed\n\nUpdating pageState with delta:", { + props, + }); + setInitialProps((curr) => ({ ...curr, ...props })); + }); + } + }, [router, initialProps]); + return ( + <> + {!initialProps ? ( + + ) : ( + + {children} + + )} + + ); +} diff --git a/website/components/appState/index.ts b/website/components/appState/index.ts new file mode 100644 index 0000000..0be4524 --- /dev/null +++ b/website/components/appState/index.ts @@ -0,0 +1 @@ +export { AppState } from "./appState"; \ No newline at end of file diff --git a/website/components/basicList/basicList.tsx b/website/components/basicList/basicList.tsx index 59a6b17..0562316 100644 --- a/website/components/basicList/basicList.tsx +++ b/website/components/basicList/basicList.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from "react"; +import React, { useMemo } from "react"; import { Box, List, ListItem, Stack, TablePagination } from "@mui/material"; import { BasicDataViewProps } from "../../types/basicDataView"; import { SearchInput } from "../searchInput"; @@ -9,6 +9,7 @@ import { useMobile } from "../layout/layout"; import { EmptyRecordsPlaceholder } from "../emptyRecordsPlaceholder"; import { FunctionOfTheDay } from "../functionOfTheDay"; import { Query, SearchOptions } from "minisearch"; +import { ViewMode } from "../../models/internals"; export type BasicListItem = { item: React.ReactNode; @@ -19,15 +20,13 @@ export type BasicListProps = BasicDataViewProps & { search: (query: Query, options?: SearchOptions) => void; }; -type ViewMode = "explore" | "browse"; - export function BasicList(props: BasicListProps) { const { items, search } = props; const { pageState, setPageStateVariable, resetQueries } = usePageContext(); const isMobile = useMobile(); - const { page, itemsPerPage, filter, term, FOTD, data } = pageState; - const [mode, setMode] = useState("explore"); + const { page, itemsPerPage, FOTD: showFunctionOfTheDay, data } = pageState; + const setViewMode = setPageStateVariable("viewMode"); const setPage = setPageStateVariable("page"); const setTerm = setPageStateVariable("term"); const setFilter = setPageStateVariable("filter"); @@ -66,12 +65,6 @@ export function BasicList(props: BasicListProps) { setPage(1); }; - const showFunctionOfTheDay = - mode === "explore" && - filter.to === "any" && - filter.from === "any" && - term === "" && - FOTD === true; return ( - - {showFunctionOfTheDay ? ( + {showFunctionOfTheDay && ( { - setMode("browse"); + setViewMode("browse"); }} /> - ) : ( + )} + + {!showFunctionOfTheDay && ( {items.length ? ( pageItems.map(({ item, key }, idx) => ( diff --git a/website/components/pageContext/pageContext.tsx b/website/components/pageContext/pageContext.tsx index 461980f..175a4e2 100644 --- a/website/components/pageContext/pageContext.tsx +++ b/website/components/pageContext/pageContext.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { NextRouter, useRouter } from "next/router"; import { InitialPageState, @@ -57,6 +57,7 @@ export const PageContextProvider = (props: PageContextProviderProps) => { const router = useRouter(); const { children, pageProps } = props; const [pageState, setPageState] = useState(pageProps); + const { term, filter, viewMode } = pageState; function setPageStateVariable(field: keyof InitialPageState) { return function (value: React.SetStateAction | T) { if (typeof value !== "function") { @@ -82,9 +83,25 @@ export const PageContextProvider = (props: PageContextProviderProps) => { } setPageState((curr) => ({ ...curr, ...initialPageState })); } + useEffect(() => { + setPageState((c) => ({ + ...c, + FOTD: + viewMode === "explore" && + filter.to === "any" && + filter.from === "any" && + term === "", + })); + }, [viewMode, filter, term]); + return ( {children} diff --git a/website/components/searchInput/searchInput.tsx b/website/components/searchInput/searchInput.tsx index 52ebc32..fca2b20 100644 --- a/website/components/searchInput/searchInput.tsx +++ b/website/components/searchInput/searchInput.tsx @@ -37,8 +37,6 @@ export function SearchInput(props: SearchInputProps) { const _handleClear = () => { _setTerm(""); handleClear(); - // handleFilter(initialPageState.filter); - // handleSubmit(""); }; const handleType = ( e: React.ChangeEvent diff --git a/website/models/internals.ts b/website/models/internals.ts index 21a883a..37b9c15 100644 --- a/website/models/internals.ts +++ b/website/models/internals.ts @@ -1,17 +1,20 @@ import { data } from "./data"; import { MetaData, NixType } from "./nix"; +export type ViewMode = "explore" | "browse"; + export type ComputedState = { FOTD: boolean; } export type PageState = { - data: MetaData; - selected: string | null; - term: string; - filter: Filter; - page: number; - itemsPerPage: number; + data: MetaData; + selected: string | null; + term: string; + filter: Filter; + page: number; + itemsPerPage: number; + viewMode: ViewMode; } & ComputedState; export type InitialPageState = Omit; @@ -23,6 +26,7 @@ export const initialPageState: InitialPageState = { filter: { from: "any", to: "any" }, page: 1, itemsPerPage: 10, + viewMode: "explore" }; export type Filter = { to: NixType; from: NixType }; \ No newline at end of file diff --git a/website/pages/_app.tsx b/website/pages/_app.tsx index fd40338..1aad6a2 100644 --- a/website/pages/_app.tsx +++ b/website/pages/_app.tsx @@ -15,6 +15,7 @@ import { lightThemeOptions, darkThemeOptions } from "../styles/theme"; import "../styles/globals.css"; import { Layout } from "../components/layout"; import Head from "next/head"; +import { AppState } from "../components/appState"; interface MyAppProps extends AppProps { emotionCache?: EmotionCache; @@ -49,7 +50,12 @@ const MyApp: React.FunctionComponent = (props) => { - + @@ -59,7 +65,7 @@ const MyApp: React.FunctionComponent = (props) => { anchorOrigin={{ vertical: "bottom", horizontal: "center" }} maxSnack={1} > - {getContent()} + {getContent()} diff --git a/website/pages/index.tsx b/website/pages/index.tsx index ab99f14..fbb7f53 100644 --- a/website/pages/index.tsx +++ b/website/pages/index.tsx @@ -1,77 +1,14 @@ -import React, { useEffect, useState } from "react"; -import { - initialPageState, - PageState, - InitialPageState, -} from "../models/internals"; -import { PageContext, PageContextProvider } from "../components/pageContext"; +import React from "react"; + +import { usePageContext } from "../components/pageContext"; import NixFunctions from "../components/NixFunctions"; -import { NextRouter, useRouter } from "next/router"; -import { LinearProgress } from "@mui/material"; - -const getInitialProps = async (context: NextRouter) => { - const { query } = context; - const initialProps = { ...initialPageState }; - - Object.entries(query).forEach(([key, value]) => { - if (value && !Array.isArray(value)) { - try { - const parsedValue = JSON.parse(value) as never; - const initialValue = initialPageState[key as keyof InitialPageState]; - - if (!initialValue || typeof parsedValue === typeof initialValue) { - initialProps[key as keyof InitialPageState] = JSON.parse( - value - ) as never; - } else { - throw "Type of query param does not match the initial values type"; - } - } catch (error) { - console.error("Invalid query:", { key, value, error }); - } - } - }); - const FOTD = Object.entries(query).length === 0; - - return { - props: { - ...initialProps, - FOTD, - }, - }; -}; - export default function FunctionsPage() { - const router = useRouter(); - const [initialProps, setInitialProps] = useState(null); - useEffect(() => { - if (router.isReady && initialProps === null) { - getInitialProps(router).then((r) => { - const { props } = r; - console.debug("Url Query changed\n\nUpdating pageState with delta:", { - props, - }); - setInitialProps((curr) => ({ ...curr, ...props })); - }); - } - }, [router, initialProps]); + const { pageState, setPageStateVariable } = usePageContext(); return ( - <> - {!initialProps ? ( - - ) : ( - - - {(context) => ( - - )} - - - )} - + ); }