Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding cursor pagination. Fixes #2155 #2173

Merged
merged 5 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
21 changes: 13 additions & 8 deletions src/server/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,26 @@ const [hostname, port] = process.env["LEMMY_UI_HOST"]

server.use(express.json());
server.use(express.urlencoded({ extended: false }));
server.use(
getStaticDir(),
express.static(path.resolve("./dist"), {
maxAge: 24 * 60 * 60 * 1000, // 1 day
immutable: true,
}),
);
server.use(setCacheControl);

const serverPath = path.resolve("./dist");

if (
!process.env["LEMMY_UI_DISABLE_CSP"] &&
!process.env["LEMMY_UI_DEBUG"] &&
process.env["NODE_ENV"] !== "development"
) {
server.use(
getStaticDir(),
express.static(serverPath, {
maxAge: 24 * 60 * 60 * 1000, // 1 day
immutable: true,
}),
);
server.use(setDefaultCsp);
server.use(setCacheControl);
} else {
// In debug mode, don't use the maxAge and immutable, or it breaks live reload for dev
server.use(getStaticDir(), express.static(serverPath));
}

server.get("/.well-known/security.txt", SecurityHandler);
Expand Down
46 changes: 46 additions & 0 deletions src/shared/components/common/paginator-cursor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Component, linkEvent } from "inferno";
import { I18NextService } from "../../services";
import { PaginationCursor } from "lemmy-js-client";

interface PaginatorCursorProps {
prevPage?: PaginationCursor;
nextPage?: PaginationCursor;
onNext(val: PaginationCursor): any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we use the return value for this? If not, we could make this return void as well.

onPrev(): any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use what gets returned from this (if anything), so it could just return void.

}

