Infinite piece query
This commit is contained in:
@@ -14,32 +14,27 @@ import clsx from "clsx";
|
||||
import { PieceId, Updater } from "common";
|
||||
import * as Body from "common/Body";
|
||||
import { getMediaTypeForFilename } from "common/MediaType";
|
||||
import { Array, Cause, Effect, Fiber, Iterable, Match, Option, Order, pipe, Predicate, Scope, SortedMap } from "effect";
|
||||
import { Import, Loader2, Plus } from "lucide-react";
|
||||
import { DragEventHandler, FormEventHandler, useId, useRef, useState } from "react";
|
||||
import { Array, Effect, Fiber, Iterable, Match, Option, Order, pipe, Predicate, Scope, SortedMap } from "effect";
|
||||
import { Import, Loader2, Plus, RefreshCwIcon } from "lucide-react";
|
||||
import { DragEventHandler, FormEventHandler, RefObject, useCallback, useId, useImperativeHandle, useRef, useState } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
const LIMIT = 100;
|
||||
|
||||
export function Pieces() {
|
||||
|
||||
const name = useStore(state => state.pieceQueryName);
|
||||
const author = useStore(state => state.pieceQueryAuthor);
|
||||
|
||||
const [importDialogOpen, setImportDialogOpen] = useState(false);
|
||||
const [refresh, setRefresh] = useState(Effect.void);
|
||||
|
||||
const debounce = useRef(Effect.void);
|
||||
const breakpoint = useBreakpoint();
|
||||
const columns = breakpoint ? 4 : 1;
|
||||
|
||||
const { isLoading, error, data: pieceIds, refresh } = useLoading(Effect.gen(function* () {
|
||||
yield* debounce.current;
|
||||
const data = yield* client.queryPieces({
|
||||
name: name !== "" ? Option.some(name) : Option.none(),
|
||||
author: author !== "" ? Option.some(author) : Option.none(),
|
||||
offset: 0,
|
||||
limit: 100,
|
||||
});
|
||||
return data;
|
||||
}), [name, author]);
|
||||
const queryRef = useCallback((ref: Query.Ref | null) => {
|
||||
setRefresh(ref !== null ? ref.refresh : Effect.void);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="p-4 overflow-y-auto flex flex-col gap-4">
|
||||
@@ -93,30 +88,98 @@ export function Pieces() {
|
||||
</TableHeader>
|
||||
)}
|
||||
<TableBody>
|
||||
{isLoading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns} >
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="animate-spin" />
|
||||
Ładowanie…
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : error !== null ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns} className="text-center">
|
||||
{Cause.isUnknownException(error) ? "Wystąpił nieznany błąd" : `Wystąpił błąd: ${JSON.stringify(error)}`}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
pieceIds.map((pieceId) => <PieceRow key={pieceId} pieceId={pieceId} />)
|
||||
)}
|
||||
<Query
|
||||
ref={queryRef}
|
||||
offset={0}
|
||||
debounce={debounce}
|
||||
/>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
namespace Query {
|
||||
export interface Props {
|
||||
readonly offset: number;
|
||||
readonly debounce?: RefObject<Effect.Effect<void>> | undefined;
|
||||
readonly ref?: React.Ref<Ref> | undefined;
|
||||
}
|
||||
|
||||
export interface Ref {
|
||||
readonly refresh: Effect.Effect<void>;
|
||||
}
|
||||
}
|
||||
|
||||
function Query({
|
||||
offset,
|
||||
debounce,
|
||||
ref,
|
||||
}: Query.Props) {
|
||||
|
||||
const name = useStore(state => state.pieceQueryName);
|
||||
const author = useStore(state => state.pieceQueryAuthor);
|
||||
|
||||
const breakpoint = useBreakpoint();
|
||||
const columns = breakpoint ? 4 : 1;
|
||||
|
||||
const [continuationOpen, setContinuationOpen] = useState(false);
|
||||
|
||||
const { isLoading, error, data: pieceIds, refresh } = useLoading(Effect.gen(function* () {
|
||||
setContinuationOpen(false);
|
||||
if (debounce !== undefined) {
|
||||
yield* debounce.current;
|
||||
}
|
||||
|
||||
const data = yield* client.queryPieces({
|
||||
name: name !== "" ? Option.some(name) : Option.none(),
|
||||
author: author !== "" ? Option.some(author) : Option.none(),
|
||||
offset,
|
||||
limit: LIMIT,
|
||||
});
|
||||
return data;
|
||||
}), [name, author, offset]);
|
||||
|
||||
useImperativeHandle(ref, () => Object.freeze<Query.Ref>({
|
||||
refresh,
|
||||
}), [refresh]);
|
||||
|
||||
return isLoading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns} >
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Loader2 className="animate-spin" />
|
||||
Ładowanie…
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : error !== null ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns} className="text-center">
|
||||
Wystąpił błąd: {Match.value(error).pipe(
|
||||
Match.tag("FetchError", () => "Nie można połączyć się z serwerem"),
|
||||
Match.tag("Unauthenticated", () => "Zaloguj się, aby kontynuować"),
|
||||
Match.tag("Unauthorized", () => "Nie posiadasz uprawnień"),
|
||||
Match.exhaustive,
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (<>
|
||||
{pieceIds.map((pieceId) => <PieceRow key={pieceId} pieceId={pieceId} />)}
|
||||
{continuationOpen ? (
|
||||
<Query offset={offset + LIMIT} />
|
||||
) : pieceIds.length >= LIMIT ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns} className="text-center">
|
||||
<Button variant="outline" onClick={() => setContinuationOpen(true)}>
|
||||
<RefreshCwIcon/>Załaduj więcej
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</>);
|
||||
}
|
||||
|
||||
namespace PieceRow {
|
||||
export interface Props {
|
||||
readonly pieceId: PieceId;
|
||||
|
||||
Reference in New Issue
Block a user