272 lines
6.9 KiB
TypeScript
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,
|
|
};
|
|
}
|