From 4d080cf26a7d79c9b9fcd8e3b7195ce40a808029 Mon Sep 17 00:00:00 2001 From: aV <62926341+a0v0@users.noreply.github.com> Date: Sun, 12 May 2024 15:10:49 +0530 Subject: [PATCH 1/2] feat: added nextjs metada --- app/layout.tsx | 30 +++++++++++++++++++++++++++++- app/tools/layout.tsx | 4 ++-- config/site.ts | 20 +++++++++++++++++--- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/app/layout.tsx b/app/layout.tsx index d7d2e0d0..e87a0ef6 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -4,17 +4,45 @@ import {Footer} from "@/components/footer"; import {Navbar} from "@/components/Navbar"; import {fontSans} from "@/config/fonts"; import {routes as manifest} from "@/config/routes"; +import {siteConfig} from "@/config/site"; import "@/styles/globals.css"; import "@/styles/sandpack.css"; import {__PROD__} from "@/utils"; import {clsx} from "@nextui-org/shared-utils"; import {Analytics} from "@vercel/analytics/react"; +import {Metadata} from "next"; import {Providers} from "./providers"; +const metadata: Metadata = { + title: { + default: siteConfig.name, + template: `%s • ${siteConfig.tagline}`, + }, + description: siteConfig.description, + keywords: siteConfig.keywords, + themeColor: [ + {media: "(prefers-color-scheme: light)", color: "white"}, + {media: "(prefers-color-scheme: dark)", color: "black"}, + ], + icons: { + icon: "/favicon.ico", + }, + manifest: "/manifest.json", + openGraph: siteConfig.openGraph, + alternates: { + canonical: "https://avtoolz.com", + types: { + "application/rss+xml": [{url: "https://avtoolz.com/feed.xml", title: "aVToolz RSS Feed"}], + }, + }, + viewport: + "viewport-fit=cover, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0", +}; + export default function RootLayout({children}: {children: React.ReactNode}) { return ( - + {/* */} diff --git a/app/tools/layout.tsx b/app/tools/layout.tsx index 30af778c..7c62e89e 100644 --- a/app/tools/layout.tsx +++ b/app/tools/layout.tsx @@ -4,11 +4,11 @@ import BuildCircleRoundedIcon from "@mui/icons-material/BuildCircleRounded"; import GridViewRoundedIcon from "@mui/icons-material/GridViewRounded"; import HomeRoundedIcon from "@mui/icons-material/HomeRounded"; import {BreadcrumbItem, Breadcrumbs} from "@nextui-org/react"; -import {usePathname, useRouter} from "next/navigation"; +import {usePathname} from "next/navigation"; + export default function ToolLayout({children}: {children: React.ReactNode}) { const path = usePathname(); const tool = getToolByHref(path); - const router = useRouter(); return (
diff --git a/config/site.ts b/config/site.ts index 1b371379..6111e295 100644 --- a/config/site.ts +++ b/config/site.ts @@ -1,13 +1,26 @@ export type SiteConfig = typeof siteConfig; export const siteConfig = { - name: "aVToolz • Fast, beautiful and modern tools for everyone.", + name: "aVToolz", + tagline: "Fast, beautiful and modern tools for everyone.", description: "Fast, beautiful and modern tools for everyone.", ogImage: "https://avtoolz.com/twitter-cards/avtoolz.jpeg", - author: "Anubhav Mahur", - email: "", siteUrl: "https://avtoolz.com", creator: "@a0v0", + keywords: [ + "aVToolz", + "pdf-converter", + "image-converter", + "image-editor", + "pdf-editor", + "text-tools", + "audio-tools", + "video-tools", + "web-tools", + "developer-tools", + "ai-tools", + "AI", + ], openGraph: { type: "website", locale: "en_US", @@ -16,6 +29,7 @@ export const siteConfig = { description: "Fast, beautiful and modern tools for everyone.", images: [ { + // TODO: add og image url: "https://avtoolz.com/twitter-cards/avtoolz.jpeg", width: 1200, height: 630, From 61c53489559edb2abbed9f1612d43e55d77720b8 Mon Sep 17 00:00:00 2001 From: aV <62926341+a0v0@users.noreply.github.com> Date: Sun, 12 May 2024 18:21:53 +0530 Subject: [PATCH 2/2] fix: fixed incorrect title and desc also conveted header link to native NextUI links --- app/layout.tsx | 63 ++++++++++++++++++++++++--------------- app/tools/layout.tsx | 32 ++++---------------- app/tools/page.tsx | 28 +++++++++-------- components/Navbar.tsx | 8 ++++- components/breadcrumb.tsx | 31 +++++++++++++++++++ utils/links.ts | 18 +++++++++++ 6 files changed, 115 insertions(+), 65 deletions(-) create mode 100644 components/breadcrumb.tsx diff --git a/app/layout.tsx b/app/layout.tsx index e87a0ef6..0ad04339 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -5,39 +5,54 @@ import {Navbar} from "@/components/Navbar"; import {fontSans} from "@/config/fonts"; import {routes as manifest} from "@/config/routes"; import {siteConfig} from "@/config/site"; +import {getToolByHref} from "@/config/tools"; import "@/styles/globals.css"; import "@/styles/sandpack.css"; import {__PROD__} from "@/utils"; +import {getPathnameFromMetadataState} from "@/utils/links"; import {clsx} from "@nextui-org/shared-utils"; import {Analytics} from "@vercel/analytics/react"; import {Metadata} from "next"; import {Providers} from "./providers"; -const metadata: Metadata = { - title: { - default: siteConfig.name, - template: `%s • ${siteConfig.tagline}`, - }, - description: siteConfig.description, - keywords: siteConfig.keywords, - themeColor: [ - {media: "(prefers-color-scheme: light)", color: "white"}, - {media: "(prefers-color-scheme: dark)", color: "black"}, - ], - icons: { - icon: "/favicon.ico", - }, - manifest: "/manifest.json", - openGraph: siteConfig.openGraph, - alternates: { - canonical: "https://avtoolz.com", - types: { - "application/rss+xml": [{url: "https://avtoolz.com/feed.xml", title: "aVToolz RSS Feed"}], +export async function generateMetadata(_: any, state: any): Promise { + // TODO: migrate to a better solution once nextjs allows reading pathname in generateMetadata + const pathname = getPathnameFromMetadataState(state); + const tool = getToolByHref(pathname ?? ""); + var title = `${siteConfig.name} • ${siteConfig.tagline}`; + var description = siteConfig.description; + + if (tool) { + title = `${tool.title} • ${siteConfig.name}`; + description = tool.description; + } else if (pathname === "/tools") { + title = `Tools • ${siteConfig.name}`; + description = "All the available tools in aVToolz."; + } + + return { + title: title, + description: description, + keywords: siteConfig.keywords, + themeColor: [ + {media: "(prefers-color-scheme: light)", color: "white"}, + {media: "(prefers-color-scheme: dark)", color: "black"}, + ], + icons: { + icon: "/favicon.ico", }, - }, - viewport: - "viewport-fit=cover, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0", -}; + manifest: "/manifest.json", + openGraph: siteConfig.openGraph, + alternates: { + canonical: "https://avtoolz.com", + types: { + "application/rss+xml": [{url: "https://avtoolz.com/feed.xml", title: "aVToolz RSS Feed"}], + }, + }, + viewport: + "viewport-fit=cover, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0", + }; +} export default function RootLayout({children}: {children: React.ReactNode}) { return ( diff --git a/app/tools/layout.tsx b/app/tools/layout.tsx index 7c62e89e..b9138eab 100644 --- a/app/tools/layout.tsx +++ b/app/tools/layout.tsx @@ -1,32 +1,10 @@ -"use client"; -import {getToolByHref} from "@/config/tools"; -import BuildCircleRoundedIcon from "@mui/icons-material/BuildCircleRounded"; -import GridViewRoundedIcon from "@mui/icons-material/GridViewRounded"; -import HomeRoundedIcon from "@mui/icons-material/HomeRounded"; -import {BreadcrumbItem, Breadcrumbs} from "@nextui-org/react"; -import {usePathname} from "next/navigation"; +import Breadcrumb from "@/components/breadcrumb"; export default function ToolLayout({children}: {children: React.ReactNode}) { - const path = usePathname(); - const tool = getToolByHref(path); - return ( -
- - }> - Home - - - }> - Tools - - {path.includes("/tools/") ? ( - }> - {tool?.title} - - ) : null} - - {children} -
+ <> + +
{children}
+ ); } diff --git a/app/tools/page.tsx b/app/tools/page.tsx index ad3510b3..ef702c6a 100644 --- a/app/tools/page.tsx +++ b/app/tools/page.tsx @@ -4,19 +4,21 @@ import {Tools} from "@/config/tools"; function page() { return ( // TODO: add a mini navbar to search, sort and filter tools -
-
- -
-
+ <> +
+
+ +
+
+ ); } diff --git a/components/Navbar.tsx b/components/Navbar.tsx index 3334987a..d3f023f9 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -146,7 +146,13 @@ export const Navbar: FC = ({routes, slug, tag}) => { }} > {category.routes.map((tool, index) => ( - + {tool.title} ))} diff --git a/components/breadcrumb.tsx b/components/breadcrumb.tsx new file mode 100644 index 00000000..f081f72f --- /dev/null +++ b/components/breadcrumb.tsx @@ -0,0 +1,31 @@ +"use client"; +import {getToolByHref} from "@/config/tools"; +import BuildCircleRoundedIcon from "@mui/icons-material/BuildCircleRounded"; +import GridViewRoundedIcon from "@mui/icons-material/GridViewRounded"; +import HomeRoundedIcon from "@mui/icons-material/HomeRounded"; +import {BreadcrumbItem, Breadcrumbs} from "@nextui-org/react"; +import {usePathname} from "next/navigation"; + +function Breadcrumb() { + const path = usePathname(); + const tool = getToolByHref(path); + + return ( + + }> + Home + + + }> + Tools + + {path.includes("/tools/") ? ( + }> + {tool?.title} + + ) : null} + + ); +} + +export default Breadcrumb; diff --git a/utils/links.ts b/utils/links.ts index f8c9e242..ee9e3ff7 100644 --- a/utils/links.ts +++ b/utils/links.ts @@ -1,2 +1,20 @@ export const isActive = (pathname: string | undefined | null, href: string) => pathname && pathname.startsWith(href); + +/** + * + * Get the pathname from the metadata state + * This dives into async storage of promise state to get the pathname + * + * This is much more performant that using headers() from next as this doesn't opt out from the cache + * @param state + * + * credit: https://github.com/vercel/next.js/discussions/50189#discussioncomment-9224262 + */ +export const getPathnameFromMetadataState = (state: any): string => { + const res = Object.getOwnPropertySymbols(state || {}) + .map((p) => state[p]) + .find((state) => state?.hasOwnProperty?.("urlPathname")); + + return res?.urlPathname.replace(/\?.+/, "") ?? ""; +};