Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(blog): add search feature #61

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/(routes)/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ReactNode } from "react";
import { twMerge } from "tailwind-merge";
import Divider from "./_components/divider";
import ArticleContent from "./_components/article-content";
import BackgroundBanner from "../background-banner";
import BackgroundBanner from "../_components/background-banner";
import Breadcrumb from "./_components/breadcrumb";
import { MetaInfo } from "./_components/meta-info";
import BackToTopButton from "../_components/back-to-top-button";
Expand Down
1 change: 0 additions & 1 deletion src/app/(routes)/blog/_components/article-snippet-card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { BlogPostType } from "@/app/_lib/contentful";
import { Text } from "@/app/_components/text";
import { documentToPlainTextString } from "@contentful/rich-text-plain-text-renderer";
import Link from "next/link";
import ContentfulImage from "../[slug]/_components/contentful-image";
import { MetaInfo } from "../[slug]/_components/meta-info";
Expand Down
62 changes: 62 additions & 0 deletions src/app/(routes)/blog/_components/posts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Text } from "@/app/_components/text";
import ArticleSnippetCard from "./article-snippet-card";
import ArticleFullCard from "./article-full-card";
import {
BlogPostWithRelevantEntries,
retrieveContentfulPublishedSlugs,
} from "@/app/_lib/contentful";
import { SearchParams } from "../page";

export async function Posts({
getStartedSnippets,
recentArticleSlugs,
searchParams,
}: {
searchParams: SearchParams;
recentArticleSlugs: string[];
getStartedSnippets: (BlogPostWithRelevantEntries | undefined)[];
}) {
const search = searchParams["search"];
if (!search) {
return (
<>
<div className="flex w-full flex-col gap-4">
<Text variant="body" className="text-grey-400">
Get started with Across
</Text>
<div className="w-full overflow-x-scroll scrollbar-hide">
<div className="grid w-[1024px] grid-cols-3 gap-5 md:w-full">
{getStartedSnippets.slice(0, 3).map((snippet) => (
<div className="w-full" key={snippet?.fields.slug}>
<ArticleSnippetCard article={snippet!} />
</div>
))}
</div>
</div>
</div>
<div className="flex w-full flex-col gap-4">
<Text variant="body" className="text-grey-400">
Most recent articles
</Text>
{recentArticleSlugs.map((slug) => (
<ArticleFullCard key={slug} slug={slug} />
))}
</div>
</>
);
} else {
const searchedSlugs = await retrieveContentfulPublishedSlugs({
query: search,
});
return (
<div className="flex w-full flex-col gap-4">
<Text variant="body" className="text-grey-400">
Search results
</Text>
{searchedSlugs.map((slug) => (
<ArticleFullCard key={slug} slug={slug} />
))}
</div>
);
}
}
58 changes: 28 additions & 30 deletions src/app/(routes)/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { Text } from "@/app/_components/text";
import BackToTopButton from "./_components/back-to-top-button";
import BackgroundBanner from "./background-banner";
import BackgroundBanner from "./_components/background-banner";
import Filter from "./_components/filter";

import { Suspense } from "react";
import {
retrieveContentfulEntry,
retrieveContentfulPublishedSlugs,
retrieveContentfulEntry,
} from "@/app/_lib/contentful";
import ArticleFullCard from "./_components/article-full-card";
import ArticleSnippetCard from "./_components/article-snippet-card";
import { Suspense } from "react";
import { Posts } from "./_components/posts";
import { createCacheKey } from "@/app/_lib/cache";

export default async function BlogHomePage() {
export type SearchParams = Record<string, string | undefined>;

type PageProps = {
searchParams: SearchParams;
};

export default async function BlogHomePage({ searchParams }: PageProps) {
const recentArticleSlugs = await retrieveContentfulPublishedSlugs({
limit: 6,
avoidTags: ["get-started"],
Expand All @@ -23,39 +30,30 @@ export default async function BlogHomePage() {
getStartedSlugs.map((s) => retrieveContentfulEntry(s)),
);

const key = createCacheKey({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add this key to Suspense

searchParams,
});

return (
<>
<BackgroundBanner />
<main className="relative z-[1] mx-auto flex w-full max-w-4xl flex-col items-center gap-6 px-4 py-10 lg:gap-8 lg:px-0">
<Text variant="heading-2" className="-mb-6 lg:-mb-8">
Across Protocol
</Text>
<Suspense>
<Suspense
fallback={
<h2 className="text-text-secondary text-2xl my-auto flex-1">Searching...</h2>
}
>
<Filter />
<Posts
getStartedSnippets={getStartedSnippets}
recentArticleSlugs={recentArticleSlugs}
searchParams={searchParams}
/>
<BackToTopButton />
</Suspense>
<div className="flex w-full flex-col gap-4">
<Text variant="body" className="text-grey-400">
Get started with Across
</Text>
<div className="w-full overflow-x-scroll scrollbar-hide">
<div className="grid w-[1024px] grid-cols-3 gap-5 md:w-full">
{getStartedSnippets.slice(0, 3).map((snippet) => (
<div className="w-full" key={snippet?.fields.slug}>
<ArticleSnippetCard article={snippet!} />
</div>
))}
</div>
</div>
</div>
<div className="flex w-full flex-col gap-4">
<Text variant="body" className="text-grey-400">
Most recent articles
</Text>
{recentArticleSlugs.map((slug) => (
<ArticleFullCard key={slug} slug={slug} />
))}
</div>
<BackToTopButton />
</main>
</>
);
Expand Down
13 changes: 13 additions & 0 deletions src/app/_lib/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { SearchParams } from "../(routes)/blog/page";

export function createCacheKey(options: { searchParams: SearchParams }) {
const { searchParams } = options;
const newParamString = new URLSearchParams();
Object.entries(searchParams).forEach(([key, value]) => {
if (typeof value === "string") {
newParamString.set(key, value);
}
});

return newParamString.toString();
}
6 changes: 5 additions & 1 deletion src/app/_lib/contentful.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export type BlogPostType = Entry<
string
>;

export type BlogPostWithRelevantEntries = BlogPostType & {
relevantEntries: BlogPostType[];
};

function getProductionClient() {
return createClient({
space: CONTENTFUL_SPACE_ID ?? "",
Expand Down Expand Up @@ -72,7 +76,7 @@ export async function retrieveContentfulPublishedSlugs({
export async function retrieveContentfulEntry(
entrySlugId: string,
relevantEntryCount = 4,
) {
): Promise<BlogPostWithRelevantEntries | undefined> {
const client = getProductionClient();
const options = {
content_type: contentType,
Expand Down
Loading