frontitygit
Version:
A Frontity source package for the REST API of self-hosted and WordPress.com sites
126 lines (107 loc) • 3.6 kB
text/typescript
import { Handler } from "../../../types";
import { ServerError } from "@frontity/source";
import { DateData, SearchData } from "@frontity/source/types/data";
import validateDate from "./utils/validateDate";
/**
* A {@link Handler} for fetching posts by date.
*
* @param params - Defined in {@link Handler}.
*
* @example
* ```js
* libraries.source.handlers.push({
* name: "date",
* priority: 20,
* pattern: "/:year(\\d+)/:month(\\d+)?/:day(\\d+)?",
* func: dateHandler,
* })
* ```
*
* @returns A Promise that will resolve once the data for the posts has loaded.
*/
export const dateHandler: Handler = async ({
link: linkArg,
route: routeArg,
params,
state,
libraries,
force,
}) => {
// This is only for backward compatibility for the moment when handlers used
// to receive `route` instead of `link`.
const link = linkArg || routeArg;
const { api, populate, parse, getTotal, getTotalPages } = libraries.source;
const { route, page, query } = parse(link);
// 1. build date properties
// year has to be parsed correctly because it HAD TO be matched by a pattern.
const year = parseInt(params.year);
// it's okay if month is undefined, this will return NaN in that case.
const month = params.month && parseInt(params.month);
// it's okay if month is undefined, this will return NaN in that case.
const day = params.day && parseInt(params.day);
validateDate(year, month, day);
const after = new Date(
`${params.year}-${params.month || "01"}-${params.day || "01"}`
);
const before = new Date(after);
if (!month) before.setUTCFullYear(year + 1);
else if (!day) before.setUTCMonth(month);
else before.setUTCDate(day + 1);
// 2. fetch the specified page
const response = await api.get({
endpoint: state.source.postEndpoint,
params: {
_embed: true,
after: after.toISOString(),
before: before.toISOString(),
search: query.s,
page,
...state.source.params,
},
});
// 3. populate response
const items = await populate({ response, state, force });
if (items.length === 0)
throw new ServerError(`date "${route}" doesn't have page ${page}`, 404);
// 4. get posts and pages count
const total = getTotal(response, items.length);
const totalPages = getTotalPages(response, 0);
// returns true if next page exists
const hasNewerPosts = page < totalPages;
// returns true if previous page exists
const hasOlderPosts = page > 1;
/**
* A helper function that helps "glue" the link back together
* from `route`, `query` and `page`.
*
* @param page - The page number.
* @returns The full link for a particular page.
* @example `getPageLink(1)`
*/
const getPageLink = (page: number) =>
libraries.source.stringify({ route, query, page });
// 5. add data to source
const currentPageData = state.source.data[link];
const newPageData = {
year,
items,
total,
totalPages,
isArchive: true,
isDate: true,
isFetching: currentPageData.isFetching,
isReady: currentPageData.isReady,
// Add next and previous if they exist.
...(hasOlderPosts && { previous: getPageLink(page - 1) }),
...(hasNewerPosts && { next: getPageLink(page + 1) }),
// Add day and month only if they exist.
...(day && { day }),
...(month && { month }),
// Add search data if this is a search.
...(query.s && { isSearch: true, searchQuery: query.s }),
};
Object.assign(currentPageData, newPageData) as
| DateData
| (DateData & SearchData);
};
export default dateHandler;