Skip to content

Commit 42ec248

Browse files
sz3rytypeofweb
andauthored
refactor: Zmiana systemu paginacji (#153)
* in progress - simplify pagination * new buttons and props * sitemap generation with new pagination * refactor, delete unused encodings * fix: last PR errors fixed Co-authored-by: Michał Miszczyszyn <[email protected]>
1 parent 2bcff95 commit 42ec248

File tree

15 files changed

+134
-165
lines changed

15 files changed

+134
-165
lines changed

api-helpers/articles.ts

Lines changed: 28 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,18 @@
11
import type { PrismaClient } from '@prisma/client';
22

3-
import { isIsoDate } from '../utils/date-utils';
3+
import {
4+
LIST_ARTICLES_PER_PAGE,
5+
TILES_ARTICLES_PER_BLOG,
6+
TILES_BLOGS_PER_PAGE,
7+
} from '../constants';
48

5-
import { cursorToText, textToCursor } from './cursor-encoding';
69
import { HTTPNotFound } from './errors';
710

8-
export const TILES_BLOGS_PER_PAGE = 4;
9-
export const TILES_ARTICLES_PER_BLOG = 5;
10-
export const LIST_ARTICLES_PER_PAGE = 20;
11-
12-
function last<T extends readonly R[], R>(arr: readonly [R, ...T]): R;
13-
function last<T>(arr: readonly T[]): T | undefined;
14-
function last<T>(arr: readonly T[]): T | undefined {
15-
return arr[arr.length - 1];
16-
}
17-
18-
export const getArticlesForGrid = async (prisma: PrismaClient, cursor?: string) => {
19-
const date = cursor && cursorToText(cursor);
20-
21-
if (date && !isIsoDate(date)) {
22-
throw new HTTPNotFound();
23-
}
24-
25-
const where = date
26-
? {
27-
lastArticlePublishedAt: {
28-
lt: date,
29-
not: null,
30-
},
31-
}
32-
: {};
33-
11+
export const getArticlesForGrid = async (prisma: PrismaClient, page: number) => {
12+
const pageNumber = reversePageNumber(page, await getLastBlogPage(prisma));
3413
const blogs = await prisma.blog.findMany({
35-
where: { ...where, isPublic: true },
14+
where: { isPublic: true },
15+
skip: pageNumber * TILES_BLOGS_PER_PAGE,
3616
take: TILES_BLOGS_PER_PAGE,
3717
orderBy: {
3818
lastArticlePublishedAt: 'desc',
@@ -46,52 +26,34 @@ export const getArticlesForGrid = async (prisma: PrismaClient, cursor?: string)
4626
},
4727
},
4828
});
29+
if (blogs.length === 0) throw new HTTPNotFound();
4930

50-
const lastBlog = last(blogs);
5131
return {
5232
data: blogs,
53-
nextCursor:
54-
lastBlog?.lastArticlePublishedAt &&
55-
textToCursor(lastBlog?.lastArticlePublishedAt.toISOString()),
5633
};
5734
};
5835

59-
export const getArticlesPaginationForGrid = async (prisma: PrismaClient) => {
60-
const blogs = (await prisma.blog.findMany({
61-
where: { isPublic: true, lastArticlePublishedAt: { not: null } },
62-
orderBy: {
63-
lastArticlePublishedAt: 'desc',
64-
},
65-
select: {
66-
lastArticlePublishedAt: true,
67-
},
68-
})) as ReadonlyArray<{ readonly lastArticlePublishedAt: Date }>;
69-
70-
const cursors = blogs.flatMap((blog, index) => {
71-
if (index % TILES_BLOGS_PER_PAGE === TILES_BLOGS_PER_PAGE - 1) {
72-
return [textToCursor(blog.lastArticlePublishedAt.toISOString())];
73-
}
74-
return [];
36+
export const getLastArticlePage = async (prisma: PrismaClient) => {
37+
const articlesCount = await prisma.article.count({
38+
where: { blog: { isPublic: true } },
7539
});
76-
return cursors;
40+
return Math.ceil(articlesCount / LIST_ARTICLES_PER_PAGE);
7741
};
7842

79-
export const getArticlesForList = async (prisma: PrismaClient, cursor?: string) => {
80-
const date = cursor && cursorToText(cursor);
81-
82-
if (date && !isIsoDate(date)) {
83-
throw new HTTPNotFound();
84-
}
85-
86-
const where = date
87-
? {
88-
publishedAt: {
89-
lt: date,
90-
},
91-
}
92-
: {};
43+
export const getLastBlogPage = async (prisma: PrismaClient) => {
44+
const blogCount = await prisma.blog.count({
45+
where: { isPublic: true, lastArticlePublishedAt: { not: null } },
46+
});
47+
return Math.ceil(blogCount / TILES_BLOGS_PER_PAGE);
48+
};
49+
const reversePageNumber = (page: number, lastPage: number): number => {
50+
return lastPage - page;
51+
};
52+
export const getArticlesForList = async (prisma: PrismaClient, page: number) => {
53+
const pageNumber = reversePageNumber(page, await getLastArticlePage(prisma));
9354
const articles = await prisma.article.findMany({
94-
where: { ...where, blog: { isPublic: true } },
55+
skip: pageNumber * LIST_ARTICLES_PER_PAGE,
56+
where: { blog: { isPublic: true } },
9557
take: LIST_ARTICLES_PER_PAGE,
9658
orderBy: {
9759
publishedAt: 'desc',
@@ -100,36 +62,12 @@ export const getArticlesForList = async (prisma: PrismaClient, cursor?: string)
10062
blog: true,
10163
},
10264
});
103-
104-
const lastArticle = last(articles);
65+
if (articles.length === 0) throw new HTTPNotFound();
10566
return {
10667
data: articles,
107-
nextCursor: lastArticle?.publishedAt && textToCursor(lastArticle?.publishedAt.toISOString()),
10868
};
10969
};
11070

111-
export const getArticlesPaginationForList = async (prisma: PrismaClient) => {
112-
const articles = await prisma.article.findMany({
113-
where: {
114-
blog: { isPublic: true },
115-
},
116-
orderBy: {
117-
publishedAt: 'desc',
118-
},
119-
select: {
120-
publishedAt: true,
121-
},
122-
});
123-
124-
const cursors = articles.flatMap((article, index) => {
125-
if (index % LIST_ARTICLES_PER_PAGE === LIST_ARTICLES_PER_PAGE - 1) {
126-
return [textToCursor(article.publishedAt.toISOString())];
127-
}
128-
return [];
129-
});
130-
return cursors;
131-
};
132-
13371
export const getArticlesSlugs = async (prisma: PrismaClient, limit?: number) => {
13472
const articles = await prisma.article.findMany({
13573
where: {

api-helpers/cursor-encoding.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.

api-helpers/sitemap.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import type { PrismaClient } from '@prisma/client';
22

3-
import {
4-
getArticlesPaginationForGrid,
5-
getArticlesPaginationForList,
6-
getArticlesSlugs,
7-
} from './articles';
3+
import { getPagesArray } from '../utils/array-utils';
4+
5+
import { getLastArticlePage, getLastBlogPage, getArticlesSlugs } from './articles';
86

97
type Item = {
108
readonly path: string;
@@ -22,25 +20,27 @@ const staticItems: readonly Item[] = [
2220
];
2321

2422
export async function getSitemap(prisma: PrismaClient) {
25-
const [gridCursors, listCursors, articleSlugs] = await Promise.all([
26-
getArticlesPaginationForGrid(prisma),
27-
getArticlesPaginationForList(prisma),
23+
const [gridLastPage, listLastPage, articleSlugs] = await Promise.all([
24+
getLastBlogPage(prisma),
25+
getLastArticlePage(prisma),
2826
getArticlesSlugs(prisma),
2927
]);
28+
const gridPages = getPagesArray(gridLastPage);
29+
const listPages = getPagesArray(listLastPage);
3030

3131
const dynamicItems: readonly Item[] = [
3232
...articleSlugs.map(({ slug }) => ({
3333
path: `/artykuly/${slug}`,
3434
changefreq: 'monthly' as const,
3535
priority: 0.5,
3636
})),
37-
...gridCursors.map((cursor) => ({
38-
path: `/grid/${cursor}`,
37+
...gridPages.map((page) => ({
38+
path: `/grid/${page}`,
3939
changefreq: 'hourly' as const,
4040
priority: 0.4,
4141
})),
42-
...listCursors.map((cursor) => ({
43-
path: `/list/${cursor}`,
42+
...listPages.map((page) => ({
43+
path: `/list/${page}`,
4444
changefreq: 'hourly' as const,
4545
priority: 0.4,
4646
})),

components/Main/Main.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import dynamic from 'next/dynamic';
22
import { memo, useState } from 'react';
33

4-
import type { HomePageProps } from '../../pages/[displayStyle]/[cursor]';
4+
import type { HomePageProps } from '../../pages/[displayStyle]/[page]';
55
import type { AlgoliaSearchProps, SearchState } from '../AlgoliaSearch/AlgoliaSearch';
66
import { MainTiles } from '../MainTiles/MainTiles';
77

components/MainTiles/BlogsGrid.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import clsx from 'clsx';
22
import { memo } from 'react';
33

4-
import type { HomePageProps } from '../../pages/[displayStyle]/[cursor]';
4+
import type { HomePageProps } from '../../pages/[displayStyle]/[page]';
55
import { ArticleTile } from '../ArticleTile/ArticleTile';
66

77
import styles from './blogsGrid.module.scss';

components/MainTiles/BlogsList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { memo } from 'react';
22

3-
import type { HomePageProps } from '../../pages/[displayStyle]/[cursor]';
3+
import type { HomePageProps } from '../../pages/[displayStyle]/[page]';
44
import { ArticleTile } from '../ArticleTile/ArticleTile';
55

66
import styles from './blogsList.module.scss';

components/MainTiles/MainTiles.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { useCallback, memo } from 'react';
66
import { useDidMount } from '../../hooks/useDidMount';
77
import type { DisplayPreferences } from '../../hooks/useDisplayPreferences';
88
import { useDisplayPreferences } from '../../hooks/useDisplayPreferences';
9-
import type { HomePageProps } from '../../pages/[displayStyle]/[cursor]';
9+
import type { HomePageProps } from '../../pages/[displayStyle]/[page]';
1010
import { Button } from '../Button/Button';
1111
import { DisplayStyleSwitch } from '../DisplayStyleSwitch/DisplayStyleSwitch';
1212

@@ -26,6 +26,9 @@ export const MainTiles = memo<MainTilesProps>((props) => {
2626
},
2727
[changeDisplay],
2828
);
29+
const nextPage = Number(props.pageNumber) - 1;
30+
const previousPage = Number(props.pageNumber) + 1;
31+
const isFirstPage = Number(props.pageNumber) === 1;
2932

3033
useDidMount(() => {
3134
void router.prefetch(`/grid`);
@@ -49,8 +52,27 @@ export const MainTiles = memo<MainTilesProps>((props) => {
4952
<BlogsGrid blogs={props.blogs} />
5053
)}
5154
<div className={styles.pagination}>
52-
{props.nextCursor && (
53-
<Link passHref href={`/${props.displayStyle}/${props.nextCursor}`}>
55+
{!props.isLastPage && (
56+
<>
57+
<Link passHref href={`/${props.displayStyle}/${previousPage}`}>
58+
<Button
59+
className={styles.nextPageButton}
60+
as="a"
61+
iconPosition="left"
62+
icon="icon-arrow-left2"
63+
>
64+
Poprzednia strona
65+
</Button>
66+
</Link>
67+
<Link passHref href={`/${props.displayStyle}`}>
68+
<Button className={styles.nextPageButton} as="a">
69+
Najnowsze
70+
</Button>
71+
</Link>
72+
</>
73+
)}
74+
{!isFirstPage && (
75+
<Link passHref href={`/${props.displayStyle}/${nextPage}`}>
5476
<Button
5577
className={styles.nextPageButton}
5678
as="a"

components/MainTiles/mainTiles.module.scss

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,18 @@
2020

2121
.pagination {
2222
display: flex;
23+
flex: 1 0 100%;
24+
flex-direction: column;
2325
justify-content: flex-end;
26+
& > a:only-child {
27+
flex-grow: 0;
28+
}
29+
& > a {
30+
flex-grow: 1;
31+
}
32+
@media screen and (min-width: 45em) {
33+
flex-direction: row;
34+
}
2435
}
2536

2637
.nextPageButton {

constants/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// PAGE [displayStyle]/[page]
2+
export const REVALIDATION_TIME = 15 * 60; // 15 minutes
3+
export const MAX_PAGES = 5;
4+
5+
//api-helpers / articles
6+
export const TILES_BLOGS_PER_PAGE = 4;
7+
export const TILES_ARTICLES_PER_BLOG = 5;
8+
export const LIST_ARTICLES_PER_PAGE = 20;

0 commit comments

Comments
 (0)