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(/\?.+/, "") ?? "";
+};