mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-01 23:10:01 +00:00
114 lines
2.9 KiB
TypeScript
114 lines
2.9 KiB
TypeScript
|
/*
|
||
|
GoToSocial
|
||
|
Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||
|
|
||
|
This program is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU Affero General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU Affero General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Affero General Public License
|
||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
import React, { ReactNode } from "react";
|
||
|
import { useLocation } from "wouter";
|
||
|
import { Error } from "./error";
|
||
|
import { SerializedError } from "@reduxjs/toolkit";
|
||
|
import { FetchBaseQueryError } from "@reduxjs/toolkit/query";
|
||
|
import { Links } from "parse-link-header";
|
||
|
import Loading from "./loading";
|
||
|
|
||
|
export interface PageableListProps<T> {
|
||
|
isSuccess: boolean;
|
||
|
items?: T[];
|
||
|
itemToEntry: (_item: T) => ReactNode;
|
||
|
isLoading: boolean;
|
||
|
isFetching: boolean;
|
||
|
isError: boolean;
|
||
|
error: FetchBaseQueryError | SerializedError | undefined;
|
||
|
emptyMessage: string;
|
||
|
prevNextLinks?: Links | null | undefined;
|
||
|
}
|
||
|
|
||
|
export function PageableList<T>({
|
||
|
isLoading,
|
||
|
isFetching,
|
||
|
isSuccess,
|
||
|
items,
|
||
|
itemToEntry,
|
||
|
isError,
|
||
|
error,
|
||
|
emptyMessage,
|
||
|
prevNextLinks,
|
||
|
}: PageableListProps<T>) {
|
||
|
const [ location, setLocation ] = useLocation();
|
||
|
|
||
|
if (!(isSuccess || isError)) {
|
||
|
// Hasn't been called yet.
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if (isLoading || isFetching) {
|
||
|
return <Loading />;
|
||
|
}
|
||
|
|
||
|
if (error) {
|
||
|
return <Error error={error} />;
|
||
|
}
|
||
|
|
||
|
// Map response to items if possible.
|
||
|
let content: ReactNode;
|
||
|
if (items == undefined || items.length == 0) {
|
||
|
content = <b>{emptyMessage}</b>;
|
||
|
} else {
|
||
|
content = (
|
||
|
<div className="entries">
|
||
|
{items.map(item => itemToEntry(item))}
|
||
|
</div>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// If it's possible to page to next and previous
|
||
|
// pages, instantiate button handlers for this.
|
||
|
let prevClick: (() => void) | undefined;
|
||
|
let nextClick: (() => void) | undefined;
|
||
|
if (prevNextLinks) {
|
||
|
const prev = prevNextLinks["prev"];
|
||
|
if (prev) {
|
||
|
const prevUrl = new URL(prev.url);
|
||
|
const prevParams = prevUrl.search;
|
||
|
prevClick = () => {
|
||
|
setLocation(location + prevParams.toString());
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const next = prevNextLinks["next"];
|
||
|
if (next) {
|
||
|
const nextUrl = new URL(next.url);
|
||
|
const nextParams = nextUrl.search;
|
||
|
nextClick = () => {
|
||
|
setLocation(location + nextParams.toString());
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
<div className="list pageable-list">
|
||
|
{ content }
|
||
|
{ prevNextLinks &&
|
||
|
<div className="prev-next">
|
||
|
{ prevClick && <button onClick={prevClick}>Previous page</button> }
|
||
|
{ nextClick && <button onClick={nextClick}>Next page</button> }
|
||
|
</div>
|
||
|
}
|
||
|
</div>
|
||
|
);
|
||
|
}
|