oxen-website/pages/search.tsx

205 lines
5.9 KiB
TypeScript

import { useRouter } from 'next/router';
import React, { useContext, useEffect, useState } from 'react';
import ReactPaginate from 'react-paginate';
// import SearchBackdropDesktopSVG from '../assets/svgs/page/search_desktop.svg';
// import SearchBackdropMobileSVG from '../assets/svgs/page/search_mobile.svg';
import client from '../client';
import { ArticleCardRow } from '../components/cards/ArticleCardRow';
import { Contained } from '../components/Contained';
import { SectionTitle } from '../components/SectionTitle';
import { Title } from '../components/Title';
import { METADATA, SEARCH } from '../constants';
import { ScreenContext } from '../contexts/screen';
import { sanityPostQuery } from '../hooks/search';
import { ISanityArticle } from '../types/article';
import { buildArticleInfo } from '../utils/article';
import { getTopPosts } from '../utils/posts';
interface Props {
sanityQuery: string;
posts: ISanityArticle[];
totalCount: number;
currentPage: number;
}
interface ISanityPageResults {
posts: ISanityArticle[];
count: number;
}
function Search(props: Props) {
const posts = props.posts.map(p => buildArticleInfo(p));
const { isMobile } = useContext(ScreenContext);
const router = useRouter();
const [isLoading, setLoading] = useState(false); //State for loading indicator
const startLoading = () => setLoading(true);
const stopLoading = () => setLoading(false);
const [topPosts, setTopPosts] = useState([] as ISanityArticle[]);
useEffect(() => {
const getPosts = async () => {
const posts = await getTopPosts(4);
setTopPosts(posts);
};
getPosts();
}, []);
// Since requests happens after chaning routes url ?page={n} we need to bind loading events
// on the router change event.
useEffect(() => {
//Setting router event handlers after component is located
router.events.on('routeChangeStart', startLoading);
router.events.on('routeChangeComplete', stopLoading);
return () => {
router.events.off('routeChangeStart', startLoading);
router.events.off('routeChangeComplete', stopLoading);
};
}, []);
const paginationHandler = page => {
const currentPath = router.pathname;
//Copy current query to avoid its removing
const currentQuery = { ...router.query };
currentQuery.page = page.selected + 1;
router.push({
pathname: currentPath,
query: currentQuery,
});
};
//
//
//
//
// USING QUERY ?s=
// OR USING CITY ?city=
// OR USING CATEGORY` ?category=
//
//
//
//
const pageCount = Math.ceil(props.totalCount / SEARCH.SEARCH_ITEMS_PER_PAGE);
const showPagination = posts.length > 0 && pageCount > 1;
console.log('search ➡️ pageCount:', pageCount);
console.log('search ➡️ props.totalCount:', props.totalCount);
return (
<div>
<title>{METADATA.TITLE_SUFFIX}</title>
<div className="relative w-full mt-6 mb-12">
{isMobile ? (
<>
{/* <SearchBackdropMobileSVG
style={{
width: '150%',
transform: 'translateX(-18%)',
}}
/> */}
<div className="absolute inset-0 flex justify-center items-center">
<Title level={1} className="font-roboto text-primary">
{posts.length > 0 ? 'Search Results' : 'Nothing Found'}
</Title>
</div>
</>
) : (
<Contained>
{/* <SearchBackdropDesktopSVG className="w-full" /> */}
<div className="absolute inset-0 flex justify-center items-center">
<Title level={1} className="font-roboto text-primary">
{posts.length > 0 ? 'Search Results' : 'Nothing Found'}
</Title>
</div>
</Contained>
)}
</div>
<Contained>
<div className="flex flex-col space-y-8">
{posts.map(post => (
<ArticleCardRow key={post.slug} {...post} />
))}
</div>
{showPagination && (
<div className="mobile:mt-12 mt-8">
<ReactPaginate
previousLabel={'<'}
nextLabel={'>'}
breakLabel={'...'}
breakClassName={'break-me'}
activeClassName={'active'}
containerClassName={'pagination'}
subContainerClassName={''}
initialPage={props.currentPage - 1}
pageCount={pageCount}
marginPagesDisplayed={2}
pageRangeDisplayed={5}
onPageChange={paginationHandler}
/>
</div>
)}
</Contained>
<div className="mt-12 mb-12">
<Contained>
<SectionTitle>Didn't find what you were looking for?</SectionTitle>
</Contained>
</div>
</div>
);
}
Search.getInitialProps = async ({ query }): Promise<Props> => {
const page = query.page ?? 1;
const { s: encodedSearchQuery } = query;
const searchQuery = decodeURI(encodedSearchQuery);
let posts: ISanityArticle[] = [];
let totalCount = 0;
const resultsStart = SEARCH.SEARCH_ITEMS_PER_PAGE * (page - 1);
const resultsEnd = resultsStart + SEARCH.SEARCH_ITEMS_PER_PAGE;
const specifier = `*[_type == "post" && (title match "*${searchQuery}*" || description match "${searchQuery}*" || "${searchQuery}*" || category match "${searchQuery}*")]`;
const sanityQuery = `
*[][0]{
"posts": ${specifier}[${resultsStart}..${resultsEnd}]{
${sanityPostQuery}
},
"count": count(${specifier})
}
`;
if (searchQuery) {
try {
const results: ISanityPageResults = await client.fetch(sanityQuery);
if (results?.posts?.length) {
posts = results.posts;
totalCount = results.count;
}
} catch (error) {
console.warn('Error: ', error);
}
}
return {
posts,
sanityQuery,
totalCount,
currentPage: page,
};
};
export default Search;