Skip to content

Commit 2c509b8

Browse files
authored
Merge pull request #105 from yamanoku/feature/rename-cont-to-page
feat: parameter from `cont` to `page` and add newer link for pagination
2 parents c47aa52 + 6ae3232 commit 2c509b8

File tree

2 files changed

+42
-8
lines changed

2 files changed

+42
-8
lines changed

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ To be released.
2222
- Added [`GET /api/v1/blocks`] API to Mastodon comapatiblity layer. This API
2323
returns a list of accounts that are blocked by the authenticated user.
2424

25+
- On profile page, backward pagination (newer posts) is now available.
26+
[[#104], [#105] by Okuto Oyama]
27+
2528
- On profile page, images are no more captioned using `<figcaption>` but
2629
use only `alt` attribute for accessibility. [[#99], [#100] by Okuto Oyama]
2730

@@ -37,6 +40,8 @@ To be released.
3740
[#99]: https://github.com/fedify-dev/hollo/issues/99
3841
[#100]: https://github.com/fedify-dev/hollo/pull/100
3942
[#103]: https://github.com/fedify-dev/hollo/issues/103
43+
[#104]: https://github.com/fedify-dev/hollo/issues/104
44+
[#105]: https://github.com/fedify-dev/hollo/pull/105
4045
[#106]: https://github.com/fedify-dev/hollo/pull/106
4146
[`GET /api/v1/mutes`]: https://docs.joinmastodon.org/methods/mutes/#get
4247
[`GET /api/v1/blocks`]: https://docs.joinmastodon.org/methods/blocks/#get

src/pages/profile/index.tsx

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { and, desc, eq, lte, or } from "drizzle-orm";
1+
import { and, count, desc, eq, or } from "drizzle-orm";
22
import { Hono } from "hono";
33
import xss from "xss";
44
import { Layout } from "../../components/Layout.tsx";
@@ -26,7 +26,7 @@ const profile = new Hono();
2626

2727
profile.route("/:id{[-a-f0-9]+}", profilePost);
2828

29-
const WINDOW = 30;
29+
const PAGE_SIZE = 30;
3030

3131
profile.get<"/:handle">(async (c) => {
3232
let handle = c.req.param("handle");
@@ -39,14 +39,38 @@ profile.get<"/:handle">(async (c) => {
3939
const contStr = c.req.query("cont");
4040
const cont = contStr == null || contStr.trim() === "" ? undefined : contStr;
4141
if (cont != null && !isUuid(cont)) return c.notFound();
42+
const pageStr = c.req.query("page");
43+
if (
44+
pageStr !== undefined &&
45+
(Number.isNaN(Number.parseInt(pageStr)) || Number.parseInt(pageStr) < 1)
46+
) {
47+
return c.notFound();
48+
}
49+
const page =
50+
pageStr !== undefined && !Number.isNaN(Number.parseInt(pageStr))
51+
? Number.parseInt(pageStr)
52+
: 1;
53+
const [{ totalPosts }] = await db
54+
.select({ totalPosts: count() })
55+
.from(posts)
56+
.where(
57+
and(
58+
eq(posts.accountId, owner.id),
59+
or(eq(posts.visibility, "public"), eq(posts.visibility, "unlisted")),
60+
),
61+
);
62+
const maxPage = Math.ceil(totalPosts / PAGE_SIZE);
63+
if (page > maxPage) {
64+
return c.notFound();
65+
}
4266
const postList = await db.query.posts.findMany({
4367
where: and(
4468
eq(posts.accountId, owner.id),
4569
or(eq(posts.visibility, "public"), eq(posts.visibility, "unlisted")),
46-
...(cont == null ? [] : [lte(posts.id, cont)]),
4770
),
4871
orderBy: desc(posts.id),
49-
limit: WINDOW + 1,
72+
limit: PAGE_SIZE,
73+
offset: (page - 1) * PAGE_SIZE,
5074
with: {
5175
account: true,
5276
media: true,
@@ -133,12 +157,13 @@ profile.get<"/:handle">(async (c) => {
133157
const atomUrl = new URL(c.req.url);
134158
atomUrl.pathname += "/atom.xml";
135159
atomUrl.search = "";
160+
const newerUrl = page > 1 ? `?page=${page - 1}` : undefined;
136161
const olderUrl =
137-
postList.length > WINDOW ? `?cont=${postList[WINDOW].id}` : undefined;
162+
postList.length === PAGE_SIZE ? `?page=${page + 1}` : undefined;
138163
return c.html(
139164
<ProfilePage
140165
accountOwner={owner}
141-
posts={postList.slice(0, WINDOW)}
166+
posts={postList.slice(0, PAGE_SIZE)}
142167
pinnedPosts={pinnedPostList
143168
.map((p) => p.post)
144169
.filter(
@@ -147,6 +172,7 @@ profile.get<"/:handle">(async (c) => {
147172
featuredTags={featuredTagList}
148173
atomUrl={atomUrl.href}
149174
olderUrl={olderUrl}
175+
newerUrl={newerUrl}
150176
/>,
151177
);
152178
});
@@ -224,6 +250,7 @@ interface ProfilePageProps {
224250
readonly featuredTags: FeaturedTag[];
225251
readonly atomUrl: string;
226252
readonly olderUrl?: string;
253+
readonly newerUrl?: string;
227254
}
228255

229256
function ProfilePage({
@@ -233,6 +260,7 @@ function ProfilePage({
233260
featuredTags,
234261
atomUrl,
235262
olderUrl,
263+
newerUrl,
236264
}: ProfilePageProps) {
237265
return (
238266
<Layout
@@ -267,8 +295,9 @@ function ProfilePage({
267295
{posts.map((post) => (
268296
<PostView post={post} />
269297
))}
270-
<div style="text-align: right;">
271-
{olderUrl && <a href={olderUrl}>Older &rarr;</a>}
298+
<div style={{ display: "flex", justifyContent: "space-between" }}>
299+
<div>{newerUrl && <a href={newerUrl}>&larr; Newer</a>}</div>
300+
<div>{olderUrl && <a href={olderUrl}>Older &rarr;</a>}</div>
272301
</div>
273302
</Layout>
274303
);

0 commit comments

Comments
 (0)