h8r4npp3p: Add the new-posts pill, feed list motion and app-wide reduced-motion switch-off

This commit is contained in:
2026-10-19 22:24:38 -06:00
parent f250cdf09c
commit 5e391ae678
10 changed files with 150 additions and 26 deletions
+25 -9
View File
@@ -1,6 +1,7 @@
import type { ReactNode, Ref } from "react";
import { canDelete, type CardPost } from "@/lib/feed-model";
import { Composer, type ComposerProps } from "./Composer";
import { GrowButton } from "./Buttons";
import { PostCard } from "./PostCard";
export type FeedStatus = "loading" | "ready" | "error";
@@ -33,6 +34,24 @@ function Spinner() {
return <span className="spinner" role="status" aria-label="Loading" />;
}
/** Shimmer placeholder card shown while the next page loads. */
function SkeletonCard() {
return (
<div className="feed-skel" role="status" aria-label="Loading more posts" data-testid="feed-skeleton">
<div className="feed-skel-head">
<span className="feed-skel-dot" />
<span className="feed-skel-lines">
<span className="feed-skel-line feed-skel-w40" />
<span className="feed-skel-line feed-skel-w25" />
</span>
</div>
<span className="feed-skel-line feed-skel-w100" />
<span className="feed-skel-line feed-skel-w80" />
<span className="feed-skel-media" />
</div>
);
}
function FeedPanel({ children, composer }: { children: ReactNode; composer?: ReactNode }) {
return (
<section className="tile panel feed" aria-labelledby="h-feed">
@@ -69,16 +88,17 @@ export function FeedView(p: FeedViewProps) {
<FeedPanel composer={p.composer ? <Composer {...p.composer} /> : null}>
<div ref={p.listRef} className="feed-list panel-scroll fade-edges">
{p.newCount > 0 ? (
<button type="button" className="new-posts" onClick={p.onShowNew}>
{p.newCount === 1 ? "1 new post" : `${p.newCount} new posts`}
</button>
<div className="feed-pill">
<GrowButton label={p.newCount === 1 ? "1 new post" : `${p.newCount} new posts`} onClick={p.onShowNew} />
</div>
) : null}
{p.posts.length === 0 ? (
<p className="feed-message">No posts yet.</p>
) : (
p.posts.map((post) => <PostCard
p.posts.map((post, i) => <PostCard
key={post.key}
post={post}
index={i}
now={p.now}
onToggleLike={p.onToggleLike}
likeBusy={p.likeBusy?.has(post.key)}
@@ -96,11 +116,7 @@ export function FeedView(p: FeedViewProps) {
<button type="button" onClick={p.onRetry}>Try again</button>
</div>
) : null}
{p.loadingMore ? (
<div className="feed-center">
<Spinner />
</div>
) : null}
{p.loadingMore ? <SkeletonCard /> : null}
<div ref={p.sentinelRef} className="sentinel" aria-hidden="true" />
</div>
</FeedPanel>