v3: code quality pass, various bug fixes

This commit is contained in:
khannurien
2026-03-23 07:47:49 +00:00
parent d94a319d96
commit fbbbb43258
44 changed files with 1060 additions and 698 deletions

View File

@@ -2,6 +2,7 @@ import {
useCallback,
useEffect,
useLayoutEffect,
useMemo,
useRef,
useState,
} from "react";
@@ -11,11 +12,12 @@ import { Avatar } from "../components/Avatar.tsx";
import { DumpCard } from "../components/DumpCard.tsx";
import { AppHeader } from "../components/AppHeader.tsx";
import { API_URL } from "../config/api.ts";
import { API_URL, DEFAULT_PAGE_SIZE } from "../config/api.ts";
import {
deserializeDump,
type Dump,
hydrateDump,
type PaginatedData,
type RawDump,
type User,
@@ -29,12 +31,6 @@ import { useWS } from "../hooks/useWS.ts";
import { useDumpListSync } from "../hooks/useDumpListSync.ts";
import { useInfiniteScroll } from "../hooks/useInfiniteScroll.ts";
const PAGE_SIZE = 20;
// After JSON roundtrip, createdAt is a string — re-parse it
const hydrateDump = (raw: Dump): Dump =>
deserializeDump(raw as unknown as RawDump);
type DumpsState =
| { status: "loading" }
| { status: "error"; error: string }
@@ -210,11 +206,13 @@ export function Index() {
useEffect(() => {
if (mainFetchDone.current || cached) return;
mainFetchDone.current = true;
const controller = new AbortController();
(async () => {
try {
const res = await fetch(
`${API_URL}/api/dumps/?page=1&limit=${PAGE_SIZE}`,
`${API_URL}/api/dumps/?page=1&limit=${DEFAULT_PAGE_SIZE}`,
{
signal: controller.signal,
headers: token ? { Authorization: `Bearer ${token}` } : {},
},
);
@@ -229,12 +227,17 @@ export function Index() {
loadingMore: false,
});
} catch (err) {
if ((err as Error).name === "AbortError") return;
setDumpsState({
status: "error",
error: friendlyFetchError(err),
});
}
})();
return () => {
mainFetchDone.current = false;
controller.abort();
};
}, [cached, token]);
// ── Followed feeds fetch (lazy, on first tab open) ──
@@ -252,7 +255,7 @@ export function Index() {
loadingMore: false,
});
} else {
fetch(`${API_URL}/api/follows/feed/users?page=1&limit=${PAGE_SIZE}`, {
fetch(`${API_URL}/api/follows/feed/users?page=1&limit=${DEFAULT_PAGE_SIZE}`, {
headers: { Authorization: `Bearer ${token}` },
})
.then((r) => r.json())
@@ -286,7 +289,7 @@ export function Index() {
});
} else {
fetch(
`${API_URL}/api/follows/feed/playlists?page=1&limit=${PAGE_SIZE}`,
`${API_URL}/api/follows/feed/playlists?page=1&limit=${DEFAULT_PAGE_SIZE}`,
{
headers: { Authorization: `Bearer ${token}` },
},
@@ -312,7 +315,7 @@ export function Index() {
}
}, [
tab,
user?.id,
user,
token,
cachedFollowedUsers,
cachedFollowedPlaylists,
@@ -331,7 +334,7 @@ export function Index() {
setDumpsState((s) =>
s.status === "loaded" ? { ...s, loadingMore: true } : s
);
fetch(`${API_URL}/api/dumps/?page=${nextPage}&limit=${PAGE_SIZE}`, {
fetch(`${API_URL}/api/dumps/?page=${nextPage}&limit=${DEFAULT_PAGE_SIZE}`, {
headers: token ? { Authorization: `Bearer ${token}` } : {},
})
.then((r) => r.json())
@@ -368,7 +371,7 @@ export function Index() {
s.status === "loaded" ? { ...s, loadingMore: true } : s
);
fetch(
`${API_URL}/api/follows/feed/users?page=${nextPage}&limit=${PAGE_SIZE}`,
`${API_URL}/api/follows/feed/users?page=${nextPage}&limit=${DEFAULT_PAGE_SIZE}`,
{
headers: { Authorization: `Bearer ${token}` },
},
@@ -407,7 +410,7 @@ export function Index() {
s.status === "loaded" ? { ...s, loadingMore: true } : s
);
fetch(
`${API_URL}/api/follows/feed/playlists?page=${nextPage}&limit=${PAGE_SIZE}`,
`${API_URL}/api/follows/feed/playlists?page=${nextPage}&limit=${DEFAULT_PAGE_SIZE}`,
{
headers: { Authorization: `Bearer ${token}` },
},
@@ -529,7 +532,7 @@ export function Index() {
const dumps = dumpsState.status === "loaded" ? dumpsState.dumps : [];
const loadingMore = dumpsState.status === "loaded" && dumpsState.loadingMore;
const restIds = new Set(dumps.map((d) => d.id));
const restIds = useMemo(() => new Set(dumps.map((d) => d.id)), [dumps]);
const combined = [...recentDumps.filter((d) => !restIds.has(d.id)), ...dumps]
.filter((d) => !deletedDumpIds.has(d.id) && d.id !== justDeletedId);