From 984fc2d869f19eac0ff5a8ae8f0c64a4e69d667b Mon Sep 17 00:00:00 2001 From: Chad Curtis Date: Tue, 3 Mar 2026 01:42:57 -0600 Subject: [PATCH] Unify following-tab empty states with shared FeedEmptyState component --- src/components/Feed.tsx | 20 ++++++++++++----- src/components/FeedEmptyState.tsx | 36 +++++++++++++++++++++++++++++++ src/pages/EventsFeedPage.tsx | 22 ++++++++----------- src/pages/PhotosFeedPage.tsx | 21 +++++++++--------- src/pages/VideosFeedPage.tsx | 15 +++++++------ src/pages/VinesFeedPage.tsx | 26 ++++++++-------------- 6 files changed, 88 insertions(+), 52 deletions(-) create mode 100644 src/components/FeedEmptyState.tsx diff --git a/src/components/Feed.tsx b/src/components/Feed.tsx index 5cc31e5e..bed6ad8a 100644 --- a/src/components/Feed.tsx +++ b/src/components/Feed.tsx @@ -4,6 +4,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { ComposeBox } from '@/components/ComposeBox'; import { NoteCard } from '@/components/NoteCard'; import { PullToRefresh } from '@/components/PullToRefresh'; +import { FeedEmptyState } from '@/components/FeedEmptyState'; import { Skeleton } from '@/components/ui/skeleton'; import { Button } from '@/components/ui/button'; import { Loader2 } from 'lucide-react'; @@ -234,11 +235,20 @@ export function Feed({ kinds, tagFilters, header, hideCompose, emptyMessage }: F )} ) : ( -
-

- {emptyMessage ?? 'No posts yet. Follow some people or switch to the Global tab to discover content.'} -

-
+ setActiveTab('global') + : undefined + } + /> )} diff --git a/src/components/FeedEmptyState.tsx b/src/components/FeedEmptyState.tsx new file mode 100644 index 00000000..65ab45a7 --- /dev/null +++ b/src/components/FeedEmptyState.tsx @@ -0,0 +1,36 @@ +import { cn } from '@/lib/utils'; + +interface FeedEmptyStateProps { + /** Primary empty-state message. */ + message: string; + /** Called when the user clicks "Switch to Global". Omit to hide the button. */ + onSwitchToGlobal?: () => void; + className?: string; +} + +/** + * Consistent empty state for Follows/Global feed tabs across all feed pages. + * + * - Follows tab: pass `onSwitchToGlobal` to render a "Switch to Global" CTA. + * - Global tab: omit `onSwitchToGlobal`; the message should guide the user + * to check their relay connections. + */ +export function FeedEmptyState({ + message, + onSwitchToGlobal, + className, +}: FeedEmptyStateProps) { + return ( +
+

{message}

+ {onSwitchToGlobal && ( + + )} +
+ ); +} diff --git a/src/pages/EventsFeedPage.tsx b/src/pages/EventsFeedPage.tsx index 741e4926..bf959f30 100644 --- a/src/pages/EventsFeedPage.tsx +++ b/src/pages/EventsFeedPage.tsx @@ -3,6 +3,7 @@ import { Link } from 'react-router-dom'; import { useInView } from 'react-intersection-observer'; import { useQueryClient } from '@tanstack/react-query'; import { ArrowLeft, CalendarDays, Loader2 } from 'lucide-react'; +import { FeedEmptyState } from '@/components/FeedEmptyState'; import { useSeoMeta } from '@unhead/react'; import type { NostrEvent } from '@nostrify/nostrify'; @@ -151,19 +152,14 @@ export function EventsFeedPage() { )} ) : ( -
- -

- {activeTab === 'follows' - ? 'No events from people you follow yet. Try the Global tab.' - : 'No calendar events found. Check your relay connections or try again later.'} -

- {activeTab === 'follows' && ( - - )} -
+ setActiveTab('global') : undefined} + /> )} diff --git a/src/pages/PhotosFeedPage.tsx b/src/pages/PhotosFeedPage.tsx index 6cd6871a..cbde4e18 100644 --- a/src/pages/PhotosFeedPage.tsx +++ b/src/pages/PhotosFeedPage.tsx @@ -8,10 +8,10 @@ import { useState, useEffect, useMemo } from 'react'; import { Link } from 'react-router-dom'; -import { ArrowLeft, Camera } from 'lucide-react'; +import { ArrowLeft, Camera, Loader2 } from 'lucide-react'; import { useSeoMeta } from '@unhead/react'; import { useInView } from 'react-intersection-observer'; -import { Loader2 } from 'lucide-react'; +import { FeedEmptyState } from '@/components/FeedEmptyState'; import type { NostrEvent } from '@nostrify/nostrify'; import { useAppContext } from '@/hooks/useAppContext'; import { useCurrentUser } from '@/hooks/useCurrentUser'; @@ -133,15 +133,14 @@ export function PhotosFeedPage() { {showSkeleton ? ( ) : photoEvents.length === 0 ? ( -
- -

- No photos yet. - {activeTab === 'follows' - ? ' Follow some photographers or switch to Global.' - : ' Check your relay connections or come back soon.'} -

-
+ setActiveTab('global') : undefined} + /> ) : ( <> ) : videoEvents.length === 0 ? ( -
- -

- No videos yet.{feedTab === 'follows' ? ' Follow some creators or switch to Global.' : ' Check your relay connections or come back soon.'} -

-
+ setFeedTab('global') : undefined} + /> ) : (
{/* Normal videos — 2-column grid */} diff --git a/src/pages/VinesFeedPage.tsx b/src/pages/VinesFeedPage.tsx index d3383813..8f1b45f8 100644 --- a/src/pages/VinesFeedPage.tsx +++ b/src/pages/VinesFeedPage.tsx @@ -16,8 +16,8 @@ import { MoreHorizontal, Play, Heart, - } from 'lucide-react'; +import { FeedEmptyState } from '@/components/FeedEmptyState'; import type { NostrEvent } from '@nostrify/nostrify'; import { useNostr } from '@nostrify/react'; @@ -726,22 +726,14 @@ export function VinesFeedPage() {
-
-

No vines yet

-

- {tab === 'follows' - ? 'None of the people you follow have posted vines. Try Global.' - : 'Short-form videos will appear here. Check back soon!'} -

- {tab === 'follows' && ( - - )} -
+ setTab('global') : undefined} + />
);