export class PaginatorCursor extends Component<PaginatorCursorProps, any> {
constructor(props: any, context: any) {
super(props, context);
}
render() {
return (
<div className="paginator my-2">
<button
className="btn btn-secondary me-2"
disabled={!this.props.prevPage}
onClick={linkEvent(this, this.handlePrev)}
>
{I18NextService.i18n.t("prev")}
</button>
<button
className="btn btn-secondary"
onClick={linkEvent(this, this.handleNext)}
disabled={!this.props.nextPage}
>
{I18NextService.i18n.t("next")}
</button>
</div>
);
}

handlePrev(i: PaginatorCursor) {
i.props.onPrev();
}

handleNext(i: PaginatorCursor) {
if (i.props.nextPage) {
i.props.onNext(i.props.nextPage);
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: these can be declared as regular functions instead of class methods since you are using linkEvent.

}
70 changes: 34 additions & 36 deletions src/shared/components/community/community.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ import {
updateCommunityBlock,
updatePersonBlock,
} from "@utils/app";
import {
getPageFromString,
getQueryParams,
getQueryString,
} from "@utils/helpers";
import { getQueryParams, getQueryString } from "@utils/helpers";
import type { QueryParams } from "@utils/types";
import { RouteDataResponse } from "@utils/types";
import { Component, RefObject, createRef, linkEvent } from "inferno";
Expand Down Expand Up @@ -62,6 +58,7 @@ import {
MarkCommentReplyAsRead,
MarkPersonMentionAsRead,
MarkPostAsRead,
PaginationCursor,
PostResponse,
PurgeComment,
PurgeCommunity,
Expand Down Expand Up @@ -96,12 +93,12 @@ import { BannerIconHeader } from "../common/banner-icon-header";
import { DataTypeSelect } from "../common/data-type-select";
import { HtmlTags } from "../common/html-tags";
import { Icon, Spinner } from "../common/icon";
import { Paginator } from "../common/paginator";
import { SortSelect } from "../common/sort-select";
import { Sidebar } from "../community/sidebar";
import { SiteSidebar } from "../home/site-sidebar";
import { PostListings } from "../post/post-listings";
import { CommunityLink } from "./community-link";
import { PaginatorCursor } from "../common/paginator-cursor";

type CommunityData = RouteDataResponse<{
communityRes: GetCommunityResponse;
Expand All @@ -122,13 +119,13 @@ interface State {
interface CommunityProps {
dataType: DataType;
sort: SortType;
page: number;
pageCursor?: PaginationCursor;
}

function getCommunityQueryParams() {
return getQueryParams<CommunityProps>({
dataType: getDataTypeFromQuery,
page: getPageFromString,
pageCursor: cursor => cursor,
sort: getSortTypeFromQuery,
});
}
Expand Down Expand Up @@ -165,7 +162,8 @@ export class Community extends Component<

this.handleSortChange = this.handleSortChange.bind(this);
this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
this.handlePageChange = this.handlePageChange.bind(this);
this.handlePageNext = this.handlePageNext.bind(this);
this.handlePagePrev = this.handlePagePrev.bind(this);

// All of the action binds
this.handleDeleteCommunity = this.handleDeleteCommunity.bind(this);
Expand Down Expand Up @@ -236,7 +234,7 @@ export class Community extends Component<
static async fetchInitialData({
client,
path,
query: { dataType: urlDataType, page: urlPage, sort: urlSort },
query: { dataType: urlDataType, pageCursor, sort: urlSort },
}: InitialFetchRequest<QueryParams<CommunityProps>>): Promise<
Promise<CommunityData>
> {
Expand All @@ -251,15 +249,13 @@ export class Community extends Component<

const sort = getSortTypeFromQuery(urlSort);

const page = getPageFromString(urlPage);

let postsResponse: RequestState<GetPostsResponse> = EMPTY_REQUEST;
let commentsResponse: RequestState<GetCommentsResponse> = EMPTY_REQUEST;

if (dataType === DataType.Post) {
const getPostsForm: GetPosts = {
community_name: communityName,
page,
page_cursor: pageCursor,
limit: fetchLimit,
sort,
type_: "All",
Expand All @@ -270,7 +266,6 @@ export class Community extends Component<
} else {
const getCommentsForm: GetComments = {
community_name: communityName,
page,
limit: fetchLimit,
sort: postToCommentSortType(sort),
type_: "All",
Expand All @@ -287,6 +282,12 @@ export class Community extends Component<
};
}

get getNextPage(): PaginationCursor | undefined {
return this.state.postsRes.state === "success"
? this.state.postsRes.data.next_page
: undefined;
}

get documentTitle(): string {
const cRes = this.state.communityRes;
return cRes.state === "success"
Expand All @@ -295,6 +296,7 @@ export class Community extends Component<
}

renderCommunity() {
const { pageCursor } = getCommunityQueryParams();
switch (this.state.communityRes.state) {
case "loading":
return (
Expand All @@ -304,7 +306,6 @@ export class Community extends Component<
);
case "success": {
const res = this.state.communityRes.data;
const { page } = getCommunityQueryParams();

return (
<>
Expand Down Expand Up @@ -341,13 +342,11 @@ export class Community extends Component<
</div>
{this.selects(res)}
{this.listings(res)}
<Paginator
page={page}
onChange={this.handlePageChange}
nextDisabled={
this.state.postsRes.state !== "success" ||
fetchLimit > this.state.postsRes.data.posts.length
}
<PaginatorCursor
prevPage={pageCursor}
nextPage={this.getNextPage}
onNext={this.handlePageNext}
onPrev={this.handlePagePrev}
/>
</main>
<aside className="d-none d-md-block col-md-4 col-lg-3">
Expand Down Expand Up @@ -542,18 +541,22 @@ export class Community extends Component<
);
}

handlePageChange(page: number) {
this.updateUrl({ page });
handlePagePrev() {
this.props.history.back();
}

handlePageNext(nextPage: PaginationCursor) {
this.updateUrl({ pageCursor: nextPage });
window.scrollTo(0, 0);
}

handleSortChange(sort: SortType) {
this.updateUrl({ sort, page: 1 });
this.updateUrl({ sort, pageCursor: undefined });
window.scrollTo(0, 0);
}

handleDataTypeChange(dataType: DataType) {
this.updateUrl({ dataType, page: 1 });
this.updateUrl({ dataType, pageCursor: undefined });
window.scrollTo(0, 0);
}

Expand All @@ -563,16 +566,12 @@ export class Community extends Component<
}));
}

async updateUrl({ dataType, page, sort }: Partial<CommunityProps>) {
const {
dataType: urlDataType,
page: urlPage,
sort: urlSort,
} = getCommunityQueryParams();
async updateUrl({ dataType, pageCursor, sort }: Partial<CommunityProps>) {
const { dataType: urlDataType, sort: urlSort } = getCommunityQueryParams();

const queryParams: QueryParams<CommunityProps> = {
dataType: getDataTypeString(dataType ?? urlDataType),
page: (page ?? urlPage).toString(),
pageCursor: pageCursor,
sort: sort ?? urlSort,
};

Expand All @@ -584,14 +583,14 @@ export class Community extends Component<
}

async fetchData() {
const { dataType, page, sort } = getCommunityQueryParams();
const { dataType, pageCursor, sort } = getCommunityQueryParams();
const { name } = this.props.match.params;

if (dataType === DataType.Post) {
this.setState({ postsRes: LOADING_REQUEST });
this.setState({
postsRes: await HttpService.client.getPosts({
page,
page_cursor: pageCursor,
limit: fetchLimit,
sort,
type_: "All",
Expand All @@ -603,7 +602,6 @@ export class Community extends Component<
this.setState({ commentsRes: LOADING_REQUEST });
this.setState({
commentsRes: await HttpService.client.getComments({
page,
limit: fetchLimit,
sort: postToCommentSortType(sort),
type_: "All",
Expand Down
Loading