Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions src/providers/lemmyv0/compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,8 +505,28 @@ export function toModlogView(
}
}

/**
* Canonical pagination for the page-number softwares (Lemmy v0, PieFed).
*
* `next_page` has to mean "there is more" — handing one out unconditionally
* makes a consumer's `while (next_page)` loop spin forever. The server's own
* cursor wins when the endpoint returns one; otherwise a page at least as
* long as the limit implies there may be another, and a shorter one is the
* end.
*
* "At least" matters: endpoints that merge several requests (notifications,
* person content, all-type search, modlog) legitimately overshoot, because
* these servers apply the limit per bucket. A merged page shorter than the
* limit means every bucket was short — genuinely exhausted.
*/
export function toPageResponse(
params: types.PageParams,
page?: {
/** How many items this page returned */
items: number;
/** The server's own cursor, for endpoints that return one */
next_page?: null | string;
},
): types.PagableResponse {
const page_cursor = params.page_cursor;

Expand All @@ -515,9 +535,25 @@ export function toPageResponse(
"lemmyv0 does not support string page_cursor",
);

return {
next_page: (page_cursor ?? 1) + 1,
};
if (page?.next_page !== undefined) {
const serverCursor =
page.next_page === null ? undefined : Number(page.next_page);

// A cursor we can't use as a page number is no cursor at all — better
// to report end-of-feed than to send the server garbage
if (serverCursor === undefined || !Number.isFinite(serverCursor))
return { next_page: undefined };

return { next_page: serverCursor };
}

const nextPage = (page_cursor ?? 1) + 1;

// Without a limit there's nothing to compare a short page against, so
// keep assuming there's more
if (!page || params.limit === undefined) return { next_page: nextPage };

return { next_page: page.items >= params.limit ? nextPage : undefined };
}

export function toPerson(
Expand Down
88 changes: 52 additions & 36 deletions src/providers/lemmyv0/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,11 @@ export class UnsafeLemmyV0Client implements BaseClient {
options,
);

const data = response.comments.map(compat.toCommentView);

return {
...compat.toPageResponse(payload),
data: response.comments.map(compat.toCommentView),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}

Expand Down Expand Up @@ -308,13 +310,15 @@ export class UnsafeLemmyV0Client implements BaseClient {
options,
);

const data = Object.values(response)
.flat()
.map(compat.toModlogView)
.filter((m) => !!m)
.sort((a, b) => Date.parse(getLogDate(b)) - Date.parse(getLogDate(a)));

return {
...compat.toPageResponse(payload),
data: Object.values(response)
.flat()
.map(compat.toModlogView)
.filter((m) => !!m)
.sort((a, b) => Date.parse(getLogDate(b)) - Date.parse(getLogDate(a))),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}

Expand Down Expand Up @@ -376,7 +380,7 @@ export class UnsafeLemmyV0Client implements BaseClient {
}

return {
...compat.toPageResponse(payload),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}
Expand Down Expand Up @@ -539,9 +543,11 @@ export class UnsafeLemmyV0Client implements BaseClient {
options,
);

const data = response.comment_reports.map(compat.toCommentReportView);

return {
...compat.toPageResponse(payload),
data: response.comment_reports.map(compat.toCommentReportView),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}

Expand All @@ -564,9 +570,11 @@ export class UnsafeLemmyV0Client implements BaseClient {
options,
);

const data = response.communities.map(compat.toCommunityView);

return {
...compat.toPageResponse(payload),
data: response.communities.map(compat.toCommunityView),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}

Expand Down Expand Up @@ -604,7 +612,7 @@ export class UnsafeLemmyV0Client implements BaseClient {
})();

return {
...compat.toPageResponse(payload),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}
Expand All @@ -625,12 +633,14 @@ export class UnsafeLemmyV0Client implements BaseClient {
this.#client.getComments(v0Payload, options),
]);

const data = [
...comments.map(compat.toCommentView),
...posts.map(compat.toPostView),
].sort(sortPostCommentByPublished);

return {
data: [
...comments.map(compat.toCommentView),
...posts.map(compat.toPostView),
].sort(sortPostCommentByPublished),
...compat.toPageResponse(payload),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}

Expand All @@ -657,9 +667,11 @@ export class UnsafeLemmyV0Client implements BaseClient {
options,
);

const data = response.post_reports.map(compat.toPostReportView);

return {
...compat.toPageResponse(payload),
data: response.post_reports.map(compat.toPostReportView),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}

Expand All @@ -674,15 +686,17 @@ export class UnsafeLemmyV0Client implements BaseClient {
this.#client.listPostReports(params, options),
]);

const data = [
...comment_reports.map(compat.toCommentReportView),
...post_reports.map(compat.toPostReportView),
].sort(
(a, b) =>
getPostCommentItemCreatedDate(b) - getPostCommentItemCreatedDate(a),
);

return {
...compat.toPageResponse(payload),
data: [
...comment_reports.map(compat.toCommentReportView),
...post_reports.map(compat.toPostReportView),
].sort(
(a, b) =>
getPostCommentItemCreatedDate(b) - getPostCommentItemCreatedDate(a),
),
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}

Expand Down Expand Up @@ -856,14 +870,16 @@ export class UnsafeLemmyV0Client implements BaseClient {
options,
);

const data = [
...response.comments.map(compat.toCommentView),
...response.posts.map(compat.toPostView),
...response.communities.map(compat.toCommunityView),
...response.users.map(compat.toPersonView),
];

return {
...compat.toPageResponse(payload),
data: [
...response.comments.map(compat.toCommentView),
...response.posts.map(compat.toPostView),
...response.communities.map(compat.toCommunityView),
...response.users.map(compat.toPersonView),
],
...compat.toPageResponse(payload, { items: data.length }),
data,
};
}

Expand Down
Loading