kemono2/client/src/pages/posts/popular.tsx
2024-11-26 00:11:49 +01:00

272 lines
6.9 KiB
TypeScript

import { LoaderFunctionArgs, useLoaderData } from "react-router-dom";
import { createPopularPostsPageURL } from "#lib/urls";
import { parseOffset } from "#lib/pagination";
import { fetchPopularPosts } from "#api/posts";
import { PageSkeleton } from "#components/pages";
import { Paginator } from "#components/pagination";
import { FooterAd, HeaderAd, SliderAd } from "#components/ads";
import { CardList, PostFavoriteCard } from "#components/cards";
import {
IPopularPostsPeriod,
IPostWithFavorites,
validatePeriod,
} from "#entities/posts";
interface IProps {
minDate: string;
maxDate: string;
rangeDescription: string;
earliestDateForPopular: string;
navigationDates: Record<IPopularPostsPeriod, [string, string, string]>;
scale?: IPopularPostsPeriod;
today: string;
count: number;
posts: IPostWithFavorites[];
offset?: number;
date?: string;
}
export function PopularPostsPage() {
const {
minDate,
maxDate,
rangeDescription,
navigationDates,
earliestDateForPopular,
scale,
today,
count,
offset,
date,
posts,
} = useLoaderData() as IProps;
const title = `Popular posts for ${rangeDescription}`;
return (
<PageSkeleton
name="popular-posts"
title={title}
heading={
<>
Popular Posts for{" "}
<span title={`${minDate} to ${maxDate}`}>{rangeDescription}</span>
</>
}
>
<SliderAd />
<div className="paginator" id="paginator-dates">
<div id="daily">
<span>
{navigationDates.day[0] < earliestDateForPopular ? (
<span>next »</span>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.day[0], "day")
)}
>
« prev
</a>
)}
</span>{" "}
<span>
{scale === "day" ? (
<strong>Day</strong>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.day[2], "day")
)}
>
Day
</a>
)}
</span>{" "}
<span>
{navigationDates.day[1] > today ? (
<span>next »</span>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.day[1], "day")
)}
>
next »
</a>
)}
</span>
</div>
<div id="weekly">
<span>
{navigationDates.week[0] < earliestDateForPopular ? (
<span>next »</span>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.week[0], "week")
)}
>
« prev
</a>
)}
</span>{" "}
<span>
{scale === "week" ? (
<strong>Week</strong>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.week[2], "week")
)}
>
Week
</a>
)}
</span>{" "}
<span>
{navigationDates.week[1] > today ? (
<span>next »</span>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.week[1], "week")
)}
>
next »
</a>
)}
</span>
</div>
<div id="monthly">
<span>
{navigationDates.month[0] < earliestDateForPopular ? (
<span>next »</span>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.month[0], "month")
)}
>
« prev
</a>
)}
</span>{" "}
<span>
{scale === "month" ? (
<strong>Month</strong>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.month[2], "month")
)}
>
Month
</a>
)}
</span>{" "}
<span>
<span>
{navigationDates.month[1] > today ? (
<span>next »</span>
) : (
<a
href={String(
createPopularPostsPageURL(navigationDates.month[1], "month")
)}
>
next »
</a>
)}
</span>
</span>
</div>
<div className="paginator" id="paginator-top">
<Paginator
count={count}
offset={offset}
constructURL={(offset) =>
String(createPopularPostsPageURL(date, scale, offset))
}
/>
<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) => (
<PostFavoriteCard
key={`${post.id}-${post.service}`}
post={post}
/>
))
)}
</CardList>
<FooterAd />
<div className="paginator" id="paginator-bottom">
<Paginator
count={count}
offset={offset}
constructURL={(offset) =>
String(createPopularPostsPageURL(date, scale, offset))
}
/>
</div>
</div>
</div>
</PageSkeleton>
);
}
export async function loader({ request }: LoaderFunctionArgs): Promise<IProps> {
const searchParams = new URL(request.url).searchParams;
const date = searchParams.get("date")?.trim();
const scale = searchParams.get("period")?.trim() ?? "recent";
validatePeriod(scale);
let offset: number | undefined;
{
const inputOffset = searchParams.get("o")?.trim();
if (inputOffset) {
offset = parseOffset(inputOffset);
}
}
const { info, props, results } = await fetchPopularPosts(date, scale, offset);
const { count, earliest_date_for_popular, today } = props;
const { range_desc, min_date, max_date, navigation_dates } = info;
return {
date,
count,
scale,
offset,
posts: results,
today,
earliestDateForPopular: earliest_date_for_popular,
rangeDescription: range_desc,
minDate: min_date,
maxDate: max_date,
navigationDates: navigation_dates,
};
}