From 0aea2f47dad6eb78e319ea1abd8c444f2cba4424 Mon Sep 17 00:00:00 2001 From: zyachel Date: Sat, 3 Jun 2023 22:12:54 +0530 Subject: [PATCH] fix(error): fix incorrect 'view on IMDb' link on error page the error was due to a faulty logic. 'useRouter' was being used to detect pathname, which doesn't keep original url on 404 page. this commit fixes that. this commit also makes it easy to go to IMDb by adding a clear link on error page. closes https://github.com/zyachel/libremdb/issues/50 --- next.config.mjs | 23 +++++++------------ src/components/error/ErrorInfo.tsx | 16 +++++++++++--- src/interfaces/shared/error.ts | 8 +++---- src/layouts/Header.tsx | 34 ++++++++--------------------- src/layouts/Layout.tsx | 5 +++-- src/pages/[...error]/index.tsx | 25 +++++++++++++++++++++ src/pages/find/index.tsx | 22 +++++++++++++------ src/pages/name/[nameId]/index.tsx | 15 ++++++++----- src/pages/title/[titleId]/index.tsx | 15 ++++++++----- 9 files changed, 94 insertions(+), 69 deletions(-) create mode 100644 src/pages/[...error]/index.tsx diff --git a/next.config.mjs b/next.config.mjs index ff6f94a..adbc227 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -2,21 +2,14 @@ const nextConfig = { reactStrictMode: true, swcMinify: true, - async rewrites() { - return { - afterFiles: [ - { - source: '/', - destination: '/find', - }, - ], - fallback: [ - { - source: '/:path*', - destination: '/404', - }, - ], - }; + async redirects() { + return [ + { + source: '/', + destination: '/find', + permanent: true, + }, + ]; }, images: { domains: ['m.media-amazon.com'], diff --git a/src/components/error/ErrorInfo.tsx b/src/components/error/ErrorInfo.tsx index e42b55e..07cca5f 100644 --- a/src/components/error/ErrorInfo.tsx +++ b/src/components/error/ErrorInfo.tsx @@ -11,7 +11,8 @@ import styles from 'src/styles/modules/components/error/error-info.module.scss'; type Props = { message: string; statusCode?: number; - // props specific to error boundary. + originalPath?: string; + /** props specific to error boundary. */ misc?: { subtext: string; buttonText: string; @@ -19,12 +20,12 @@ type Props = { }; }; -const ErrorInfo = ({ message, statusCode, misc }: Props) => { +const ErrorInfo = ({ message, statusCode, misc, originalPath }: Props) => { const title = statusCode ? `${message} (${statusCode})` : message; return ( <> - + { the homepage + , or view this route{' '} + + on IMDb + .

)} diff --git a/src/interfaces/shared/error.ts b/src/interfaces/shared/error.ts index 477d852..9beabf8 100644 --- a/src/interfaces/shared/error.ts +++ b/src/interfaces/shared/error.ts @@ -1,5 +1,3 @@ -export type AppError = { - message: string; - statusCode: number; - stack?: any; -}; +import { AppError as AppErrorClass } from 'src/utils/helpers'; + +export type AppError = Omit, 'name'>; diff --git a/src/layouts/Header.tsx b/src/layouts/Header.tsx index b95c9e5..5154742 100644 --- a/src/layouts/Header.tsx +++ b/src/layouts/Header.tsx @@ -1,21 +1,14 @@ -import { ReactNode } from 'react'; -import { useRouter } from 'next/router'; import Link from 'next/link'; import ThemeToggler from 'src/components/buttons/ThemeToggler'; import styles from 'src/styles/modules/layout/header.module.scss'; -type Props = { full?: boolean; children?: ReactNode }; - -const Header = (props: Props) => { - const { asPath: path } = useRouter(); +type Props = { full?: boolean; originalPath?: string }; +const Header = ({ full, originalPath }: Props) => { return ( -
- + @@ -23,7 +16,7 @@ const Header = (props: Props) => { libremdb - {props.full && ( + {full && ( )}
- - - View on IMDb (opens in new tab) - + + View on IMDb (opens in new tab) @@ -68,7 +55,7 @@ const Header = (props: Props) => {
- {props.full && ( + {full && (

A free & open source IMDb front-end @@ -83,10 +70,7 @@ const Header = (props: Props) => { nitter , and{' '} - + many others . diff --git a/src/layouts/Layout.tsx b/src/layouts/Layout.tsx index 756693f..d26e019 100644 --- a/src/layouts/Layout.tsx +++ b/src/layouts/Layout.tsx @@ -6,12 +6,13 @@ type Props = { full?: true; children: ReactNode; className: string; + originalPath?: string; }; -const Layout = ({ full, children, className }: Props) => { +const Layout = ({ full, children, className, originalPath }: Props) => { return ( <> -
+
{children}
diff --git a/src/pages/[...error]/index.tsx b/src/pages/[...error]/index.tsx new file mode 100644 index 0000000..a732dd5 --- /dev/null +++ b/src/pages/[...error]/index.tsx @@ -0,0 +1,25 @@ +import type { GetServerSideProps, InferGetServerSidePropsType } from 'next'; +import ErrorInfo from 'src/components/error/ErrorInfo'; + +const error = { + statusCode: 404, + message: 'Not found, sorry.', +} as const; + +type Props = InferGetServerSidePropsType; + +const Error404 = ({ originalPath }: Props) => { + return ; +}; + +export default Error404; + +type Data = { originalPath: string }; +type Params = { error: string[] }; + +export const getServerSideProps: GetServerSideProps = async ctx => { + ctx.res.statusCode = error.statusCode; + ctx.res.statusMessage = error.message; + + return { props: { originalPath: ctx.resolvedUrl } }; +}; diff --git a/src/pages/find/index.tsx b/src/pages/find/index.tsx index 7bf325a..cb63df5 100644 --- a/src/pages/find/index.tsx +++ b/src/pages/find/index.tsx @@ -21,13 +21,16 @@ const getMetadata = (title: string | null) => ({ : 'Search for anything on libremdb, a free & open source IMDb front-end', }); -const BasicSearch = ({ data: { title, results }, error }: Props) => { - if (error) return ; +const BasicSearch = ({ data: { title, results }, error, originalPath }: Props) => { + if (error) return ; + + let layoutClassName = styles.find; + if (!title) layoutClassName += ' ' + styles.find__home; return ( <> - + {title && ( // only showing when user has searched for something )} @@ -38,17 +41,21 @@ const BasicSearch = ({ data: { title, results }, error }: Props) => { }; // TODO: use generics for passing in queryParams(to components) for better type-checking. -type Data = +type Data = ( | { data: { title: string; results: Find }; error: null } | { data: { title: null; results: null }; error: null } - | { data: { title: string; results: null }; error: AppError }; + | { data: { title: string; results: null }; error: AppError } +) & { + originalPath: string; +}; export const getServerSideProps: GetServerSideProps = async ctx => { // sample query str: find/?q=babylon&s=tt&ttype=ft&exact=true const queryObj = ctx.query as FindQueryParams; const query = queryObj.q?.trim(); + const originalPath = ctx.resolvedUrl; - if (!query) return { props: { data: { title: null, results: null }, error: null } }; + if (!query) return { props: { data: { title: null, results: null }, error: null, originalPath } }; try { const entries = Object.entries(queryObj); @@ -57,7 +64,7 @@ export const getServerSideProps: GetServerSideProps = asy const res = await getOrSetApiCache(findKey(queryStr), basicSearch, queryStr); return { - props: { data: { title: query, results: res }, error: null }, + props: { data: { title: query, results: res }, error: null, originalPath }, }; } catch (error: any) { const { message, statusCode } = error; @@ -68,6 +75,7 @@ export const getServerSideProps: GetServerSideProps = asy props: { error: { message, statusCode }, data: { title: query, results: null }, + originalPath, }, }; } diff --git a/src/pages/name/[nameId]/index.tsx b/src/pages/name/[nameId]/index.tsx index 51c35d3..e3afcd5 100644 --- a/src/pages/name/[nameId]/index.tsx +++ b/src/pages/name/[nameId]/index.tsx @@ -14,8 +14,8 @@ import styles from 'src/styles/modules/pages/name/name.module.scss'; type Props = InferGetServerSidePropsType; -const NameInfo = ({ data, error }: Props) => { - if (error) return ; +const NameInfo = ({ data, error, originalPath }: Props) => { + if (error) return ; return ( <> @@ -24,7 +24,7 @@ const NameInfo = ({ data, error }: Props) => { description={data.basic.bio.short + '...'} imgUrl={data.basic.poster?.url && getProxiedIMDbImgUrl(data.basic.poster.url)} /> - +
@@ -41,23 +41,26 @@ const NameInfo = ({ data, error }: Props) => { ); }; -type Data = { data: Name; error: null } | { error: AppError; data: null }; +type Data = ({ data: Name; error: null } | { error: AppError; data: null }) & { + originalPath: string; +}; type Params = { nameId: string }; export const getServerSideProps: GetServerSideProps = async ctx => { const nameId = ctx.params!.nameId; + const originalPath = ctx.resolvedUrl; try { const data = await getOrSetApiCache(nameKey(nameId), name, nameId); - return { props: { data, error: null } }; + return { props: { data, error: null, originalPath } }; } catch (error: any) { const { message, statusCode } = error; ctx.res.statusCode = statusCode; ctx.res.statusMessage = message; - return { props: { error: { message, statusCode }, data: null } }; + return { props: { error: { message, statusCode }, data: null, originalPath } }; } }; diff --git a/src/pages/title/[titleId]/index.tsx b/src/pages/title/[titleId]/index.tsx index fb27c5e..3fab840 100644 --- a/src/pages/title/[titleId]/index.tsx +++ b/src/pages/title/[titleId]/index.tsx @@ -15,8 +15,8 @@ import styles from 'src/styles/modules/pages/title/title.module.scss'; type Props = InferGetServerSidePropsType; // TO-DO: make a wrapper page component to display errors, if present in props -const TitleInfo = ({ data, error }: Props) => { - if (error) return ; +const TitleInfo = ({ data, error, originalPath }: Props) => { + if (error) return ; const info = { meta: data.meta, @@ -34,7 +34,7 @@ const TitleInfo = ({ data, error }: Props) => { description={data.basic.plot ?? undefined} imgUrl={data.basic.poster?.url && getProxiedIMDbImgUrl(data.basic.poster.url)} /> - + @@ -50,22 +50,25 @@ const TitleInfo = ({ data, error }: Props) => { }; // TO-DO: make a getServerSideProps wrapper for handling errors -type Data = { data: Title; error: null } | { error: AppError; data: null }; +type Data = ({ data: Title; error: null } | { error: AppError; data: null }) & { + originalPath: string; +}; type Params = { titleId: string }; export const getServerSideProps: GetServerSideProps = async ctx => { const titleId = ctx.params!.titleId; + const originalPath = ctx.resolvedUrl; try { const data = await getOrSetApiCache(titleKey(titleId), title, titleId); - return { props: { data, error: null } }; + return { props: { data, error: null, originalPath } }; } catch (error: any) { const { message, statusCode } = error; ctx.res.statusCode = statusCode; ctx.res.statusMessage = message; - return { props: { error: { message, statusCode }, data: null } }; + return { props: { error: { message, statusCode }, data: null, originalPath } }; } };