Browse Source

add a filter to the profile articles

imwald
Silberengel 5 months ago
parent
commit
74d974616f
  1. 75
      src/components/Profile/ProfileArticles.tsx
  2. 40
      src/components/Profile/index.tsx

75
src/components/Profile/ProfileArticles.tsx

@ -1,4 +1,4 @@ @@ -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 { @@ -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<Event[]>([])
const [isLoading, setIsLoading] = useState(true)
@ -148,23 +150,42 @@ const ProfileArticles = forwardRef<{ refresh: () => void }, ProfileArticlesProps @@ -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 @@ -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 (
<div className="flex justify-center items-center py-8">
<div className="text-sm text-muted-foreground">No articles, publications, or highlights match your search</div>
<div className="text-sm text-muted-foreground">
{searchQuery.trim()
? `No ${getKindLabel(kindFilter)} match your search`
: `No ${getKindLabel(kindFilter)} found`}
</div>
</div>
)
}
@ -224,9 +261,9 @@ const ProfileArticles = forwardRef<{ refresh: () => void }, ProfileArticlesProps @@ -224,9 +261,9 @@ const ProfileArticles = forwardRef<{ refresh: () => void }, ProfileArticlesProps
🔄 Refreshing articles...
</div>
)}
{searchQuery.trim() && (
{(searchQuery.trim() || (kindFilter && kindFilter !== 'all')) && (
<div className="px-4 py-2 text-sm text-muted-foreground">
{filteredEvents.length} of {events.length} articles
{filteredEvents.length} of {events.length} {getKindLabel(kindFilter)}
</div>
)}
<div className="space-y-2">

40
src/components/Profile/index.tsx

@ -13,7 +13,16 @@ import ProfileSearchBar from '@/components/ui/ProfileSearchBar' @@ -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 }) { @@ -41,11 +50,13 @@ export default function Profile({ id }: { id?: string }) {
const { pubkey: accountPubkey } = useNostr()
const [activeTab, setActiveTab] = useState<ProfileTabValue>('posts')
const [searchQuery, setSearchQuery] = useState('')
const [articleKindFilter, setArticleKindFilter] = useState<string>('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<Event[]>([])
const isFollowingYou = useMemo(() => {
// This will be handled by the FollowedBy component
@ -225,6 +236,31 @@ export default function Profile({ id }: { id?: string }) { @@ -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 (
<Select value={articleKindFilter} onValueChange={setArticleKindFilter}>
<SelectTrigger className="w-48">
<SelectValue placeholder="Filter by type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Types ({allCount})</SelectItem>
<SelectItem value={String(kinds.LongFormArticle)}>Long Form Articles ({longFormCount})</SelectItem>
<SelectItem value={String(ExtendedKind.WIKI_ARTICLE_MARKDOWN)}>Wiki (Markdown) ({wikiMarkdownCount})</SelectItem>
<SelectItem value={String(ExtendedKind.WIKI_ARTICLE)}>Wiki (AsciiDoc) ({wikiAsciiDocCount})</SelectItem>
<SelectItem value={String(ExtendedKind.PUBLICATION)}>Publications ({publicationCount})</SelectItem>
<SelectItem value={String(kinds.Highlights)}>Highlights ({highlightsCount})</SelectItem>
</SelectContent>
</Select>
)
})()}
<RetroRefreshButton
onClick={handleRefresh}
size="sm"
@ -246,6 +282,8 @@ export default function Profile({ id }: { id?: string }) { @@ -246,6 +282,8 @@ export default function Profile({ id }: { id?: string }) {
pubkey={pubkey}
topSpace={0}
searchQuery={searchQuery}
kindFilter={articleKindFilter}
onEventsChange={setArticleEvents}
/>
)}
{(activeTab === 'pins' || activeTab === 'bookmarks' || activeTab === 'interests') && (

Loading…
Cancel
Save