Unify following-tab empty states with shared FeedEmptyState component
This commit is contained in:
+15
-5
@@ -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
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-16 px-8 text-center">
|
||||
<p className="text-muted-foreground text-lg">
|
||||
{emptyMessage ?? 'No posts yet. Follow some people or switch to the Global tab to discover content.'}
|
||||
</p>
|
||||
</div>
|
||||
<FeedEmptyState
|
||||
message={
|
||||
emptyMessage ?? (
|
||||
activeTab === 'follows'
|
||||
? 'No posts yet. Follow some people to see their content here.'
|
||||
: 'No posts found. Check your relay connections or come back soon.'
|
||||
)
|
||||
}
|
||||
onSwitchToGlobal={
|
||||
activeTab === 'follows' && showGlobalFeed
|
||||
? () => setActiveTab('global')
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</PullToRefresh>
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<div className={cn('py-16 px-8 text-center space-y-3', className)}>
|
||||
<p className="text-muted-foreground">{message}</p>
|
||||
{onSwitchToGlobal && (
|
||||
<button
|
||||
className="text-sm text-primary hover:underline"
|
||||
onClick={onSwitchToGlobal}
|
||||
>
|
||||
Switch to Global
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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() {
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="py-16 px-8 text-center space-y-3">
|
||||
<CalendarDays className="size-10 text-muted-foreground/40 mx-auto" />
|
||||
<p className="text-muted-foreground">
|
||||
{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.'}
|
||||
</p>
|
||||
{activeTab === 'follows' && (
|
||||
<button className="text-sm text-primary hover:underline" onClick={() => setActiveTab('global')}>
|
||||
Switch to Global
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<FeedEmptyState
|
||||
message={
|
||||
activeTab === 'follows'
|
||||
? 'No events from people you follow yet.'
|
||||
: 'No calendar events found. Check your relay connections or try again later.'
|
||||
}
|
||||
onSwitchToGlobal={activeTab === 'follows' ? () => setActiveTab('global') : undefined}
|
||||
/>
|
||||
)}
|
||||
</PullToRefresh>
|
||||
</main>
|
||||
|
||||
@@ -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 ? (
|
||||
<MediaGridSkeleton count={15} />
|
||||
) : photoEvents.length === 0 ? (
|
||||
<div className="py-16 px-8 text-center">
|
||||
<Camera className="size-10 text-muted-foreground/30 mx-auto mb-3" />
|
||||
<p className="text-muted-foreground">
|
||||
No photos yet.
|
||||
{activeTab === 'follows'
|
||||
? ' Follow some photographers or switch to Global.'
|
||||
: ' Check your relay connections or come back soon.'}
|
||||
</p>
|
||||
</div>
|
||||
<FeedEmptyState
|
||||
message={
|
||||
activeTab === 'follows'
|
||||
? 'No photos yet. Follow some photographers to see their photos here.'
|
||||
: 'No photos found. Check your relay connections or come back soon.'
|
||||
}
|
||||
onSwitchToGlobal={activeTab === 'follows' ? () => setActiveTab('global') : undefined}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<MediaGrid
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ArrowLeft, Film, Radio, Play, Eye } from 'lucide-react';
|
||||
import { FeedEmptyState } from '@/components/FeedEmptyState';
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import { Blurhash } from 'react-blurhash';
|
||||
@@ -676,12 +677,14 @@ export function VideosFeedPage() {
|
||||
</div>
|
||||
</div>
|
||||
) : videoEvents.length === 0 ? (
|
||||
<div className="py-16 px-8 text-center">
|
||||
|
||||
<p className="text-muted-foreground">
|
||||
No videos yet.{feedTab === 'follows' ? ' Follow some creators or switch to Global.' : ' Check your relay connections or come back soon.'}
|
||||
</p>
|
||||
</div>
|
||||
<FeedEmptyState
|
||||
message={
|
||||
feedTab === 'follows'
|
||||
? 'No videos yet. Follow some creators to see their videos here.'
|
||||
: 'No videos found. Check your relay connections or come back soon.'
|
||||
}
|
||||
onSwitchToGlobal={feedTab === 'follows' ? () => setFeedTab('global') : undefined}
|
||||
/>
|
||||
) : (
|
||||
<div className="pt-3 pb-8">
|
||||
{/* Normal videos — 2-column grid */}
|
||||
|
||||
@@ -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() {
|
||||
<div className="flex-1 min-w-0 flex flex-col">
|
||||
<VinesTabBar tab={tab} onTabChange={setTab} hasUser={!!user} />
|
||||
<div className="flex-1 flex items-center justify-center">
|
||||
<div className="text-center space-y-3 px-8">
|
||||
<p className="text-lg font-semibold">No vines yet</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{tab === 'follows'
|
||||
? 'None of the people you follow have posted vines. Try Global.'
|
||||
: 'Short-form videos will appear here. Check back soon!'}
|
||||
</p>
|
||||
{tab === 'follows' && (
|
||||
<button
|
||||
className="text-sm text-primary hover:underline"
|
||||
onClick={() => setTab('global')}
|
||||
>
|
||||
Switch to Global
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<FeedEmptyState
|
||||
message={
|
||||
tab === 'follows'
|
||||
? 'None of the people you follow have posted vines yet.'
|
||||
: 'No vines found. Check your relay connections or come back soon.'
|
||||
}
|
||||
onSwitchToGlobal={tab === 'follows' ? () => setTab('global') : undefined}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user