From d37dd134b5f1e9f5fd5eb2591bcb67ed80a4a569 Mon Sep 17 00:00:00 2001 From: Igor Lobanov Date: Mon, 31 Jul 2023 22:19:08 +0200 Subject: [PATCH] 404 page (#151) * 404 page * lint * build fix --- src/components/App.tsx | 3 ++- src/components/Article/FullArticle.tsx | 2 +- src/pages/article.page.server.ts | 5 +++++ src/pages/fourOuFour.page.route.ts | 4 ++++ src/renderer/_default.page.client.tsx | 11 ++++++++++- src/renderer/_default.page.server.tsx | 8 ++++++-- src/renderer/_error.page.ts | 4 ++++ src/renderer/types.ts | 2 +- src/stores/router.ts | 13 ++++--------- 9 files changed, 37 insertions(+), 15 deletions(-) create mode 100644 src/pages/fourOuFour.page.route.ts create mode 100644 src/renderer/_error.page.ts diff --git a/src/components/App.tsx b/src/components/App.tsx index 0481610f..dd6f0189 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -76,7 +76,8 @@ const pagesMap: Record> = { thanks: ThanksPage, profileSettings: ProfileSettingsPage, profileSecurity: ProfileSecurityPage, - profileSubscriptions: ProfileSubscriptionsPage + profileSubscriptions: ProfileSubscriptionsPage, + fourOuFour: FourOuFourPage } export const App = (props: PageProps) => { diff --git a/src/components/Article/FullArticle.tsx b/src/components/Article/FullArticle.tsx index e2bc5689..78a76403 100644 --- a/src/components/Article/FullArticle.tsx +++ b/src/components/Article/FullArticle.tsx @@ -1,4 +1,4 @@ -import { capitalize, formatDate } from '../../utils' +import { formatDate } from '../../utils' import { Icon } from '../_shared/Icon' import { AuthorCard } from '../Author/AuthorCard' import { AudioPlayer } from './AudioPlayer' diff --git a/src/pages/article.page.server.ts b/src/pages/article.page.server.ts index e4777649..fd439321 100644 --- a/src/pages/article.page.server.ts +++ b/src/pages/article.page.server.ts @@ -1,11 +1,16 @@ import type { PageContext } from '../renderer/types' import type { PageProps } from './types' import { apiClient } from '../utils/apiClient' +import { RenderErrorPage } from 'vite-plugin-ssr/RenderErrorPage' export const onBeforeRender = async (pageContext: PageContext) => { const { slug } = pageContext.routeParams const article = await apiClient.getShoutBySlug(slug) + if (!article) { + throw RenderErrorPage({ pageContext: {} }) + } + const pageProps: PageProps = { article } return { diff --git a/src/pages/fourOuFour.page.route.ts b/src/pages/fourOuFour.page.route.ts new file mode 100644 index 00000000..ae3ffde9 --- /dev/null +++ b/src/pages/fourOuFour.page.route.ts @@ -0,0 +1,4 @@ +import { ROUTES } from '../stores/router' +import { getServerRoute } from '../utils/getServerRoute' + +export default getServerRoute(ROUTES.fourOuFour) diff --git a/src/renderer/_default.page.client.tsx b/src/renderer/_default.page.client.tsx index 1348e177..937896dc 100644 --- a/src/renderer/_default.page.client.tsx +++ b/src/renderer/_default.page.client.tsx @@ -8,11 +8,20 @@ import HttpApi from 'i18next-http-backend' import * as Sentry from '@sentry/browser' import { SENTRY_DSN } from '../utils/config' import { resolveHydrationPromise } from '../utils/hydrationPromise' +import { initRouter } from '../stores/router' let layoutReady = false export const render = async (pageContext: PageContextBuiltInClientWithClientRouting & PageContext) => { - const { lng, pageProps } = pageContext + const { lng, pageProps, is404 } = pageContext + + if (is404) { + initRouter('/404') + } else { + const { pathname, search } = window.location + const searchParams = Object.fromEntries(new URLSearchParams(search)) + initRouter(pathname, searchParams) + } if (SENTRY_DSN) { Sentry.init({ diff --git a/src/renderer/_default.page.server.tsx b/src/renderer/_default.page.server.tsx index b8d652de..1501e428 100644 --- a/src/renderer/_default.page.server.tsx +++ b/src/renderer/_default.page.server.tsx @@ -9,7 +9,7 @@ import ru from '../../public/locales/ru/translation.json' import en from '../../public/locales/en/translation.json' import type { Language } from '../context/localize' -export const passToClient = ['pageProps', 'lng', 'documentProps'] +export const passToClient = ['pageProps', 'lng', 'documentProps', 'is404'] const metaTags = [] @@ -47,7 +47,11 @@ export const render = async (pageContext: PageContext) => { await changeLanguage(lng) } - initRouter(pageContext.urlParsed.pathname, pageContext.urlParsed.search) + if (pageContext.is404) { + initRouter('/404') + } else { + initRouter(pageContext.urlParsed.pathname, pageContext.urlParsed.search) + } pageContext.lng = lng diff --git a/src/renderer/_error.page.ts b/src/renderer/_error.page.ts new file mode 100644 index 00000000..f3733631 --- /dev/null +++ b/src/renderer/_error.page.ts @@ -0,0 +1,4 @@ +// this file is required by vite-plugin-ssr to show something after an error occurred +// it's empty because error handling logic lives in _default.page.server.tsx and _default.page.client.tsx + +// eslint-disable-next-line unicorn/no-empty-file diff --git a/src/renderer/types.ts b/src/renderer/types.ts index 1a466336..f7b03b9b 100644 --- a/src/renderer/types.ts +++ b/src/renderer/types.ts @@ -1,4 +1,4 @@ -import type { PageContextBuiltIn } from 'vite-plugin-ssr' +import type { PageContextBuiltIn } from 'vite-plugin-ssr/types' import type { PageProps } from '../pages/types' import type { Component } from 'solid-js' diff --git a/src/stores/router.ts b/src/stores/router.ts index 4b7325f7..9afb1d59 100644 --- a/src/stores/router.ts +++ b/src/stores/router.ts @@ -23,7 +23,6 @@ export const ROUTES = { feedBookmarks: '/feed/bookmarks', feedCollaborations: '/feed/collaborations', search: '/search/:q?', - article: '/:slug', dogma: '/about/dogma', discussionRules: '/about/discussion-rules', guide: '/about/guide', @@ -37,7 +36,9 @@ export const ROUTES = { expo: '/expo/:layout', profileSettings: '/profile/settings', profileSecurity: '/profile/security', - profileSubscriptions: '/profile/subscriptions' + profileSubscriptions: '/profile/subscriptions', + fourOuFour: '/404', + article: '/:slug' } as const const searchParamsStore = createSearchParams() @@ -117,7 +118,7 @@ const handleClientRouteLinkClick = async (event) => { scrollToHash(url.hash) } -export const initRouter = (pathname: string, search: Record) => { +export const initRouter = (pathname: string, search?: Record) => { routerStore.open(pathname) const params = Object.fromEntries(new URLSearchParams(search)) searchParamsStore.open(params) @@ -127,12 +128,6 @@ export const initRouter = (pathname: string, search: Record) => } } -if (!isServer) { - const { pathname, search } = window.location - const searchParams = Object.fromEntries(new URLSearchParams(search)) - initRouter(pathname, searchParams) -} - export const useRouter = = Record>() => { const page = useStore(routerStore) const searchParams = useStore(searchParamsStore) as unknown as Accessor