You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
6.5 KiB
186 lines
6.5 KiB
import NoteList, { TNoteListRef } from '@/components/NoteList' |
|
import Tabs from '@/components/Tabs' |
|
import logger from '@/lib/logger' |
|
import { useKindFilter } from '@/providers/KindFilterProvider' |
|
import { useUserTrust } from '@/providers/UserTrustProvider' |
|
import storage from '@/services/local-storage.service' |
|
import { TFeedSubRequest, TNoteListMode } from '@/types' |
|
import { forwardRef, useMemo, useRef, useState, useEffect } from 'react' |
|
import KindFilter from '../KindFilter' |
|
import { RefreshButton } from '../RefreshButton' |
|
import RssFeedList from '../RssFeedList' |
|
import { useNostr } from '@/providers/NostrProvider' |
|
import rssFeedService from '@/services/rss-feed.service' |
|
import { DEFAULT_RSS_FEEDS } from '@/constants' |
|
|
|
const NormalFeed = forwardRef<TNoteListRef, { |
|
subRequests: TFeedSubRequest[] |
|
areAlgoRelays?: boolean |
|
isMainFeed?: boolean |
|
showRelayCloseReason?: boolean |
|
}>(function NormalFeed({ |
|
subRequests, |
|
areAlgoRelays = false, |
|
isMainFeed = false, |
|
showRelayCloseReason = false |
|
}, ref) { |
|
logger.debug('NormalFeed component rendering with:', { subRequests, areAlgoRelays, isMainFeed }) |
|
const { hideUntrustedNotes } = useUserTrust() |
|
const { showKinds } = useKindFilter() |
|
const [temporaryShowKinds, setTemporaryShowKinds] = useState(showKinds) |
|
const [listMode, setListMode] = useState<TNoteListMode>(() => { |
|
// For main feed, always default to 'posts' (Notes tab) to show the main content |
|
// Only use stored mode for non-main feeds |
|
if (isMainFeed) { |
|
return 'posts' |
|
} |
|
const storedMode = storage.getNoteListMode() |
|
return storedMode || 'posts' |
|
}) |
|
const internalNoteListRef = useRef<TNoteListRef>(null) |
|
const noteListRef = ref || internalNoteListRef |
|
const [showRssFeed, setShowRssFeed] = useState(() => storage.getShowRssFeed()) |
|
const [activeTab, setActiveTab] = useState<string>(listMode) |
|
const [rssRefreshKey, setRssRefreshKey] = useState(0) |
|
const { pubkey, rssFeedListEvent } = useNostr() |
|
|
|
// Sync activeTab with listMode when listMode changes (but not when switching to RSS) |
|
useEffect(() => { |
|
if (activeTab !== 'rss' && activeTab !== listMode) { |
|
setActiveTab(listMode) |
|
} |
|
}, [listMode, activeTab]) |
|
|
|
// Check showRssFeed setting on mount |
|
useEffect(() => { |
|
const currentShowRssFeed = storage.getShowRssFeed() |
|
setShowRssFeed(currentShowRssFeed) |
|
}, []) |
|
|
|
// Handle RSS tab visibility when showRssFeed changes |
|
useEffect(() => { |
|
// If RSS tab is hidden while it's active, switch to posts |
|
if (!showRssFeed && activeTab === 'rss') { |
|
setActiveTab('posts') |
|
setListMode('posts') |
|
} |
|
}, [showRssFeed, activeTab]) |
|
|
|
const handleListModeChange = (mode: TNoteListMode | string) => { |
|
if (mode === 'rss') { |
|
setActiveTab('rss') |
|
return |
|
} |
|
const noteListMode = mode as TNoteListMode |
|
setListMode(noteListMode) |
|
setActiveTab(noteListMode) |
|
if (isMainFeed) { |
|
storage.setNoteListMode(noteListMode) |
|
} |
|
if (noteListRef && typeof noteListRef !== 'function') { |
|
noteListRef.current?.scrollToTop('smooth') |
|
} |
|
} |
|
|
|
const handleShowKindsChange = (newShowKinds: number[]) => { |
|
setTemporaryShowKinds(newShowKinds) |
|
if (noteListRef && typeof noteListRef !== 'function') { |
|
noteListRef.current?.scrollToTop() |
|
} |
|
} |
|
|
|
// Build tabs array conditionally |
|
const tabs = useMemo(() => { |
|
const baseTabs = [ |
|
{ value: 'posts', label: 'Notes' }, |
|
{ value: 'postsAndReplies', label: 'Replies' } |
|
] |
|
|
|
if (showRssFeed) { |
|
baseTabs.push({ value: 'rss', label: 'RSS' }) |
|
} |
|
|
|
return baseTabs |
|
}, [showRssFeed]) |
|
|
|
// Determine current tab value |
|
const currentTabValue = activeTab |
|
|
|
return ( |
|
<> |
|
<Tabs |
|
value={currentTabValue} |
|
tabs={tabs} |
|
onTabChange={(tab) => { |
|
handleListModeChange(tab) |
|
}} |
|
options={ |
|
<> |
|
<RefreshButton onClick={() => { |
|
if (activeTab === 'rss') { |
|
// Refresh RSS feeds |
|
// Get feed URLs from event or use default |
|
let feedUrls: string[] = DEFAULT_RSS_FEEDS |
|
if (pubkey && rssFeedListEvent) { |
|
try { |
|
const urls = rssFeedListEvent.tags |
|
.filter(tag => tag[0] === 'u' && tag[1]) |
|
.map(tag => tag[1] as string) |
|
.filter((url): url is string => { |
|
if (typeof url !== 'string') return false |
|
const trimmed = url.trim() |
|
return trimmed.length > 0 |
|
}) |
|
if (urls.length > 0) { |
|
feedUrls = urls |
|
} |
|
} catch (e) { |
|
// Use default feeds on error |
|
} |
|
} |
|
|
|
// Trigger background refresh and UI update |
|
logger.info('[NormalFeed] Manual refresh: triggering RSS background refresh', { feedCount: feedUrls.length }) |
|
// Start background refresh (don't wait for it) |
|
rssFeedService.backgroundRefreshFeeds(feedUrls).catch(err => { |
|
logger.error('[NormalFeed] Manual refresh: background refresh failed', { error: err }) |
|
}) |
|
// Immediately trigger UI update (will show cached items, then update when background refresh completes) |
|
if (pubkey) { |
|
window.dispatchEvent(new CustomEvent('rssFeedListUpdated', { |
|
detail: { pubkey, feedUrls, eventId: 'manual-refresh' } |
|
})) |
|
} |
|
// Also force re-render by updating key |
|
setRssRefreshKey(prev => prev + 1) |
|
} else { |
|
// Refresh Notes/Replies |
|
if (noteListRef && typeof noteListRef !== 'function') { |
|
noteListRef.current?.refresh() |
|
} |
|
} |
|
}} /> |
|
{activeTab !== 'rss' && ( |
|
<KindFilter showKinds={temporaryShowKinds} onShowKindsChange={handleShowKindsChange} /> |
|
)} |
|
</> |
|
} |
|
/> |
|
{activeTab === 'rss' ? ( |
|
<RssFeedList key={rssRefreshKey} /> |
|
) : ( |
|
<NoteList |
|
ref={noteListRef} |
|
showKinds={temporaryShowKinds} |
|
subRequests={subRequests} |
|
hideReplies={listMode === 'posts'} |
|
hideUntrustedNotes={hideUntrustedNotes} |
|
areAlgoRelays={areAlgoRelays} |
|
showRelayCloseReason={showRelayCloseReason} |
|
/> |
|
)} |
|
</> |
|
) |
|
}) |
|
|
|
export default NormalFeed
|
|
|