From 74d974616f62f45eef896b8cd5760ff83b6d6071 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Fri, 31 Oct 2025 04:59:02 +0100 Subject: [PATCH] add a filter to the profile articles --- src/components/Profile/ProfileArticles.tsx | 75 ++++++++++++++++------ src/components/Profile/index.tsx | 40 +++++++++++- 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/src/components/Profile/ProfileArticles.tsx b/src/components/Profile/ProfileArticles.tsx index 93f8009..faf5f24 100644 --- a/src/components/Profile/ProfileArticles.tsx +++ b/src/components/Profile/ProfileArticles.tsx @@ -1,4 +1,4 @@ -import { FAST_READ_RELAY_URLS } from '@/constants' +import { ExtendedKind, FAST_READ_RELAY_URLS } from '@/constants' import logger from '@/lib/logger' import { normalizeUrl } from '@/lib/url' import client from '@/services/client.service' @@ -12,9 +12,11 @@ interface ProfileArticlesProps { pubkey: string topSpace?: number searchQuery?: string + kindFilter?: string + onEventsChange?: (events: Event[]) => void } -const ProfileArticles = forwardRef<{ refresh: () => void }, ProfileArticlesProps>(({ pubkey, topSpace, searchQuery = '' }, ref) => { +const ProfileArticles = forwardRef<{ refresh: () => void; getEvents: () => Event[] }, ProfileArticlesProps>(({ pubkey, topSpace, searchQuery = '', kindFilter = 'all', onEventsChange }, ref) => { console.log('[ProfileArticles] Component rendered with pubkey:', pubkey) const [events, setEvents] = useState([]) const [isLoading, setIsLoading] = useState(true) @@ -148,23 +150,42 @@ const ProfileArticles = forwardRef<{ refresh: () => void }, ProfileArticlesProps }, [fetchArticles]) useImperativeHandle(ref, () => ({ - refresh - }), [refresh]) + refresh, + getEvents: () => events + }), [refresh, events]) - // Filter events based on search query + // Notify parent of events changes + useEffect(() => { + if (onEventsChange) { + onEventsChange(events) + } + }, [events, onEventsChange]) + + // Filter events based on search query and kind filter const filteredEvents = useMemo(() => { - if (!searchQuery.trim()) { - return events + let filtered = events + + // Filter by kind first + if (kindFilter && kindFilter !== 'all') { + const kindFilterNum = parseInt(kindFilter) + if (!isNaN(kindFilterNum)) { + filtered = filtered.filter(event => event.kind === kindFilterNum) + } } - - const query = searchQuery.toLowerCase() - return events.filter(event => - event.content.toLowerCase().includes(query) || - event.tags.some(tag => - tag.length > 1 && tag[1]?.toLowerCase().includes(query) + + // Then filter by search query + if (searchQuery.trim()) { + const query = searchQuery.toLowerCase() + filtered = filtered.filter(event => + event.content.toLowerCase().includes(query) || + event.tags.some(tag => + tag.length > 1 && tag[1]?.toLowerCase().includes(query) + ) ) - ) - }, [events, searchQuery]) + } + + return filtered + }, [events, searchQuery, kindFilter]) // Separate effect for initial fetch only with a small delay useEffect(() => { @@ -209,10 +230,26 @@ const ProfileArticles = forwardRef<{ refresh: () => void }, ProfileArticlesProps ) } - if (filteredEvents.length === 0 && searchQuery.trim()) { + // Get kind label for display + const getKindLabel = (kindValue: string) => { + if (!kindValue || kindValue === 'all') return 'articles, publications, or highlights' + const kindNum = parseInt(kindValue) + if (kindNum === kinds.LongFormArticle) return 'long form articles' + if (kindNum === ExtendedKind.WIKI_ARTICLE_MARKDOWN) return 'wiki articles (markdown)' + if (kindNum === ExtendedKind.WIKI_ARTICLE) return 'wiki articles (asciidoc)' + if (kindNum === ExtendedKind.PUBLICATION) return 'publications' + if (kindNum === kinds.Highlights) return 'highlights' + return 'items' + } + + if (filteredEvents.length === 0 && (searchQuery.trim() || (kindFilter && kindFilter !== 'all'))) { return (
-
No articles, publications, or highlights match your search
+
+ {searchQuery.trim() + ? `No ${getKindLabel(kindFilter)} match your search` + : `No ${getKindLabel(kindFilter)} found`} +
) } @@ -224,9 +261,9 @@ const ProfileArticles = forwardRef<{ refresh: () => void }, ProfileArticlesProps 🔄 Refreshing articles... )} - {searchQuery.trim() && ( + {(searchQuery.trim() || (kindFilter && kindFilter !== 'all')) && (
- {filteredEvents.length} of {events.length} articles + {filteredEvents.length} of {events.length} {getKindLabel(kindFilter)}
)}
diff --git a/src/components/Profile/index.tsx b/src/components/Profile/index.tsx index 4645f12..361fab6 100644 --- a/src/components/Profile/index.tsx +++ b/src/components/Profile/index.tsx @@ -13,7 +13,16 @@ import ProfileSearchBar from '@/components/ui/ProfileSearchBar' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { ExtendedKind } from '@/constants' import { useFetchProfile } from '@/hooks' +import { Event, kinds } from 'nostr-tools' import { toProfileEditor } from '@/lib/link' import { generateImageByPubkey } from '@/lib/pubkey' import { useSecondaryPage } from '@/PageManager' @@ -41,11 +50,13 @@ export default function Profile({ id }: { id?: string }) { const { pubkey: accountPubkey } = useNostr() const [activeTab, setActiveTab] = useState('posts') const [searchQuery, setSearchQuery] = useState('') + const [articleKindFilter, setArticleKindFilter] = useState('all') // Refs for child components const profileFeedRef = useRef<{ refresh: () => void }>(null) const profileBookmarksRef = useRef<{ refresh: () => void }>(null) - const profileArticlesRef = useRef<{ refresh: () => void }>(null) + const profileArticlesRef = useRef<{ refresh: () => void; getEvents: () => Event[] }>(null) + const [articleEvents, setArticleEvents] = useState([]) const isFollowingYou = useMemo(() => { // This will be handled by the FollowedBy component @@ -225,6 +236,31 @@ export default function Profile({ id }: { id?: string }) { placeholder={`Search ${activeTab}...`} className="w-64" /> + {activeTab === 'articles' && (() => { + // Calculate counts for each kind + const allCount = articleEvents.length + const longFormCount = articleEvents.filter(e => e.kind === kinds.LongFormArticle).length + const wikiMarkdownCount = articleEvents.filter(e => e.kind === ExtendedKind.WIKI_ARTICLE_MARKDOWN).length + const wikiAsciiDocCount = articleEvents.filter(e => e.kind === ExtendedKind.WIKI_ARTICLE).length + const publicationCount = articleEvents.filter(e => e.kind === ExtendedKind.PUBLICATION).length + const highlightsCount = articleEvents.filter(e => e.kind === kinds.Highlights).length + + return ( + + ) + })()} )} {(activeTab === 'pins' || activeTab === 'bookmarks' || activeTab === 'interests') && (