v1 review pass: fixed some minor bugs

This commit is contained in:
khannurien
2026-03-16 11:08:39 +00:00
parent e88fed4e98
commit 867e64cb5b
37 changed files with 1228 additions and 400 deletions

View File

@@ -1,14 +1,17 @@
import { useEffect, useState } from "react";
import { Link, useLocation } from "react-router";
import { API_URL } from "../config/api.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { useWS } from "../hooks/useWS.ts";
import { type Dump } from "../model.ts";
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 { deserializeDump, type Dump } from "../model.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { useWS } from "../hooks/useWS.ts";
type DumpsState =
| { status: "loading" }
| { status: "error"; error: string }
@@ -17,18 +20,29 @@ type DumpsState =
type SortMode = "new" | "hot";
function hotScore(dump: Dump): number {
const ageHours = (Date.now() - new Date(dump.createdAt).getTime()) / 3_600_000;
const ageHours = (Date.now() - dump.createdAt.getTime()) / 3_600_000;
return (dump.voteCount + 1) / Math.pow(ageHours + 2, 1.5);
}
export function Index() {
const location = useLocation();
const justDeletedId = (location.state as { deletedDumpId?: string } | null)?.deletedDumpId;
const justDeletedId = (location.state as { deletedDumpId?: string } | null)
?.deletedDumpId;
const { user } = useAuth();
const { onlineUsers, voteCounts, myVotes, recentDumps, deletedDumpIds, castVote, removeVote } = useWS();
const {
onlineUsers,
voteCounts,
myVotes,
recentDumps,
deletedDumpIds,
castVote,
removeVote,
} = useWS();
const [dumpsState, setDumpsState] = useState<DumpsState>({ status: "loading" });
const [dumpsState, setDumpsState] = useState<DumpsState>({
status: "loading",
});
const [sort, setSort] = useState<SortMode>("hot");
useEffect(() => {
@@ -37,9 +51,15 @@ export function Index() {
const res = await fetch(`${API_URL}/api/dumps/`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.json();
setDumpsState({ status: "loaded", dumps: body.data });
setDumpsState({
status: "loaded",
dumps: body.data.map(deserializeDump),
});
} catch (err) {
setDumpsState({ status: "error", error: err instanceof Error ? err.message : "Failed to load" });
setDumpsState({
status: "error",
error: err instanceof Error ? err.message : "Failed to load",
});
}
})();
}, []);
@@ -54,14 +74,24 @@ export function Index() {
const sortedDumps = [...combined].sort(
sort === "hot"
? (a, b) => hotScore(b) - hotScore(a)
: (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
: (a, b) => b.createdAt.getTime() - a.createdAt.getTime(),
);
const presenceRow = (
<div className="index-presence">
{onlineUsers.map((u) => (
<Link key={u.userId} to={`/users/${u.username}`} title={u.username} className="index-presence-avatar">
<Avatar userId={u.userId} username={u.username} hasAvatar={u.hasAvatar} size={32} />
<Link
key={u.userId}
to={`/users/${u.username}`}
title={u.username}
className="index-presence-avatar"
>
<Avatar
userId={u.userId}
username={u.username}
hasAvatar={u.hasAvatar}
size={32}
/>
</Link>
))}
</div>
@@ -69,19 +99,33 @@ export function Index() {
const sortButtons = !loading && !error && combined.length > 0 && (
<div className="feed-sort">
<button className={`feed-sort-btn${sort === "hot" ? " active" : ""}`} onClick={() => setSort("hot")}>Hot</button>
<button className={`feed-sort-btn${sort === "new" ? " active" : ""}`} onClick={() => setSort("new")}>New</button>
<button
type="button"
className={`feed-sort-btn${sort === "hot" ? " active" : ""}`}
onClick={() => setSort("hot")}
>
Hot
</button>
<button
type="button"
className={`feed-sort-btn${sort === "new" ? " active" : ""}`}
onClick={() => setSort("new")}
>
New
</button>
</div>
);
return (
<div className="index-page">
<AppHeader centerSlot={
<div className="header-center-slot">
{presenceRow}
{sortButtons}
</div>
} />
<AppHeader
centerSlot={
<div className="header-center-slot">
{presenceRow}
{sortButtons}
</div>
}
/>
{/* Shown only on narrow viewports */}
<div className="index-below-header">
@@ -98,7 +142,6 @@ export function Index() {
{!loading && !error && combined.length > 0 && (
<>
<ul className="dump-feed">
{sortedDumps.map((dump) => (
<DumpCard