mirror of
https://github.com/hsjobeki/noogle.git
synced 2024-08-17 00:30:21 +03:00
Fix/separate appstate (#33)
* add: appState component for wrapping the state * add: viewMode to pageState * add: wrapp everything with the pageState at top level
This commit is contained in:
parent
ca48535b85
commit
39ccbcd763
73
website/components/appState/appState.tsx
Normal file
73
website/components/appState/appState.tsx
Normal file
@ -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<PageState | null>(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 ? (
|
||||
<LinearProgress />
|
||||
) : (
|
||||
<PageContextProvider pageProps={initialProps}>
|
||||
{children}
|
||||
</PageContextProvider>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
1
website/components/appState/index.ts
Normal file
1
website/components/appState/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { AppState } from "./appState";
|
@ -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<ViewMode>("explore");
|
||||
const { page, itemsPerPage, FOTD: showFunctionOfTheDay, data } = pageState;
|
||||
|
||||
const setViewMode = setPageStateVariable<ViewMode>("viewMode");
|
||||
const setPage = setPageStateVariable<number>("page");
|
||||
const setTerm = setPageStateVariable<string>("term");
|
||||
const setFilter = setPageStateVariable<Filter>("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 (
|
||||
<Stack>
|
||||
<SearchInput
|
||||
@ -80,15 +73,16 @@ export function BasicList(props: BasicListProps) {
|
||||
placeholder="search nix functions"
|
||||
handleSearch={handleSearch}
|
||||
/>
|
||||
|
||||
{showFunctionOfTheDay ? (
|
||||
{showFunctionOfTheDay && (
|
||||
<FunctionOfTheDay
|
||||
data={data}
|
||||
handleClose={() => {
|
||||
setMode("browse");
|
||||
setViewMode("browse");
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
)}
|
||||
|
||||
{!showFunctionOfTheDay && (
|
||||
<List aria-label="basic-list" sx={{ pt: 0 }}>
|
||||
{items.length ? (
|
||||
pageItems.map(({ item, key }, idx) => (
|
||||
|
@ -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<PageState>(pageProps);
|
||||
const { term, filter, viewMode } = pageState;
|
||||
function setPageStateVariable<T>(field: keyof InitialPageState) {
|
||||
return function (value: React.SetStateAction<T> | 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 (
|
||||
<PageContext.Provider
|
||||
value={{ pageState, setPageState, setPageStateVariable, resetQueries }}
|
||||
value={{
|
||||
pageState,
|
||||
setPageState,
|
||||
setPageStateVariable,
|
||||
resetQueries,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</PageContext.Provider>
|
||||
|
@ -37,8 +37,6 @@ export function SearchInput(props: SearchInputProps) {
|
||||
const _handleClear = () => {
|
||||
_setTerm("");
|
||||
handleClear();
|
||||
// handleFilter(initialPageState.filter);
|
||||
// handleSubmit("");
|
||||
};
|
||||
const handleType = (
|
||||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||
|
@ -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<PageState, keyof ComputedState>;
|
||||
@ -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 };
|
@ -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<MyAppProps> = (props) => {
|
||||
<meta />
|
||||
<meta name="robots" content="all" />
|
||||
<link rel="icon" href="/favicon.png" />
|
||||
<link rel="search" type="application/opensearchdescription+xml" title="Search nix function on noogle" href="/search.xml"></link>
|
||||
<link
|
||||
rel="search"
|
||||
type="application/opensearchdescription+xml"
|
||||
title="Search nix function on noogle"
|
||||
href="/search.xml"
|
||||
></link>
|
||||
</Head>
|
||||
|
||||
<CacheProvider value={emotionCache}>
|
||||
@ -59,7 +65,7 @@ const MyApp: React.FunctionComponent<MyAppProps> = (props) => {
|
||||
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
|
||||
maxSnack={1}
|
||||
>
|
||||
{getContent()}
|
||||
<AppState>{getContent()}</AppState>
|
||||
</SnackbarProvider>
|
||||
</ThemeProvider>
|
||||
</CacheProvider>
|
||||
|
@ -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<PageState | null>(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 ? (
|
||||
<LinearProgress />
|
||||
) : (
|
||||
<PageContextProvider pageProps={initialProps}>
|
||||
<PageContext.Consumer>
|
||||
{(context) => (
|
||||
<NixFunctions
|
||||
pageState={context.pageState}
|
||||
setPageStateVariable={context.setPageStateVariable}
|
||||
/>
|
||||
)}
|
||||
</PageContext.Consumer>
|
||||
</PageContextProvider>
|
||||
)}
|
||||
</>
|
||||
<NixFunctions
|
||||
pageState={pageState}
|
||||
setPageStateVariable={setPageStateVariable}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user