le8i2digy: Add a refresh button to the Home feed header and singularize one comment

This commit is contained in:
2026-10-20 08:08:38 -06:00
parent 3f0595ad55
commit 69a11436f5
7 changed files with 123 additions and 11 deletions
+41 -5
View File
@@ -1,7 +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 { GrowButton, IconButton } from "./Buttons";
import { PostCard } from "./PostCard";
export type FeedStatus = "loading" | "ready" | "error";
@@ -14,6 +14,9 @@ export interface FeedViewProps {
newCount: number;
onRetry: () => void;
onShowNew: () => void;
/** Reloads the first page; the header shows the button when it is given. */
onRefresh?: () => void;
refreshing?: boolean;
onToggleLike?: (post: CardPost) => void;
likeBusy?: ReadonlySet<string>;
likeErrors?: ReadonlySet<string>;
@@ -52,11 +55,44 @@ function SkeletonCard() {
);
}
function FeedPanel({ children, composer }: { children: ReactNode; composer?: ReactNode }) {
function RefreshIcon({ spinning }: { spinning: boolean }) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
className={spinning ? "refresh-icon refresh-icon-spin" : "refresh-icon"}
>
<path d="M21 12a9 9 0 1 1-2.64-6.36" />
<path d="M21 3v6h-6" />
</svg>
);
}
export function RefreshButton({ onRefresh, busy }: { onRefresh: () => void; busy?: boolean }) {
return (
<IconButton
label="Refresh feed"
shape="round"
size="sm"
icon={<RefreshIcon spinning={!!busy} />}
disabled={busy}
aria-busy={busy || undefined}
onClick={onRefresh}
/>
);
}
function FeedPanel({ children, composer, onRefresh, busy }: { children: ReactNode; composer?: ReactNode; onRefresh?: () => void; busy?: boolean }) {
return (
<section className="tile panel feed" aria-labelledby="h-feed">
<div className="panel-head feed-head">
<h2 id="h-feed" className="type-title type-title-strong">Home feed</h2>
{onRefresh ? <RefreshButton onRefresh={onRefresh} busy={busy} /> : null}
</div>
{composer}
{children}
@@ -67,7 +103,7 @@ function FeedPanel({ children, composer }: { children: ReactNode; composer?: Rea
export function FeedView(p: FeedViewProps) {
if (p.status === "loading") {
return (
<FeedPanel>
<FeedPanel onRefresh={p.onRefresh} busy>
<div className="feed-center">
<Spinner />
</div>
@@ -76,7 +112,7 @@ export function FeedView(p: FeedViewProps) {
}
if (p.status === "error") {
return (
<FeedPanel>
<FeedPanel onRefresh={p.onRefresh}>
<div className="feed-message" role="alert">
<p>The feed did not load.</p>
<button type="button" onClick={p.onRetry}>Try again</button>
@@ -85,7 +121,7 @@ export function FeedView(p: FeedViewProps) {
);
}
return (
<FeedPanel composer={p.composer ? <Composer {...p.composer} /> : null}>
<FeedPanel composer={p.composer ? <Composer {...p.composer} /> : null} onRefresh={p.onRefresh} busy={p.refreshing}>
<div ref={p.listRef} className="feed-list panel-scroll fade-edges">
{p.newCount > 0 ? (
<div className="feed-pill">