Files
gerbeur/src/pages/UserDumps.tsx
khannurien b822f861ed
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 46s
v3: lots of small UI tweaks and fixes
2026-04-11 08:35:16 +00:00

131 lines
3.7 KiB
TypeScript

import { useState } from "react";
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { Link, useParams } from "react-router";
import { useAuth } from "../hooks/useAuth.ts";
import { useWS } from "../hooks/useWS.ts";
import { useDumpListSync } from "../hooks/useDumpListSync.ts";
import { usePositionAwareSync } from "../hooks/usePositionAwareSync.ts";
import { useUserDumpFeed } from "../hooks/useUserDumpFeed.ts";
import { DumpCard } from "../components/DumpCard.tsx";
import { DumpCreateModal } from "../components/DumpCreateModal.tsx";
import { ProfileSubpageHeader } from "../components/ProfileSubpageHeader.tsx";
import { PageShell } from "../components/PageShell.tsx";
import { PageError } from "../components/PageError.tsx";
export function UserDumps() {
const { username } = useParams();
const { user: me } = useAuth();
const { voteCounts, myVotes, lastDumpEvent, castVote, removeVote } = useWS();
const { state, setItems, sentinelRef } = useUserDumpFeed(
username,
"dumps",
`feed:user-dumps-full:${username ?? ""}`,
);
const [createModalOpen, setCreateModalOpen] = useState(false);
const profileUserId = state.status === "loaded" ? state.profileUser.id : null;
const isOwnProfile = me?.id === profileUserId;
const dumpItems = state.status === "loaded" ? state.items : [];
usePositionAwareSync(
dumpItems,
setItems,
lastDumpEvent,
(d) => d.isPrivate,
(d) => !d.isPrivate && d.userId === profileUserId,
);
useDumpListSync(setItems, {
ownerId: profileUserId ?? undefined,
isOwner: isOwnProfile,
skipReinsert: true,
});
if (state.status === "loading") {
return (
<PageShell>
<p className="page-loading">
<Trans>Loading</Trans>
</p>
</PageShell>
);
}
if (state.status === "error") {
return (
<PageError
message={state.error}
actions={
<Link to={`/users/${username}`} className="btn-border">
<Trans> Back to profile</Trans>
</Link>
}
/>
);
}
const { profileUser, items: dumps, hasMore, loadingMore } = state;
return (
<PageShell
feed={dumps.length > 0 && (
<>
<ul className="dump-feed">
{dumps.map((dump) => (
<DumpCard
key={dump.id}
dump={dump}
voteCount={voteCounts[dump.id] ?? dump.voteCount}
voted={myVotes.has(dump.id)}
canVote={!!me}
castVote={castVote}
removeVote={removeVote}
isOwner={isOwnProfile}
/>
))}
</ul>
{hasMore && <div ref={sentinelRef} />}
{loadingMore && (
<p className="feed-loading-more">
<Trans>Loading more</Trans>
</p>
)}
{!hasMore && (
<p className="feed-end">
<Trans>You've reached the end.</Trans>
</p>
)}
</>
)}
>
<ProfileSubpageHeader
username={username!}
profileUser={profileUser}
title={t`Dumps`}
actions={isOwnProfile && (
<button
type="button"
className="new-playlist-toggle"
onClick={() => setCreateModalOpen(true)}
>
+<span className="btn-new-label">
<Trans>New dump</Trans>
</span>
</button>
)}
/>
{createModalOpen && (
<DumpCreateModal onClose={() => setCreateModalOpen(false)} />
)}
{dumps.length === 0 && (
<p className="empty-state">
<Trans>Nothing here yet.</Trans>
</p>
)}
</PageShell>
);
}