[website] add blog page filter tags (#424)

* add tag images

* add visibility filter

* add no-scrollbar tailwind utility

* add filter tags

* use tag filters

* rm cosole.log
This commit is contained in:
Felicio Mununga 2023-06-26 18:55:16 +01:00 committed by GitHub
parent 9479b4bb2f
commit ceb1f60605
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 176 additions and 23 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -19,8 +19,9 @@ export const getPosts = async (params: Params = {}) => {
order: 'published_at DESC', order: 'published_at DESC',
limit, limit,
page, page,
...(tag && { filter: `tag:${tag}` }), ...(tag
filter: 'visibility:public', ? { filter: `tag:${tag}+visibility:public` }
: { filter: 'visibility:public' }),
}) })
return { posts: [...response], meta: response.meta } return { posts: [...response], meta: response.meta }

View File

@ -2,34 +2,135 @@ import { useMemo } from 'react'
import { Avatar, Button, Shadow, Tag, Text } from '@status-im/components' import { Avatar, Button, Shadow, Tag, Text } from '@status-im/components'
import { useInfiniteQuery } from '@tanstack/react-query' import { useInfiniteQuery } from '@tanstack/react-query'
import Image from 'next/image'
import { useRouter } from 'next/router'
// import Image from 'next/image'
import { formatDate } from '@/components/chart/utils/format-time' import { formatDate } from '@/components/chart/utils/format-time'
import { Link } from '@/components/link' import { Link } from '@/components/link'
import { AppLayout } from '@/layouts/app-layout' import { AppLayout } from '@/layouts/app-layout'
import { getPosts } from '@/lib/ghost' import { getPosts } from '@/lib/ghost'
import type { PostOrPage, PostsOrPages } from '@tryghost/content-api' import type { PostOrPage, PostsOrPages } from '@tryghost/content-api'
import type { GetStaticProps, InferGetStaticPropsType, Page } from 'next' import type {
GetServerSideProps,
InferGetServerSidePropsType,
Page,
} from 'next'
export const getStaticProps: GetStaticProps<{ const FILTER_TAGS = [
{
id: '63b48c62fc2070000104be8c',
slug: 'news-and-announcements',
name: 'News & Announcements',
icon: (
<Image
src="/images/tags/news-and-announcements-20x20.png"
alt="Latest news on Products built by Status Network"
width={20}
height={20}
unoptimized
/>
),
},
{
id: '63b48c62fc2070000104bea2',
slug: 'product',
name: 'Product',
icon: (
<Image
src="/images/tags/product-20x20.png"
alt="Latest news on Products built by Status Network"
width={20}
height={20}
unoptimized
/>
),
},
{
id: '63b48c62fc2070000104be61',
slug: 'developers',
name: 'Developers',
icon: (
<Image
src="/images/tags/developers-20x20.png"
alt="Latest news on Products built by Status Network"
width={20}
height={20}
unoptimized
/>
),
},
{
id: '63b48c62fc2070000104bea4',
slug: 'privacy-security',
name: 'Privacy & Security',
icon: (
<Image
src="/images/tags/privacy-security-20x20.png"
alt="Latest news on Products built by Status Network"
width={20}
height={20}
unoptimized
/>
),
},
{
id: '63b48c62fc2070000104be60',
slug: 'dapps',
name: 'Dapps',
icon: (
<Image
src="/images/tags/dapps-20x20.png"
alt="Latest news on Products built by Status Network"
width={20}
height={20}
unoptimized
/>
),
},
{
id: '63b48c62fc2070000104be64',
slug: 'community',
name: 'Community',
icon: (
<Image
src="/images/tags/community-20x20.png"
alt="Latest news on Products built by Status Network"
width={20}
height={20}
unoptimized
/>
),
},
]
export const getServerSideProps: GetServerSideProps<{
posts: PostOrPage[] posts: PostOrPage[]
meta: PostsOrPages['meta'] meta: PostsOrPages['meta']
}> = async () => { tag?: string
const { posts, meta } = await getPosts() }> = async ({ query }) => {
const tag = query?.tag as string | undefined
const { posts, meta } = await getPosts({ tag })
return { return {
props: { props: {
posts, posts,
meta, meta,
...(tag && { tag }),
}, },
} }
} }
type Props = InferGetStaticPropsType<typeof getStaticProps> type BlogPageProps = InferGetServerSidePropsType<typeof getServerSideProps>
const BlogPage: Page<Props> = props => { const BlogPage: Page<BlogPageProps> = ({
const { posts, meta } = props posts: initialPosts,
meta,
tag: initialTag,
}) => {
const router = useRouter()
const tag = (router.query.tag as string | undefined) ?? initialTag
const { const {
data, data,
@ -41,40 +142,80 @@ const BlogPage: Page<Props> = props => {
// status, // status,
// isFetched, // isFetched,
} = useInfiniteQuery({ } = useInfiniteQuery({
queryKey: ['posts'], refetchOnWindowFocus: false,
queryFn: async ({ pageParam: page }) => await getPosts({ page }), queryKey: ['posts', tag],
queryFn: async ({ pageParam: page, queryKey }) => {
const [, tag] = queryKey
const response = await getPosts({ page, tag })
return response
},
getNextPageParam: ({ meta }) => meta.pagination.next, getNextPageParam: ({ meta }) => meta.pagination.next,
initialData: { pages: [{ posts, meta }], pageParams: [1] }, initialData: {
staleTime: Infinity, pages: [
{
posts: initialPosts,
meta,
},
],
pageParams: [1],
},
}) })
const { highlightedPost, visiblePosts } = useMemo(() => { const { highlightedPost, visiblePosts } = useMemo(() => {
const [highlightedPost, ...posts] = data!.pages.flatMap(page => page.posts) const [highlightedPost, ...restPosts] =
const maxLength = posts.length - (posts.length % 3) // the number of posts should be divisible by 3 data?.pages.flatMap(page => page.posts) ?? []
const visiblePosts = posts.slice(0, maxLength)
const maxLength = restPosts.length - (restPosts.length % 3) // the number of posts should be divisible by 3
const visiblePosts = restPosts.slice(0, maxLength)
return { highlightedPost, visiblePosts } return { highlightedPost, visiblePosts }
}, [data]) }, [data])
// loading/skeleton if not complete
return ( return (
<div className="min-h-[900px] rounded-3xl bg-white-100 lg:mx-1"> <div className="min-h-[900px] rounded-3xl bg-white-100 lg:mx-1">
<div className="px-5"> <div className="overflow-x-hidden px-5">
<div className="mx-auto max-w-[1184px] pb-24 pt-12 lg:pb-32 lg:pt-20"> <div className="mx-auto max-w-[1184px] pb-24 pt-12 lg:pb-32 lg:pt-20">
<div className="mb-10 grid gap-2"> <div className=" grid gap-2">
<h1 className="text-[40px] font-bold leading-[44px] tracking-[-.02em] lg:text-[64px] lg:leading-[68px]"> <h1 className="text-[40px] font-bold leading-[44px] tracking-[-.02em] lg:text-[64px] lg:leading-[68px]">
Blog. Blog.
</h1> </h1>
<Text size={19}>Long form articles, thoughts, and ideas.</Text> <Text size={19}>Long form articles, thoughts, and ideas.</Text>
</div> </div>
<div className="no-scrollbar mr-[-2rem] flex gap-2 overflow-x-scroll pb-12 pr-8 pt-10">
{FILTER_TAGS.map(filterTag => (
<div key={filterTag.id} className="shrink-0">
<Shadow className="rounded-[10px]">
<Link
href={{ query: { ...router.query, tag: filterTag.slug } }}
className="flex h-[32px] items-center gap-2 rounded-[10px] border border-solid border-neutral-10 pl-2 pr-3 data-[active=true]:bg-neutral-10"
data-active={filterTag.slug === tag}
scroll={false}
>
{filterTag.icon}
<Text size={15}>{filterTag.name}</Text>
</Link>
</Shadow>
</div>
))}
</div>
<div> <div>
<div className="mb-[44px] xl:mb-12"> <div className="mb-[44px] xl:mb-12">
<HighlightedPostCard post={highlightedPost} /> {highlightedPost && (
<HighlightedPostCard post={highlightedPost} />
)}
</div> </div>
<div className="grid auto-rows-[1fr] grid-cols-[repeat(auto-fill,minmax(350px,1fr))] gap-5"> <div className="grid auto-rows-[1fr] grid-cols-[repeat(auto-fill,minmax(350px,1fr))] gap-5">
{visiblePosts.map(post => ( {visiblePosts &&
<PostCard key={post.id} post={post} /> visiblePosts.map(post => (
))} <PostCard key={post.id} post={post} />
))}
</div> </div>
</div> </div>

View File

@ -14,6 +14,17 @@
} }
} }
@layer utilities {
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
}
/* /*
1. Use a more-intuitive box-sizing model. 1. Use a more-intuitive box-sizing model.
*/ */