95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
"use client";
|
|
|
|
// Dev-only: the real signed-in shell with fixture data and no network calls.
|
|
// Likes, search and the composer work on local state so the panels can be
|
|
// checked by hand. Used by src/app/dev/feed/page.tsx.
|
|
import { useEffect, useMemo, useState } from "react";
|
|
import { AppShell } from "@/components/AppShell";
|
|
import { DiscoverRailsView } from "@/components/DiscoverRails";
|
|
import { ExpertSearchView } from "@/components/ExpertSearch";
|
|
import { FeedView } from "@/components/FeedView";
|
|
import { toComments } from "@/lib/comment-model";
|
|
import { toCardPosts, type CardPost } from "@/lib/feed-model";
|
|
import {
|
|
FIXTURE_CATEGORIES,
|
|
FIXTURE_COMMENTS,
|
|
FIXTURE_DISCOVER,
|
|
FIXTURE_FEED_ITEMS,
|
|
FIXTURE_NOW,
|
|
FIXTURE_PERSON,
|
|
FIXTURE_SEARCH_RESULTS,
|
|
} from "./fixtures";
|
|
|
|
export function FeedPreview() {
|
|
const [posts, setPosts] = useState<CardPost[]>(() => toCardPosts(FIXTURE_FEED_ITEMS));
|
|
const [draft, setDraft] = useState("");
|
|
const [query, setQuery] = useState("");
|
|
const [category, setCategory] = useState<string | null>(null);
|
|
|
|
// The comments calls answer from the fixtures; the preview never reaches the network.
|
|
useEffect(() => {
|
|
const real = window.fetch;
|
|
const lists: Record<string, unknown[]> = { ...FIXTURE_COMMENTS };
|
|
window.fetch = async (input, init) => {
|
|
const m = /\/api\/posts\/([^/]+)\/comments/.exec(String(input));
|
|
if (!m) return real(input, init);
|
|
const id = decodeURIComponent(m[1]);
|
|
if (init?.method === "POST") {
|
|
const { content } = JSON.parse(String(init.body)) as { content: string };
|
|
const made = { id: `local-${Date.now()}`, content, createdAt: new Date(FIXTURE_NOW).toISOString(), author: { displayName: FIXTURE_PERSON.name } };
|
|
lists[id] = [...(lists[id] ?? []), made];
|
|
return Response.json({ comment: toComments([made])[0] });
|
|
}
|
|
return Response.json({ comments: toComments(lists[id] ?? []) });
|
|
};
|
|
return () => { window.fetch = real; };
|
|
}, []);
|
|
|
|
// With no query or category, show every fixture result so the list scrolls.
|
|
const results = useMemo(() => {
|
|
const q = query.trim().toLowerCase();
|
|
return q ? FIXTURE_SEARCH_RESULTS.filter((e) => e.displayName.toLowerCase().includes(q)) : FIXTURE_SEARCH_RESULTS;
|
|
}, [query]);
|
|
|
|
const toggleLike = (post: CardPost) =>
|
|
setPosts((cur) =>
|
|
cur.map((p) =>
|
|
p.key === post.key ? { ...p, isLiked: !p.isLiked, likesCount: p.likesCount + (p.isLiked ? -1 : 1) } : p,
|
|
),
|
|
);
|
|
|
|
return (
|
|
<AppShell
|
|
person={FIXTURE_PERSON}
|
|
search={
|
|
<ExpertSearchView
|
|
query={query}
|
|
onQuery={setQuery}
|
|
categories={FIXTURE_CATEGORIES}
|
|
activeCategory={category}
|
|
onCategory={setCategory}
|
|
status="ready"
|
|
results={results}
|
|
/>
|
|
}
|
|
feed={
|
|
<FeedView
|
|
posts={posts}
|
|
status="ready"
|
|
loadingMore={false}
|
|
loadMoreFailed={false}
|
|
newCount={0}
|
|
onRetry={() => {}}
|
|
onShowNew={() => {}}
|
|
onToggleLike={toggleLike}
|
|
feedUserId={FIXTURE_PERSON.feedUserId}
|
|
onDelete={(post) => setPosts((cur) => cur.filter((p) => p.key !== post.key))}
|
|
composer={{ value: draft, busy: false, error: false, onChange: setDraft, onSubmit: () => setDraft("") }}
|
|
now={FIXTURE_NOW}
|
|
/>
|
|
}
|
|
discover={<DiscoverRailsView data={FIXTURE_DISCOVER} />}
|
|
/>
|
|
);
|
|
}
|