v3: search engine, responsive header with compact user menu

This commit is contained in:
khannurien
2026-03-29 11:56:31 +00:00
parent f0f6472db6
commit cbb3505139
31 changed files with 1206 additions and 178 deletions

View File

@@ -6,10 +6,12 @@ import {
useRef,
useState,
} from "react";
import { Link, useLocation, useNavigate } from "react-router";
import { useLocation } from "react-router";
import { Avatar } from "../components/Avatar.tsx";
import { AppHeader } from "../components/AppHeader.tsx";
import { SearchBar } from "../components/SearchBar.tsx";
import { PresenceRow } from "../components/PresenceRow.tsx";
import { FeedTabBar, type FeedTab, VALID_TABS } from "../components/FeedTabBar.tsx";
import { API_URL, DEFAULT_PAGE_SIZE } from "../config/api.ts";
@@ -46,19 +48,13 @@ type DumpsState =
loadingMore: boolean;
};
type FeedTab = "hot" | "new" | "journal" | "followed";
const VALID_TABS = new Set<string>(["hot", "new", "journal", "followed"]);
export function Index() {
const location = useLocation();
const navigate = useNavigate();
const justDeletedId = (location.state as { deletedDumpId?: string } | null)
?.deletedDumpId;
const { user, token } = useAuth();
const {
onlineUsers,
voteCounts,
myVotes,
recentDumps,
@@ -89,10 +85,6 @@ export function Index() {
const rawTab = new URLSearchParams(location.search).get("tab") ?? "hot";
const tab: FeedTab = VALID_TABS.has(rawTab) ? rawTab as FeedTab : "hot";
function setTab(t: FeedTab) {
navigate(`/?tab=${t}`, { replace: true });
}
// ── Main feed fetch ──
useEffect(() => {
@@ -237,77 +229,22 @@ export function Index() {
// ── Render ──
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}
version={u.avatarVersion}
/>
</Link>
))}
</div>
);
const tabBar = (
<div className="feed-sort">
<button
type="button"
className={`feed-sort-btn${tab === "hot" ? " active" : ""}`}
onClick={() => setTab("hot")}
>
Hot
</button>
<button
type="button"
className={`feed-sort-btn${tab === "new" ? " active" : ""}`}
onClick={() => setTab("new")}
>
New
</button>
<button
type="button"
className={`feed-sort-btn${tab === "journal" ? " active" : ""}`}
onClick={() => setTab("journal")}
>
Journal
</button>
{user && (
<button
type="button"
className={`feed-sort-btn${tab === "followed" ? " active" : ""}`}
onClick={() => setTab("followed")}
>
Followed
</button>
)}
</div>
);
return (
<div className="index-page">
<AppHeader
centerSlot={
<div className="header-center-slot">
{presenceRow}
{tabBar}
<PresenceRow />
<FeedTabBar />
<SearchBar collapsible />
</div>
}
disableNew={dumpsState.status === "error"}
/>
<div className="index-below-header">
{tabBar}
{presenceRow}
<FeedTabBar />
<PresenceRow />
</div>
{tab === "hot" && <HotFeed {...mainFeedProps} />}