Browse Source

remove bookmark trending and nostr.band api

imwald
Silberengel 5 months ago
parent
commit
ef34895e30
  1. 9
      src/components/NoteStats/RepostButton.tsx
  2. 1
      src/components/NoteStats/index.tsx
  3. 202
      src/components/TrendingNotes/index.tsx
  4. 35
      src/services/client.service.ts

9
src/components/NoteStats/RepostButton.tsx

@ -26,7 +26,7 @@ export default function RepostButton({ event }: { event: Event }) { @@ -26,7 +26,7 @@ export default function RepostButton({ event }: { event: Event }) {
const { isSmallScreen } = useScreenSize()
const { hideUntrustedInteractions, isUserTrusted } = useUserTrust()
const { publish, checkLogin, pubkey } = useNostr()
const noteStats = useNoteStatsById(event.id)
const noteStats = useNoteStatsById(event.id) as import('@/services/note-stats.service').TNoteStats | undefined
const [reposting, setReposting] = useState(false)
const [isPostDialogOpen, setIsPostDialogOpen] = useState(false)
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
@ -51,10 +51,9 @@ export default function RepostButton({ event }: { event: Event }) { @@ -51,10 +51,9 @@ export default function RepostButton({ event }: { event: Event }) {
const hasReposted = noteStats?.repostPubkeySet?.has(pubkey)
if (hasReposted) return
if (!noteStats?.updatedAt) {
const fetchedNoteStats = await noteStatsService.fetchNoteStats(event, pubkey)
if (fetchedNoteStats?.repostPubkeySet?.has(pubkey)) {
return
}
await noteStatsService.fetchNoteStats(event, pubkey)
// Note: fetchNoteStats doesn't return the stats, it updates them asynchronously
// The updated stats will be available through the useNoteStatsById hook
}
const repost = createRepostDraftEvent(event)

1
src/components/NoteStats/index.tsx

@ -2,7 +2,6 @@ import { cn } from '@/lib/utils' @@ -2,7 +2,6 @@ import { cn } from '@/lib/utils'
import { useNostr } from '@/providers/NostrProvider'
import { useScreenSize } from '@/providers/ScreenSizeProvider'
import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
import { useFeed } from '@/providers/FeedProvider'
import noteStatsService from '@/services/note-stats.service'
import { ExtendedKind } from '@/constants'
import { getRootEventHexId } from '@/lib/event'

202
src/components/TrendingNotes/index.tsx

@ -22,30 +22,27 @@ let cachedCustomEvents: { @@ -22,30 +22,27 @@ let cachedCustomEvents: {
events: Array<{ event: NostrEvent; score: number }>
timestamp: number
hashtags: string[]
listEventIds: string[]
} | null = null
// Flag to prevent concurrent initialization
let isInitializing = false
type TrendingTab = 'band' | 'relays' | 'bookmarks' | 'hashtags'
type TrendingTab = 'relays' | 'hashtags'
type SortOrder = 'newest' | 'oldest' | 'most-popular' | 'least-popular'
type BookmarkFilter = 'yours' | 'follows'
type HashtagFilter = 'popular'
export default function TrendingNotes() {
const { t } = useTranslation()
const { isEventDeleted } = useDeletedEvent()
const { hideUntrustedNotes, isUserTrusted } = useUserTrust()
const { pubkey, relayList, bookmarkListEvent } = useNostr()
const { pubkey, relayList } = useNostr()
const { favoriteRelays } = useFavoriteRelays()
const { zapReplyThreshold } = useZap()
const [trendingNotes, setTrendingNotes] = useState<NostrEvent[]>([])
const [trendingNotes] = useState<NostrEvent[]>([])
const [showCount, setShowCount] = useState(10)
const [loading, setLoading] = useState(true)
const [activeTab, setActiveTab] = useState<TrendingTab>('band')
const [loading] = useState(true)
const [activeTab, setActiveTab] = useState<TrendingTab>('relays')
const [sortOrder, setSortOrder] = useState<SortOrder>('most-popular')
const [bookmarkFilter] = useState<BookmarkFilter>('yours')
const [hashtagFilter] = useState<HashtagFilter>('popular')
const [selectedHashtag, setSelectedHashtag] = useState<string | null>(null)
const [popularHashtags, setPopularHashtags] = useState<string[]>([])
@ -64,77 +61,7 @@ export default function TrendingNotes() { @@ -64,77 +61,7 @@ export default function TrendingNotes() {
}, [cacheLoading])
// Extract event IDs from bookmark and pin lists (kinds 10003 and 10001)
const listEventIds = useMemo(() => {
const eventIds: string[] = []
// Add bookmarks (kind 10003)
if (bookmarkListEvent) {
bookmarkListEvent.tags.forEach((tag) => {
if (tag[0] === 'e' && tag[1]) {
eventIds.push(tag[1])
}
})
}
// Add pins (kind 10001) - fetch from client
// Note: We'll fetch pin list event separately since it's not in NostrProvider
return eventIds
}, [bookmarkListEvent])
// Fetch bookmark/pin lists from follows
const [followsBookmarkEventIds, setFollowsBookmarkEventIds] = useState<string[]>([])
useEffect(() => {
const fetchFollowsBookmarks = async () => {
if (!pubkey) return
try {
// Get follows list
const followPubkeys = await client.fetchFollowings(pubkey)
if (!followPubkeys || followPubkeys.length === 0) return
// Fetch bookmark and pin lists from follows
const bookmarkPromises = followPubkeys.map(async (followPubkey: string) => {
try {
const [bookmarkList, pinList] = await Promise.all([
client.fetchBookmarkListEvent(followPubkey),
client.fetchPinListEvent(followPubkey)
])
const eventIds: string[] = []
if (bookmarkList) {
bookmarkList.tags.forEach(tag => {
if (tag[0] === 'e' && tag[1]) {
eventIds.push(tag[1])
}
})
}
if (pinList) {
pinList.tags.forEach(tag => {
if (tag[0] === 'e' && tag[1]) {
eventIds.push(tag[1])
}
})
}
return eventIds
} catch (error) {
logger.error(`[TrendingNotes] Error fetching bookmarks for ${followPubkey}:`, error)
return []
}
})
const allEventIds = await Promise.all(bookmarkPromises)
const flattenedIds = allEventIds.flat()
setFollowsBookmarkEventIds(flattenedIds)
} catch (error) {
logger.error('Error fetching follows bookmarks:', error)
}
}
fetchFollowsBookmarks()
}, [pubkey])
// Calculate popular hashtags from cache events (all events from relays)
const calculatePopularHashtags = useMemo(() => {
@ -321,44 +248,7 @@ export default function TrendingNotes() { @@ -321,44 +248,7 @@ export default function TrendingNotes() {
allEvents.push(...recentEvents)
// 2. Fetch events from bookmark/pin lists (with rate limiting) - use full relay list
if (listEventIds.length > 0) {
try {
const bookmarkPinEvents = await client.fetchEvents(relays, {
ids: listEventIds,
limit: 500
})
allEvents.push(...bookmarkPinEvents)
} catch (error) {
logger.warn('[TrendingNotes] Error fetching bookmark/pin events:', error)
}
}
// 3. Fetch pin list if user is logged in - use full relay list
if (pubkey) {
try {
const pinListEvent = await client.fetchPinListEvent(pubkey)
if (pinListEvent) {
const pinEventIds = pinListEvent.tags
.filter(tag => tag[0] === 'e' && tag[1])
.map(tag => tag[1])
if (pinEventIds.length > 0) {
try {
const pinEvents = await client.fetchEvents(relays, {
ids: pinEventIds,
limit: 100
})
allEvents.push(...pinEvents)
} catch (error) {
logger.warn('[TrendingNotes] Error fetching pin events:', error)
}
}
}
} catch (error) {
logger.error('[TrendingNotes] Error fetching pin list:', error)
}
}
// Filter for top-level posts only (no replies or quotes)
const topLevelEvents = allEvents.filter(event => {
@ -457,8 +347,7 @@ export default function TrendingNotes() { @@ -457,8 +347,7 @@ export default function TrendingNotes() {
cachedCustomEvents = {
events: scoredEvents,
timestamp: now,
hashtags: [],
listEventIds: listEventIds.slice()
hashtags: []
}
// Store ALL events from the cache for hashtag analysis
@ -484,10 +373,7 @@ export default function TrendingNotes() { @@ -484,10 +373,7 @@ export default function TrendingNotes() {
// Use appropriate data source based on tab and filter
let sourceEvents: NostrEvent[] = []
if (activeTab === 'band') {
// "on Band" tab: use trending notes from nostr.band API
sourceEvents = trendingNotes
} else if (activeTab === 'relays') {
if (activeTab === 'relays') {
// "on your relays" tab: use cache events from user's relays
sourceEvents = cacheEvents
logger.debug('[TrendingNotes] Relays tab - cacheEvents.length:', cacheEvents.length, 'cacheLoading:', cacheLoading)
@ -524,9 +410,6 @@ export default function TrendingNotes() { @@ -524,9 +410,6 @@ export default function TrendingNotes() {
} else if (activeTab === 'relays') {
// For "on your relays" tab, we'll show all events (they're already from user's relays)
// This is the default behavior, so no additional filtering needed
} else if (activeTab === 'band') {
// For "on Band" tab, we'll show all events (this is the general trending)
// This is the default behavior, so no additional filtering needed
}
const id = isReplaceableEvent(evt.kind) ? getReplaceableCoordinateFromEvent(evt) : evt.id
@ -583,55 +466,9 @@ export default function TrendingNotes() { @@ -583,55 +466,9 @@ export default function TrendingNotes() {
})
return filtered.slice(0, showCount)
}, [trendingNotes, hideUntrustedNotes, showCount, isEventDeleted, isUserTrusted, activeTab, listEventIds, bookmarkFilter, followsBookmarkEventIds, hashtagFilter, selectedHashtag, sortOrder, zapReplyThreshold, cacheEvents])
useEffect(() => {
const fetchTrendingPosts = async () => {
setLoading(true)
const events = await client.fetchTrendingNotes()
// Apply the same NSFW and content warning filtering
const filteredEvents = events.filter(event => {
// Check for NSFW in 't' tags
const hasNsfwTag = event.tags.some(tag =>
tag[0] === 't' && tag[1] && tag[1].toLowerCase() === 'nsfw'
)
// Check for sensitive content tag
const hasSensitiveTag = event.tags.some(tag =>
tag[0] === 't' && tag[1] && tag[1].toLowerCase() === 'sensitive'
)
// Check for #NSFW hashtag in content
const hasNsfwHashtag = event.content.toLowerCase().includes('#nsfw')
// Check for content-warning tag (NIP-36)
const hasContentWarning = event.tags.some(tag =>
tag[0] === 'content-warning'
)
// Check for L tag with content-warning namespace
const hasContentWarningL = event.tags.some(tag =>
tag[0] === 'L' && tag[1] && tag[1].toLowerCase() === 'content-warning'
)
// Check for l tag with content-warning namespace
const hasContentWarningl = event.tags.some(tag =>
tag[0] === 'l' && tag[1] && tag[1].toLowerCase() === 'content-warning'
)
// Filter out if any NSFW or content warning indicators are found
return !hasNsfwTag && !hasSensitiveTag && !hasNsfwHashtag &&
!hasContentWarning && !hasContentWarningL && !hasContentWarningl
})
}, [trendingNotes, hideUntrustedNotes, showCount, isEventDeleted, isUserTrusted, activeTab, hashtagFilter, selectedHashtag, sortOrder, zapReplyThreshold, cacheEvents])
setTrendingNotes(filteredEvents)
setLoading(false)
}
fetchTrendingPosts()
}, [])
// Reset showCount when tab changes
useEffect(() => {
@ -640,9 +477,7 @@ export default function TrendingNotes() { @@ -640,9 +477,7 @@ export default function TrendingNotes() {
// Reset filters when switching tabs
useEffect(() => {
if (activeTab === 'band') {
setSortOrder('most-popular')
} else if (activeTab === 'relays') {
if (activeTab === 'relays') {
setSortOrder('most-popular')
// If cache is empty and not loading, log the issue for debugging
if (cacheEvents.length === 0 && !cacheLoading && !isInitializing) {
@ -654,12 +489,6 @@ export default function TrendingNotes() { @@ -654,12 +489,6 @@ export default function TrendingNotes() {
}
}, [activeTab, pubkey, cacheEvents.length, cacheLoading])
// Handle case where bookmarks tab is not available
useEffect(() => {
if (!pubkey && activeTab === 'bookmarks') {
setActiveTab('band')
}
}, [pubkey, activeTab])
useEffect(() => {
if (showCount >= trendingNotes.length) return
@ -698,16 +527,6 @@ export default function TrendingNotes() { @@ -698,16 +527,6 @@ export default function TrendingNotes() {
<div className="flex items-center gap-2 px-4 pb-2">
<span className="text-sm font-medium text-muted-foreground">Trending:</span>
<div className="flex gap-1">
<button
onClick={() => setActiveTab('band')}
className={`px-3 py-1 text-sm rounded-md transition-colors ${
activeTab === 'band'
? 'bg-primary text-primary-foreground'
: 'bg-muted hover:bg-muted/80 text-muted-foreground'
}`}
>
on Band
</button>
<button
onClick={() => setActiveTab('relays')}
className={`px-3 py-1 text-sm rounded-md transition-colors ${
@ -824,8 +643,7 @@ export default function TrendingNotes() { @@ -824,8 +643,7 @@ export default function TrendingNotes() {
))}
{(() => {
// Determine the current data source length based on active tab
const currentDataLength = activeTab === 'band' ? trendingNotes.length :
activeTab === 'relays' || activeTab === 'hashtags' ? cacheEvents.length :
const currentDataLength = activeTab === 'relays' || activeTab === 'hashtags' ? cacheEvents.length :
trendingNotes.length
// Show loading if:

35
src/services/client.service.ts

@ -24,9 +24,7 @@ import { @@ -24,9 +24,7 @@ import {
kinds,
Event as NEvent,
nip19,
Relay,
SimplePool,
validateEvent,
VerifiedEvent
} from 'nostr-tools'
import { AbstractRelay } from 'nostr-tools/abstract-relay'
@ -57,7 +55,6 @@ class ClientService extends EventTarget { @@ -57,7 +55,6 @@ class ClientService extends EventTarget {
(ids) => Promise.all(ids.map((id) => this._fetchEvent(id))),
{ cacheMap: this.eventCacheMap }
)
private trendingNotesCache: NEvent[] | null = null
private requestThrottle = new Map<string, number>() // Track request timestamps per relay
private readonly REQUEST_COOLDOWN = 3000 // 3 second cooldown between requests to prevent "too many REQs"
private failureCount = new Map<string, number>() // Track consecutive failures per relay
@ -1145,38 +1142,6 @@ class ClientService extends EventTarget { @@ -1145,38 +1142,6 @@ class ClientService extends EventTarget {
return true
}
async fetchTrendingNotes() {
if (this.trendingNotesCache) {
return this.trendingNotesCache
}
try {
const response = await fetch('https://api.nostr.band/v0/trending/notes')
const data = await response.json()
const events: NEvent[] = []
for (const note of data.notes ?? []) {
if (validateEvent(note.event)) {
events.push(note.event)
this.addEventToCache(note.event)
if (note.relays?.length) {
note.relays.map((r: string) => {
try {
const relay = new Relay(r)
this.trackEventSeenOn(note.event.id, relay)
} catch {
return null
}
})
}
}
}
this.trendingNotesCache = events
return this.trendingNotesCache
} catch (error) {
console.error('fetchTrendingNotes error', error)
return []
}
}
addEventToCache(event: NEvent) {
this.eventDataLoader.prime(event.id, Promise.resolve(event))

Loading…
Cancel
Save