mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-25 13:51:13 +00:00
* Adding cursor pagination. Fixes #2155 * Addressing PR comments. --------- Co-authored-by: SleeplessOne1917 <abias1122@gmail.com>
This commit is contained in:
parent
ae4c37ed44
commit
2c1f4538be
4 changed files with 138 additions and 76 deletions
|
@ -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);
|
||||
|
|
46
src/shared/components/common/paginator-cursor.tsx
Normal file
46
src/shared/components/common/paginator-cursor.tsx
Normal file
|
@ -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): void;
|
||||
onPrev(): void;
|
||||
}
|
||||
|
||||
function handlePrev(i: PaginatorCursor) {
|
||||
i.props.onPrev();
|
||||
}
|
||||
|
||||
function handleNext(i: PaginatorCursor) {
|
||||
if (i.props.nextPage) {
|
||||
i.props.onNext(i.props.nextPage);
|
||||
}
|
||||
}
|
||||
|
||||
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, handlePrev)}
|
||||
>
|
||||
{I18NextService.i18n.t("prev")}
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-secondary"
|
||||
onClick={linkEvent(this, handleNext)}
|
||||
disabled={!this.props.nextPage}
|
||||
>
|
||||
{I18NextService.i18n.t("next")}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
@ -62,6 +58,7 @@ import {
|
|||
MarkCommentReplyAsRead,
|
||||
MarkPersonMentionAsRead,
|
||||
MarkPostAsRead,
|
||||
PaginationCursor,
|
||||
PostResponse,
|
||||
PurgeComment,
|
||||
PurgeCommunity,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
> {
|
||||
|
@ -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",
|
||||
|
@ -270,7 +266,6 @@ export class Community extends Component<
|
|||
} else {
|
||||
const getCommentsForm: GetComments = {
|
||||
community_name: communityName,
|
||||
page,
|
||||
limit: fetchLimit,
|
||||
sort: postToCommentSortType(sort),
|
||||
type_: "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"
|
||||
|
@ -295,6 +296,7 @@ export class Community extends Component<
|
|||
}
|
||||
|
||||
renderCommunity() {
|
||||
const { pageCursor } = getCommunityQueryParams();
|
||||
switch (this.state.communityRes.state) {
|
||||
case "loading":
|
||||
return (
|
||||
|
@ -304,7 +306,6 @@ export class Community extends Component<
|
|||
);
|
||||
case "success": {
|
||||
const res = this.state.communityRes.data;
|
||||
const { page } = getCommunityQueryParams();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -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">
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
updatePersonBlock,
|
||||
} from "@utils/app";
|
||||
import {
|
||||
getPageFromString,
|
||||
getQueryParams,
|
||||
getQueryString,
|
||||
getRandomFromList,
|
||||
|
@ -60,6 +59,7 @@ import {
|
|||
MarkCommentReplyAsRead,
|
||||
MarkPersonMentionAsRead,
|
||||
MarkPostAsRead,
|
||||
PaginationCursor,
|
||||
PostResponse,
|
||||
PurgeComment,
|
||||
PurgeItemResponse,
|
||||
|
@ -98,11 +98,11 @@ import { DataTypeSelect } from "../common/data-type-select";
|
|||
import { HtmlTags } from "../common/html-tags";
|
||||
import { Icon, Spinner } from "../common/icon";
|
||||
import { ListingTypeSelect } from "../common/listing-type-select";
|
||||
import { Paginator } from "../common/paginator";
|
||||
import { SortSelect } from "../common/sort-select";
|
||||
import { CommunityLink } from "../community/community-link";
|
||||
import { PostListings } from "../post/post-listings";
|
||||
import { SiteSidebar } from "./site-sidebar";
|
||||
import { PaginatorCursor } from "../common/paginator-cursor";
|
||||
|
||||
interface HomeState {
|
||||
postsRes: RequestState<GetPostsResponse>;
|
||||
|
@ -123,7 +123,7 @@ interface HomeProps {
|
|||
listingType?: ListingType;
|
||||
dataType: DataType;
|
||||
sort: SortType;
|
||||
page: number;
|
||||
pageCursor?: PaginationCursor;
|
||||
}
|
||||
|
||||
type HomeData = RouteDataResponse<{
|
||||
|
@ -185,13 +185,14 @@ function getSortTypeFromQuery(type?: string): SortType {
|
|||
return (type ? (type as SortType) : mySortType) ?? "Active";
|
||||
}
|
||||
|
||||
const getHomeQueryParams = () =>
|
||||
getQueryParams<HomeProps>({
|
||||
function getHomeQueryParams() {
|
||||
return getQueryParams<HomeProps>({
|
||||
sort: getSortTypeFromQuery,
|
||||
listingType: getListingTypeFromQuery,
|
||||
page: getPageFromString,
|
||||
pageCursor: cursor => cursor,
|
||||
dataType: getDataTypeFromQuery,
|
||||
});
|
||||
}
|
||||
|
||||
const MobileButton = ({
|
||||
textKey,
|
||||
|
@ -245,7 +246,8 @@ export class Home extends Component<any, HomeState> {
|
|||
this.handleSortChange = this.handleSortChange.bind(this);
|
||||
this.handleListingTypeChange = this.handleListingTypeChange.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);
|
||||
|
||||
this.handleCreateComment = this.handleCreateComment.bind(this);
|
||||
this.handleEditComment = this.handleEditComment.bind(this);
|
||||
|
@ -315,7 +317,7 @@ export class Home extends Component<any, HomeState> {
|
|||
|
||||
static async fetchInitialData({
|
||||
client,
|
||||
query: { dataType: urlDataType, listingType, page: urlPage, sort: urlSort },
|
||||
query: { dataType: urlDataType, listingType, pageCursor, sort: urlSort },
|
||||
site,
|
||||
}: InitialFetchRequest<QueryParams<HomeProps>>): Promise<HomeData> {
|
||||
const dataType = getDataTypeFromQuery(urlDataType);
|
||||
|
@ -324,15 +326,13 @@ export class Home extends Component<any, HomeState> {
|
|||
site.site_view.local_site.default_post_listing_type;
|
||||
const sort = getSortTypeFromQuery(urlSort);
|
||||
|
||||
const page = urlPage ? Number(urlPage) : 1;
|
||||
|
||||
let postsRes: RequestState<GetPostsResponse> = EMPTY_REQUEST;
|
||||
let commentsRes: RequestState<GetCommentsResponse> = EMPTY_REQUEST;
|
||||
|
||||
if (dataType === DataType.Post) {
|
||||
const getPostsForm: GetPosts = {
|
||||
type_,
|
||||
page,
|
||||
page_cursor: pageCursor,
|
||||
limit: fetchLimit,
|
||||
sort,
|
||||
saved_only: false,
|
||||
|
@ -341,7 +341,6 @@ export class Home extends Component<any, HomeState> {
|
|||
postsRes = await client.getPosts(getPostsForm);
|
||||
} else {
|
||||
const getCommentsForm: GetComments = {
|
||||
page,
|
||||
limit: fetchLimit,
|
||||
sort: postToCommentSortType(sort),
|
||||
type_,
|
||||
|
@ -616,18 +615,22 @@ export class Home extends Component<any, HomeState> {
|
|||
);
|
||||
}
|
||||
|
||||
async updateUrl({ dataType, listingType, page, sort }: Partial<HomeProps>) {
|
||||
async updateUrl({
|
||||
dataType,
|
||||
listingType,
|
||||
pageCursor,
|
||||
sort,
|
||||
}: Partial<HomeProps>) {
|
||||
const {
|
||||
dataType: urlDataType,
|
||||
listingType: urlListingType,
|
||||
page: urlPage,
|
||||
sort: urlSort,
|
||||
} = getHomeQueryParams();
|
||||
|
||||
const queryParams: QueryParams<HomeProps> = {
|
||||
dataType: getDataTypeString(dataType ?? urlDataType),
|
||||
listingType: listingType ?? urlListingType,
|
||||
page: (page ?? urlPage).toString(),
|
||||
pageCursor: pageCursor,
|
||||
sort: sort ?? urlSort,
|
||||
};
|
||||
|
||||
|
@ -645,26 +648,30 @@ export class Home extends Component<any, HomeState> {
|
|||
}
|
||||
|
||||
get posts() {
|
||||
const { page } = getHomeQueryParams();
|
||||
const { pageCursor } = getHomeQueryParams();
|
||||
|
||||
return (
|
||||
<div className="main-content-wrapper">
|
||||
<div>
|
||||
{this.selects}
|
||||
{this.listings}
|
||||
<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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
get getNextPage(): PaginationCursor | undefined {
|
||||
return this.state.postsRes.state === "success"
|
||||
? this.state.postsRes.data.next_page
|
||||
: undefined;
|
||||
}
|
||||
|
||||
get listings() {
|
||||
const { dataType } = getHomeQueryParams();
|
||||
const siteRes = this.state.siteRes;
|
||||
|
@ -804,7 +811,7 @@ export class Home extends Component<any, HomeState> {
|
|||
}
|
||||
|
||||
async fetchData() {
|
||||
const { dataType, page, listingType, sort } = getHomeQueryParams();
|
||||
const { dataType, pageCursor, listingType, sort } = getHomeQueryParams();
|
||||
|
||||
if (dataType === DataType.Post) {
|
||||
if (HomeCacheService.active) {
|
||||
|
@ -814,13 +821,12 @@ export class Home extends Component<any, HomeState> {
|
|||
window.scrollTo({
|
||||
left: 0,
|
||||
top: scrollY,
|
||||
behavior: "instant",
|
||||
});
|
||||
} else {
|
||||
this.setState({ postsRes: LOADING_REQUEST });
|
||||
this.setState({
|
||||
postsRes: await HttpService.client.getPosts({
|
||||
page,
|
||||
page_cursor: pageCursor,
|
||||
limit: fetchLimit,
|
||||
sort,
|
||||
saved_only: false,
|
||||
|
@ -834,7 +840,6 @@ export class Home extends Component<any, HomeState> {
|
|||
this.setState({ commentsRes: LOADING_REQUEST });
|
||||
this.setState({
|
||||
commentsRes: await HttpService.client.getComments({
|
||||
page,
|
||||
limit: fetchLimit,
|
||||
sort: postToCommentSortType(sort),
|
||||
saved_only: false,
|
||||
|
@ -862,24 +867,32 @@ export class Home extends Component<any, HomeState> {
|
|||
i.setState({ subscribedCollapsed: !i.state.subscribedCollapsed });
|
||||
}
|
||||
|
||||
handlePageChange(page: number) {
|
||||
handlePagePrev() {
|
||||
this.props.history.back();
|
||||
// A hack to scroll to top
|
||||
setTimeout(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
handlePageNext(nextPage: PaginationCursor) {
|
||||
this.setState({ scrolled: false });
|
||||
this.updateUrl({ page });
|
||||
this.updateUrl({ pageCursor: nextPage });
|
||||
}
|
||||
|
||||
handleSortChange(val: SortType) {
|
||||
this.setState({ scrolled: false });
|
||||
this.updateUrl({ sort: val, page: 1 });
|
||||
this.updateUrl({ sort: val, pageCursor: undefined });
|
||||
}
|
||||
|
||||
handleListingTypeChange(val: ListingType) {
|
||||
this.setState({ scrolled: false });
|
||||
this.updateUrl({ listingType: val, page: 1 });
|
||||
this.updateUrl({ listingType: val, pageCursor: undefined });
|
||||
}
|
||||
|
||||
handleDataTypeChange(val: DataType) {
|
||||
this.setState({ scrolled: false });
|
||||
this.updateUrl({ dataType: val, page: 1 });
|
||||
this.updateUrl({ dataType: val, pageCursor: undefined });
|
||||
}
|
||||
|
||||
async handleAddModToCommunity(form: AddModToCommunity) {
|
||||
|
|
Loading…
Reference in a new issue