le8i2digy: Add a refresh button to the Home feed header and singularize one comment
This commit is contained in:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user