162 lines
4.3 KiB
TypeScript
162 lines
4.3 KiB
TypeScript
import { LoaderFunctionArgs, useLoaderData } from "react-router";
|
|
import { parseOffset } from "#lib/pagination";
|
|
import { createPostsPageURL } from "#lib/urls";
|
|
import { fetchPosts } from "#api/posts";
|
|
import { PageSkeleton } from "#components/pages";
|
|
import { Paginator } from "#components/pagination";
|
|
import { FooterAd, HeaderAd, SliderAd } from "#components/advs";
|
|
import { CardList, PostCard } from "#components/cards";
|
|
import { FormRouter, FormSection } from "#components/forms";
|
|
import { IPost } from "#entities/posts";
|
|
import { findFavouritePosts, findFavouriteProfiles } from "#entities/account";
|
|
|
|
interface IProps {
|
|
count: number;
|
|
trueCount: number;
|
|
offset?: number;
|
|
posts: IPost[];
|
|
query?: string;
|
|
tags?: string[];
|
|
}
|
|
|
|
export function PostsPage() {
|
|
const { count, trueCount, offset, query, posts, tags } =
|
|
useLoaderData() as IProps;
|
|
const title = "Posts";
|
|
const heading = "Posts";
|
|
|
|
return (
|
|
<PageSkeleton name="posts" title={title} heading={heading}>
|
|
<div className="paginator" id="paginator-top">
|
|
<Paginator
|
|
count={count}
|
|
true_count={trueCount}
|
|
offset={offset}
|
|
constructURL={(offset) =>
|
|
String(createPostsPageURL(offset, query, tags))
|
|
}
|
|
/>
|
|
<FormRouter method="GET">
|
|
<FormSection>
|
|
<input
|
|
id="q"
|
|
className="search-input"
|
|
type="text"
|
|
name="q"
|
|
aria-autocomplete="none"
|
|
defaultValue={query}
|
|
minLength={2}
|
|
placeholder="search for posts..."
|
|
/>
|
|
</FormSection>
|
|
</FormRouter>
|
|
</div>
|
|
|
|
<SliderAd />
|
|
<HeaderAd />
|
|
|
|
<CardList>
|
|
{count === 0 ? (
|
|
<div className="card-list__item--no-results">
|
|
<h2 className="subtitle">Nobody here but us chickens!</h2>
|
|
<p className="subtitle">There are no posts for your query.</p>
|
|
</div>
|
|
) : (
|
|
posts.map((post) => (
|
|
<PostCard
|
|
key={`${post.id}-${post.service}`}
|
|
post={post}
|
|
isFavourite={post.isFavourite}
|
|
isFavouriteProfile={post.isFavouriteProfile}
|
|
/>
|
|
))
|
|
)}
|
|
</CardList>
|
|
|
|
<FooterAd />
|
|
|
|
<div className="paginator" id="paginator-bottom">
|
|
<Paginator
|
|
count={count}
|
|
true_count={trueCount}
|
|
offset={offset}
|
|
constructURL={(offset) =>
|
|
String(createPostsPageURL(offset, query, tags))
|
|
}
|
|
/>
|
|
</div>
|
|
</PageSkeleton>
|
|
);
|
|
}
|
|
|
|
export async function loader({ request }: LoaderFunctionArgs): Promise<IProps> {
|
|
const searchParams = new URL(request.url).searchParams;
|
|
|
|
let offset: number | undefined = undefined;
|
|
{
|
|
const parsedOffset = searchParams.get("o")?.trim();
|
|
|
|
if (parsedOffset) {
|
|
offset = parseOffset(parsedOffset);
|
|
}
|
|
}
|
|
|
|
const query = searchParams.get("q")?.trim();
|
|
const tags = searchParams.getAll("tag");
|
|
const { count, true_count, posts } = await fetchPosts(offset, query, tags);
|
|
const postsData = posts.map(({ service, user, id }) => ({
|
|
service,
|
|
user,
|
|
id,
|
|
}));
|
|
const profilesData = posts.reduce<{ service: string; id: string }[]>(
|
|
(profilesData, post) => {
|
|
const match = profilesData.find(
|
|
(profileData) =>
|
|
profileData.id === post.user && profileData.service === post.service
|
|
);
|
|
|
|
if (!match) {
|
|
profilesData.push({ service: post.service, id: post.user });
|
|
}
|
|
|
|
return profilesData;
|
|
},
|
|
[]
|
|
);
|
|
const favPosts = await findFavouritePosts(postsData);
|
|
const favProfiles = await findFavouriteProfiles(profilesData);
|
|
const postsWithFavs = posts.map<IPost>((post) => {
|
|
const isFavPost = Boolean(
|
|
favPosts.find(
|
|
({ service, user, id }) =>
|
|
id === post.id && user === post.user && service === post.service
|
|
)
|
|
);
|
|
const isFavProfile = Boolean(
|
|
favProfiles.find(
|
|
({ service, id }) => id === post.user && service === post.service
|
|
)
|
|
);
|
|
|
|
if (!isFavPost && !isFavProfile) {
|
|
return post;
|
|
}
|
|
|
|
return {
|
|
...post,
|
|
isFavourite: isFavPost,
|
|
isFavouriteProfile: isFavProfile,
|
|
};
|
|
});
|
|
|
|
return {
|
|
offset,
|
|
query,
|
|
tags,
|
|
count,
|
|
trueCount: true_count,
|
|
posts: postsWithFavs,
|
|
};
|
|
}
|