Fix top nagivation flickering, add 404 page
This commit is contained in:
parent
ae30e8d4a8
commit
b43abafa1c
@ -1,17 +1,17 @@
|
|||||||
|
import Head from "next/head";
|
||||||
import { PropsWithChildren } from "react";
|
import { PropsWithChildren } from "react";
|
||||||
import { Navigation } from "../Navigation";
|
|
||||||
|
|
||||||
export interface PageProps {
|
export interface PageProps {
|
||||||
className?: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Page({ className, children }: PropsWithChildren<PageProps>) {
|
export function Page({ title, children }: PropsWithChildren<PageProps>) {
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<>
|
||||||
<header>
|
<Head>
|
||||||
<Navigation />
|
<title>{title}</title>
|
||||||
</header>
|
</Head>
|
||||||
<main className={className}>{children}</main>
|
{children}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
10
src/pages/404.tsx
Normal file
10
src/pages/404.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { NextPage } from "next";
|
||||||
|
import { PageWithError } from "../views/PageWithError";
|
||||||
|
|
||||||
|
const PageTitle = "Flash Card App - 404";
|
||||||
|
|
||||||
|
const Page404: NextPage = () => {
|
||||||
|
return <PageWithError title={PageTitle} message="404 - content not found" />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page404;
|
@ -1,7 +1,17 @@
|
|||||||
import "../styles/globals.css";
|
import "../styles/globals.css";
|
||||||
import "../styles/components.css";
|
import "../styles/components.css";
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
|
import { Navigation } from "../components";
|
||||||
|
|
||||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||||
return <Component {...pageProps} />;
|
return (
|
||||||
|
<div className="page">
|
||||||
|
<header>
|
||||||
|
<Navigation />
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,10 @@ import { useExpressionSetQueryId } from "../../hooks/useExpressionSetQueryId";
|
|||||||
import { useExpressionsInSet } from "../../hooks/useExpressionsInSet";
|
import { useExpressionsInSet } from "../../hooks/useExpressionsInSet";
|
||||||
import { PageWithError } from "../../views/PageWithError/PageWithError";
|
import { PageWithError } from "../../views/PageWithError/PageWithError";
|
||||||
|
|
||||||
|
function pageTitle(name?: string) {
|
||||||
|
return `Flash Card App - ${name || "Sets"}`;
|
||||||
|
}
|
||||||
|
|
||||||
const ExpressionSetDetailsPage: NextPage = () => {
|
const ExpressionSetDetailsPage: NextPage = () => {
|
||||||
const expression_set_id = useExpressionSetQueryId();
|
const expression_set_id = useExpressionSetQueryId();
|
||||||
const expression_set = useExpressionSet(expression_set_id);
|
const expression_set = useExpressionSet(expression_set_id);
|
||||||
@ -15,13 +19,15 @@ const ExpressionSetDetailsPage: NextPage = () => {
|
|||||||
|
|
||||||
// Fallback for expression set not found
|
// Fallback for expression set not found
|
||||||
if (!expression_set) {
|
if (!expression_set) {
|
||||||
return <PageWithError message="Expression set not found" />;
|
return (
|
||||||
|
<PageWithError title={pageTitle()} message="Expression set not found" />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for expression set empty
|
// Fallback for expression set empty
|
||||||
if (expressions.length === 0)
|
if (expressions.length === 0)
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page title={pageTitle(expression_set.name)}>
|
||||||
<div className="page-with-padding scroll">
|
<div className="page-with-padding scroll">
|
||||||
<ExpressionSetInfo
|
<ExpressionSetInfo
|
||||||
id={expression_set.id!}
|
id={expression_set.id!}
|
||||||
@ -35,7 +41,7 @@ const ExpressionSetDetailsPage: NextPage = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page title={pageTitle(expression_set.name)}>
|
||||||
<div className="page-with-bottom-navigation">
|
<div className="page-with-bottom-navigation">
|
||||||
<section className="padding-small scroll">
|
<section className="padding-small scroll">
|
||||||
<ExpressionSetInfo
|
<ExpressionSetInfo
|
||||||
|
@ -8,15 +8,19 @@ import { useExpressionsInSet } from "../../hooks/useExpressionsInSet";
|
|||||||
import { IndexedExpressionSet } from "../../model";
|
import { IndexedExpressionSet } from "../../model";
|
||||||
import { PageWithError } from "../../views/PageWithError";
|
import { PageWithError } from "../../views/PageWithError";
|
||||||
|
|
||||||
|
const PageTitle = "Flash Card App - Sets";
|
||||||
|
|
||||||
const ExpressionSetListPage: NextPage = () => {
|
const ExpressionSetListPage: NextPage = () => {
|
||||||
const expression_sets = useExpressionSets();
|
const expression_sets = useExpressionSets();
|
||||||
|
|
||||||
if (!expression_sets?.length) {
|
if (!expression_sets?.length) {
|
||||||
return <PageWithError message="No expression sets found" />;
|
return (
|
||||||
|
<PageWithError title={PageTitle} message="No expression sets found" />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page title={PageTitle}>
|
||||||
<div className="page-with-padding content-list scroll">
|
<div className="page-with-padding content-list scroll">
|
||||||
{expression_sets.map(({ id, name, description }) => (
|
{expression_sets.map(({ id, name, description }) => (
|
||||||
<ExpressionSetLink
|
<ExpressionSetLink
|
||||||
|
@ -16,6 +16,8 @@ import {
|
|||||||
import { sample } from "../../util/array-utils";
|
import { sample } from "../../util/array-utils";
|
||||||
import { PageWithError } from "../../views/PageWithError/PageWithError";
|
import { PageWithError } from "../../views/PageWithError/PageWithError";
|
||||||
|
|
||||||
|
const PageTitle = "Flash Card App - Practice";
|
||||||
|
|
||||||
// Do random selection here so we don't keep flipping states with interaction
|
// Do random selection here so we don't keep flipping states with interaction
|
||||||
const ExpressionPracticePage: NextPage = () => {
|
const ExpressionPracticePage: NextPage = () => {
|
||||||
// Query info
|
// Query info
|
||||||
@ -36,10 +38,17 @@ const ExpressionPracticePage: NextPage = () => {
|
|||||||
|
|
||||||
// Fallback views for expression set content not found
|
// Fallback views for expression set content not found
|
||||||
if (!expression_set_id) {
|
if (!expression_set_id) {
|
||||||
return <PageWithError message="Expression set not found" />;
|
return (
|
||||||
|
<PageWithError title={PageTitle} message="Expression set not found" />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!expression) {
|
if (!expression) {
|
||||||
return <PageWithError message="No expressions left in this set" />;
|
return (
|
||||||
|
<PageWithError
|
||||||
|
title={PageTitle}
|
||||||
|
message="No expressions left in this set"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -63,7 +72,7 @@ function ExpressionCardPracticeView({
|
|||||||
}: ExpressionCardPracticeViewProps) {
|
}: ExpressionCardPracticeViewProps) {
|
||||||
const [revealed, setRevealed] = useState(false);
|
const [revealed, setRevealed] = useState(false);
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page title={PageTitle}>
|
||||||
<div className="page-with-bottom-navigation">
|
<div className="page-with-bottom-navigation">
|
||||||
<section className="padding-small scroll">
|
<section className="padding-small scroll">
|
||||||
<ExpressionCard
|
<ExpressionCard
|
||||||
@ -120,7 +129,6 @@ function DemoteExpressionButton({ expression_id }: ExpressionIdProps) {
|
|||||||
const { query, pathname, push } = useRouter();
|
const { query, pathname, push } = useRouter();
|
||||||
const expression_set_id = useExpressionSetQueryId();
|
const expression_set_id = useExpressionSetQueryId();
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
() => {
|
|
||||||
if (expression_set_id === 1) {
|
if (expression_set_id === 1) {
|
||||||
const filter_ids = query["filter-ids"]
|
const filter_ids = query["filter-ids"]
|
||||||
? `${query["filter-ids"]} ${expression_id}`
|
? `${query["filter-ids"]} ${expression_id}`
|
||||||
@ -138,8 +146,8 @@ function DemoteExpressionButton({ expression_id }: ExpressionIdProps) {
|
|||||||
expression_set_id: Math.max(1, expression_set_id - 1),
|
expression_set_id: Math.max(1, expression_set_id - 1),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
}, [expression_id, expression_set_id, pathname, push, query]);
|
||||||
}, [expression_id, expression_set_id, pathname, query, push]);
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className="navigation-item bottom text-navigation grow"
|
className="navigation-item bottom text-navigation grow"
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import type { NextPage } from "next";
|
import type { NextPage } from "next";
|
||||||
import { Page } from "../components/Page";
|
import { Page } from "../components/Page";
|
||||||
|
|
||||||
|
const PageTitle = "Flash Card App";
|
||||||
|
|
||||||
const Home: NextPage = () => {
|
const Home: NextPage = () => {
|
||||||
return <Page />;
|
return <Page title={PageTitle} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { Page } from "../../components/Page";
|
import { Page } from "../../components/Page";
|
||||||
|
|
||||||
export interface PageWithErrorProps {
|
export interface PageWithErrorProps {
|
||||||
|
title: string;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PageWithError({ message }: PageWithErrorProps) {
|
export function PageWithError({ title, message }: PageWithErrorProps) {
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page title={title}>
|
||||||
<div className="page-with-padding">
|
<div className="page-with-padding">
|
||||||
<p className="text-meta">{message}</p>
|
<p className="text-meta">{message}</p>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user