Skip to content

Commit

Permalink
feat: add dynamic metadata for search results (#842)
Browse files Browse the repository at this point in the history
<!-- 👋 Hi, thanks for sending a PR to tidelift-me-up-site! 💖.
Please fill out all fields below and make sure each item is true and [x]
checked.
Otherwise we may not be able to review your PR. -->

## PR Checklist

- [x] Addresses an existing open issue: fixes #67 
- [x] That issue was marked as [`status: accepting
prs`](https://github.com/JoshuaKGoldberg/tidelift-me-up-site/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22)
- [x] Steps in
[CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/tidelift-me-up-site/blob/main/.github/CONTRIBUTING.md)
were taken

## Overview

<!-- Description of what is changed and how the code change does that.
-->

This PR introduces dynamic metadata generation for the home/search page.
It's metadata is now dynamically generated based on the `searchParams`
in the URL:

> {username} | Tidelift Me Up
> {username} has {packageCount} npm packages eligible for Tidelift
funding. 💸

This includes when a valid user has 0 packages eligible for funding
based on search parameters. The metadata will look like the following:

> {username} | Tidelift Me Up
> {username} has 0 npm packages eligible for Tidelift funding. 💸

When no `searchParams` are provided, the default metadata is used, which
is defined in `layout.tsx`:

> Tidelift Me Up
> Find your npm packages eligible for Tidelift funding. 💸

Also, something to note is that the API-related logic that was in
`page.tsx` has been extracted into a separate file to improve code
readability and maintainability.
  • Loading branch information
maggienegm authored Jan 20, 2025
1 parent 774563e commit 1dc9e6e
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const raleway = Raleway({ subsets: ["latin"] });

export const metadata = {
description:
"Check if your npm packages are eligible for Tidelift funding. 💸<",
"Check if your npm packages are eligible for Tidelift funding. 💸",
title: "Tidelift Me Up",
};

Expand Down
55 changes: 27 additions & 28 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,44 @@
import {
EstimatedPackage,
PackageOwnership,
tideliftMeUp,
} from "tidelift-me-up";

import { Footer } from "~/components/Footer";
import { MainArea } from "~/components/MainArea";
import { OptionsForm } from "~/components/OptionsForm";
import { ResultDisplay } from "~/components/ResultDisplay";
import { ScrollButton } from "~/components/ScrollButton";
import { fetchData } from "~/utils/fetchData";
import { SearchParams, getOptions } from "~/utils/getOptions";

import { metadata as defaultMetadata } from "./layout";
import styles from "./page.module.css";

export interface HomeProps {
searchParams: Record<string, unknown>;
searchParams: SearchParams;
}

export default async function Home({ searchParams }: HomeProps) {
const options = {
ownership: undefinedIfEmpty(
[
searchParams["author"] === "on" && "author",
searchParams["maintainer"] === "on" && "maintainer",
searchParams["publisher"] === "on" && "publisher",
].filter(Boolean) as PackageOwnership[],
),
since: (searchParams["since"] || undefined) as string | undefined,
username: searchParams.username as string,
};
let result: Error | EstimatedPackage[] | undefined;
export async function generateMetadata({ searchParams }: HomeProps) {
const options = getOptions(searchParams);
const username = options.username;

try {
result = options.username ? await tideliftMeUp(options) : undefined;
} catch (error) {
result = error as Error;
if (!username) {
return defaultMetadata;
}

const result = await fetchData(options);

const description = Array.isArray(result)
? `${username} has ${result.length} npm package${
result.length === 1 ? "" : "s"
} eligible for Tidelift funding. 💸`
: `Could not find packages for ${username}`;

return {
description,
title: `${username} | Tidelift Me Up`,
};
}

export default async function Home({ searchParams }: HomeProps) {
const options = getOptions(searchParams);
const result = await fetchData(options);

return (
<>
<MainArea as="main" className={styles.main}>
Expand All @@ -51,7 +54,3 @@ export default async function Home({ searchParams }: HomeProps) {
</>
);
}

function undefinedIfEmpty<T>(items: T[]) {
return items.length === 0 ? undefined : items;
}
19 changes: 19 additions & 0 deletions src/utils/fetchData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { cache } from "react";
import { EstimatedPackage, tideliftMeUp } from "tidelift-me-up";

export type DataOptions = Record<string, unknown>;
export type DataResults = Error | EstimatedPackage[] | undefined;

export const fetchData = cache(
async (options: DataOptions): Promise<DataResults> => {
let result: DataResults;

try {
result = options.username ? await tideliftMeUp(options) : undefined;
} catch (error) {
result = error as Error;
}

return result;
},
);
21 changes: 21 additions & 0 deletions src/utils/getOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { PackageOwnership } from "tidelift-me-up";

export type SearchParams = Record<string, unknown>;

export function getOptions(searchParams: SearchParams) {
return {
ownership: undefinedIfEmpty(
[
searchParams.author === "on" && "author",
searchParams.maintainer === "on" && "maintainer",
searchParams.publisher === "on" && "publisher",
].filter(Boolean) as PackageOwnership[],
),
since: (searchParams.since || undefined) as string | undefined,
username: (searchParams.username || "") as string,
};
}

function undefinedIfEmpty<T>(items: T[]) {
return items.length === 0 ? undefined : items;
}

0 comments on commit 1dc9e6e

Please sign in to comment.