From b71b580331ffbb6a9480ec626be002d91fd021b7 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Fri, 19 Apr 2024 19:21:54 +0800 Subject: [PATCH] Optimize content fetching and update configurations Content fetching in the marketing section has been refactored to utilize the Next.js cache, which significantly improves performance. The date format of publishedAt has been updated to be more consistent across files. Code related to CSRF token, fonts, and metadata has been refactored into separate files for easier maintenance and readability. --- apps/web/app/(marketing)/blog/[slug]/page.tsx | 2 +- .../blog/_components/post-header.tsx | 2 +- .../blog/_components/post-preview.tsx | 2 +- apps/web/app/(marketing)/blog/page.tsx | 27 ++++++--- .../app/(marketing)/docs/[...slug]/page.tsx | 3 +- apps/web/app/(marketing)/docs/page.tsx | 12 +++- apps/web/app/layout.tsx | 57 +++---------------- apps/web/components/csrf-token-meta.tsx | 12 ++++ apps/web/lib/fonts.ts | 30 ++++++++++ apps/web/lib/root-metdata.ts | 32 +++++++++++ packages/cms/core/src/cms-client.ts | 2 +- packages/cms/keystatic/src/client.ts | 4 +- 12 files changed, 116 insertions(+), 69 deletions(-) create mode 100644 apps/web/components/csrf-token-meta.tsx create mode 100644 apps/web/lib/fonts.ts create mode 100644 apps/web/lib/root-metdata.ts diff --git a/apps/web/app/(marketing)/blog/[slug]/page.tsx b/apps/web/app/(marketing)/blog/[slug]/page.tsx index 804f9b415..215f9e3e6 100644 --- a/apps/web/app/(marketing)/blog/[slug]/page.tsx +++ b/apps/web/app/(marketing)/blog/[slug]/page.tsx @@ -36,7 +36,7 @@ export async function generateMetadata({ title, description, type: 'article', - publishedTime: publishedAt?.toDateString(), + publishedTime: publishedAt, url: post.url, images: image ? [ diff --git a/apps/web/app/(marketing)/blog/_components/post-header.tsx b/apps/web/app/(marketing)/blog/_components/post-header.tsx index 7fbff4201..e9222228e 100644 --- a/apps/web/app/(marketing)/blog/_components/post-header.tsx +++ b/apps/web/app/(marketing)/blog/_components/post-header.tsx @@ -20,7 +20,7 @@ export const PostHeader: React.FC<{
- +
diff --git a/apps/web/app/(marketing)/blog/_components/post-preview.tsx b/apps/web/app/(marketing)/blog/_components/post-preview.tsx index 5ea463565..2641b4cf6 100644 --- a/apps/web/app/(marketing)/blog/_components/post-preview.tsx +++ b/apps/web/app/(marketing)/blog/_components/post-preview.tsx @@ -50,7 +50,7 @@ export function PostPreview({
- +
diff --git a/apps/web/app/(marketing)/blog/page.tsx b/apps/web/app/(marketing)/blog/page.tsx index 86df0eb59..18926d722 100644 --- a/apps/web/app/(marketing)/blog/page.tsx +++ b/apps/web/app/(marketing)/blog/page.tsx @@ -1,3 +1,5 @@ +import { unstable_cache as cache } from 'next/dist/server/web/spec-extension/unstable-cache'; + import { createCmsClient } from '@kit/cms'; import { If } from '@kit/ui/if'; import { Trans } from '@kit/ui/trans'; @@ -18,22 +20,33 @@ export const generateMetadata = async () => { }; }; +const getContentItems = cache( + async (language: string | undefined, limit: number, offset: number) => { + const client = await createCmsClient(); + + return client.getContentItems({ + collection: 'posts', + limit, + offset, + language, + sortBy: 'publishedAt', + sortDirection: 'desc', + }); + }, +); + async function BlogPage({ searchParams }: { searchParams: { page: string } }) { const { t, resolvedLanguage: language } = await createI18nServerInstance(); - const cms = await createCmsClient(); const page = searchParams.page ? parseInt(searchParams.page) : 0; const limit = 10; const offset = page * limit; - const { items: posts, total } = await cms.getContentItems({ - collection: 'posts', + const { total, items: posts } = await getContentItems( + language, limit, offset, - language, - sortBy: 'publishedAt', - sortDirection: 'desc', - }); + ); return ( <> diff --git a/apps/web/app/(marketing)/docs/[...slug]/page.tsx b/apps/web/app/(marketing)/docs/[...slug]/page.tsx index 7b183c74e..b9b5600eb 100644 --- a/apps/web/app/(marketing)/docs/[...slug]/page.tsx +++ b/apps/web/app/(marketing)/docs/[...slug]/page.tsx @@ -1,5 +1,4 @@ -import { cache } from 'react'; - +import { unstable_cache as cache } from 'next/cache'; import { notFound } from 'next/navigation'; import { ContentRenderer, createCmsClient } from '@kit/cms'; diff --git a/apps/web/app/(marketing)/docs/page.tsx b/apps/web/app/(marketing)/docs/page.tsx index 902091dcd..6d571bb85 100644 --- a/apps/web/app/(marketing)/docs/page.tsx +++ b/apps/web/app/(marketing)/docs/page.tsx @@ -1,3 +1,5 @@ +import { unstable_cache as cache } from 'next/cache'; + import { createCmsClient } from '@kit/cms'; import { PageBody } from '@kit/ui/page'; @@ -14,14 +16,18 @@ export const generateMetadata = async () => { }; }; -async function DocsPage() { +const getContentItems = cache(async (resolvedLanguage: string | undefined) => { const client = await createCmsClient(); - const { t, resolvedLanguage } = await createI18nServerInstance(); - const { items } = await client.getContentItems({ + return client.getContentItems({ collection: 'documentation', language: resolvedLanguage, }); +}); + +async function DocsPage() { + const { t, resolvedLanguage } = await createI18nServerInstance(); + const { items } = await getContentItems(resolvedLanguage); // Filter out any docs that have a parentId, as these are children of other docs const cards = items.filter((item) => !item.parentId); diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 2cd75f400..789c26526 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,32 +1,17 @@ -import { Urbanist as HeadingFont, Inter as SansFont } from 'next/font/google'; import Head from 'next/head'; -import { cookies, headers } from 'next/headers'; +import { cookies } from 'next/headers'; import { Toaster } from '@kit/ui/sonner'; import { cn } from '@kit/ui/utils'; +import { CsrfTokenMeta } from '~/components/csrf-token-meta'; import { RootProviders } from '~/components/root-providers'; -import appConfig from '~/config/app.config'; +import { heading, sans } from '~/lib/fonts'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; +import { rootMetadata } from '~/lib/root-metdata'; import '../styles/globals.css'; -const sans = SansFont({ - subsets: ['latin'], - variable: '--font-sans', - fallback: ['system-ui', 'Helvetica Neue', 'Helvetica', 'Arial'], - preload: true, - weight: ['300', '400', '500', '600', '700'], -}); - -const heading = HeadingFont({ - subsets: ['latin'], - variable: '--font-heading', - fallback: ['system-ui', 'Helvetica Neue', 'Helvetica', 'Arial'], - preload: true, - weight: ['500', '700'], -}); - export default async function RootLayout({ children, }: { @@ -34,9 +19,10 @@ export default async function RootLayout({ }) { const { language } = await createI18nServerInstance(); const theme = getTheme(); + const className = getClassName(theme); return ( - + @@ -71,33 +57,4 @@ function getTheme() { return cookies().get('theme')?.value; } -export const metadata = { - title: appConfig.name, - description: appConfig.description, - metadataBase: new URL(appConfig.url), - openGraph: { - url: appConfig.url, - siteName: appConfig.name, - description: appConfig.description, - }, - twitter: { - card: 'summary_large_image', - title: appConfig.title, - description: appConfig.description, - }, - icons: { - icon: '/images/favicon/favicon.ico', - shortcut: '/shortcut-icon.png', - apple: '/images/favicon/apple-touch-icon.png', - other: { - rel: 'apple-touch-icon-precomposed', - url: '/apple-touch-icon-precomposed.png', - }, - }, -}; - -function CsrfTokenMeta() { - const csrf = headers().get('x-csrf-token') ?? ''; - - return ; -} +export const metadata = rootMetadata; diff --git a/apps/web/components/csrf-token-meta.tsx b/apps/web/components/csrf-token-meta.tsx new file mode 100644 index 000000000..5e40cd37d --- /dev/null +++ b/apps/web/components/csrf-token-meta.tsx @@ -0,0 +1,12 @@ +import { headers } from 'next/headers'; + +/** + * @description This component is used to render the CSRF token as a meta tag. + * this tag can be retrieved for use in forms that require CSRF protection. + * @constructor + */ +export function CsrfTokenMeta() { + const csrf = headers().get('x-csrf-token') ?? ''; + + return ; +} diff --git a/apps/web/lib/fonts.ts b/apps/web/lib/fonts.ts new file mode 100644 index 000000000..85572e07a --- /dev/null +++ b/apps/web/lib/fonts.ts @@ -0,0 +1,30 @@ +import { Urbanist as HeadingFont, Inter as SansFont } from 'next/font/google'; + +/** + * @sans + * @description Define here the sans font. + * By default, it uses the Inter font from Google Fonts. + */ +const sans = SansFont({ + subsets: ['latin'], + variable: '--font-sans', + fallback: ['system-ui', 'Helvetica Neue', 'Helvetica', 'Arial'], + preload: true, + weight: ['300', '400', '500', '600', '700'], +}); + +/** + * @heading + * @description Define here the heading font. + * By default, it uses the Urbanist font from Google Fonts. + */ +const heading = HeadingFont({ + subsets: ['latin'], + variable: '--font-heading', + fallback: ['system-ui', 'Helvetica Neue', 'Helvetica', 'Arial'], + preload: true, + weight: ['500', '700'], +}); + +// we export these fonts into the root layout +export { sans, heading }; diff --git a/apps/web/lib/root-metdata.ts b/apps/web/lib/root-metdata.ts new file mode 100644 index 000000000..d8aea6fcc --- /dev/null +++ b/apps/web/lib/root-metdata.ts @@ -0,0 +1,32 @@ +import { Metadata } from 'next'; + +import appConfig from '~/config/app.config'; + +/** + * @name rootMetadata + * @description Define the root metadata for the application. + */ +export const rootMetadata: Metadata = { + title: appConfig.name, + description: appConfig.description, + metadataBase: new URL(appConfig.url), + openGraph: { + url: appConfig.url, + siteName: appConfig.name, + description: appConfig.description, + }, + twitter: { + card: 'summary_large_image', + title: appConfig.title, + description: appConfig.description, + }, + icons: { + icon: '/images/favicon/favicon.ico', + shortcut: '/shortcut-icon.png', + apple: '/images/favicon/apple-touch-icon.png', + other: { + rel: 'apple-touch-icon-precomposed', + url: '/apple-touch-icon-precomposed.png', + }, + }, +}; diff --git a/packages/cms/core/src/cms-client.ts b/packages/cms/core/src/cms-client.ts index 7d15732b2..aa8d886d8 100644 --- a/packages/cms/core/src/cms-client.ts +++ b/packages/cms/core/src/cms-client.ts @@ -6,7 +6,7 @@ export namespace Cms { url: string; description: string | undefined; content: unknown; - publishedAt: Date; + publishedAt: string; image: string | undefined; slug: string; categories: Category[]; diff --git a/packages/cms/keystatic/src/client.ts b/packages/cms/keystatic/src/client.ts index d5b816541..c8555cb6b 100644 --- a/packages/cms/keystatic/src/client.ts +++ b/packages/cms/keystatic/src/client.ts @@ -16,8 +16,6 @@ export class KeystaticClient implements CmsClient { const docs = await reader.collections[collection].all(); - console.log(docs); - const startOffset = options?.offset ?? 0; const endOffset = startOffset + (options?.limit ?? 10); @@ -144,7 +142,7 @@ export class KeystaticClient implements CmsClient { url: item.slug, slug: item.slug, description: item.entry.description, - publishedAt, + publishedAt: publishedAt.toISOString(), content, image: item.entry.image ?? undefined, categories: