Browse Source

updated trending notes. fixed pagination and reinstated nostr.band trends

imwald
Silberengel 4 months ago
parent
commit
179f3d8232
  1. 4
      package-lock.json
  2. 2
      package.json
  3. 2
      src/components/Note/MarkdownArticle/MarkdownArticle.tsx
  4. 4
      src/components/SaveRelayDropdownMenu/index.tsx
  5. 38
      src/components/TrendingNotes/index.tsx
  6. 7
      src/hooks/useProfileTimeline.tsx
  7. 6
      src/lib/draft-event.ts
  8. 2
      src/lib/nip05.ts
  9. 4
      src/services/relay-selection.service.ts

4
package-lock.json generated

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
{
"name": "jumble-imwald",
"version": "10.15",
"version": "12.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jumble-imwald",
"version": "10.15",
"version": "12.2",
"license": "MIT",
"dependencies": {
"@asciidoctor/core": "^3.0.4",

2
package.json

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
{
"name": "jumble-imwald",
"version": "12.1",
"version": "12.2",
"description": "A user-friendly Nostr client focused on relay feed browsing and relay discovery, forked from Jumble",
"private": true,
"type": "module",

2
src/components/Note/MarkdownArticle/MarkdownArticle.tsx

@ -49,7 +49,7 @@ export default function MarkdownArticle({ @@ -49,7 +49,7 @@ export default function MarkdownArticle({
// Convert "Read naddr... instead." patterns to markdown links for replaceable events
// This is a standard format for forwarding readers to referred events (e.g., in wikis)
const redirectRegex = /Read (naddr1[a-z0-9]+) instead\./gi
content = content.replace(redirectRegex, (match, naddr) => {
content = content.replace(redirectRegex, (_match, naddr) => {
const href = toNote(naddr)
return `Read [${naddr}](${href}) instead.`
})

4
src/components/SaveRelayDropdownMenu/index.tsx

@ -131,7 +131,7 @@ function RelayItem({ urls }: { urls: string[] }) { @@ -131,7 +131,7 @@ function RelayItem({ urls }: { urls: string[] }) {
await addFavoriteRelays(urls)
}
} catch (error) {
logger.error('Failed to toggle favorite relay', { error, url })
logger.error('Failed to toggle favorite relay', { error, urls })
} finally {
setIsLoading(false)
}
@ -256,7 +256,7 @@ function BlockRelayItem({ urls }: { urls: string[] }) { @@ -256,7 +256,7 @@ function BlockRelayItem({ urls }: { urls: string[] }) {
await addBlockedRelays(urls)
}
} catch (error) {
logger.error('Failed to toggle blocked relay', { error, url })
logger.error('Failed to toggle blocked relay', { error, urls })
} finally {
setIsLoading(false)
}

38
src/components/TrendingNotes/index.tsx

@ -14,7 +14,7 @@ import { FAST_READ_RELAY_URLS } from '@/constants' @@ -14,7 +14,7 @@ import { FAST_READ_RELAY_URLS } from '@/constants'
import logger from '@/lib/logger'
import { normalizeUrl } from '@/lib/url'
const SHOW_COUNT = 10
const SHOW_COUNT = 25
const CACHE_DURATION = 30 * 60 * 1000 // 30 minutes
// Unified cache for all custom trending feeds
@ -40,7 +40,7 @@ export default function TrendingNotes() { @@ -40,7 +40,7 @@ export default function TrendingNotes() {
const { zapReplyThreshold } = useZap()
const [nostrEvents, setNostrEvents] = useState<NostrEvent[]>([])
const [nostrLoading, setNostrLoading] = useState(false)
const [showCount, setShowCount] = useState(10)
const [showCount, setShowCount] = useState(SHOW_COUNT)
const [activeTab, setActiveTab] = useState<TrendingTab>('nostr')
const [sortOrder, setSortOrder] = useState<SortOrder>('most-popular')
const [hashtagFilter] = useState<HashtagFilter>('popular')
@ -244,7 +244,7 @@ export default function TrendingNotes() { @@ -244,7 +244,7 @@ export default function TrendingNotes() {
const events = await client.fetchEvents([relay], {
kinds: [1, 11, 30023, 9802, 20, 21, 22],
since: twentyFourHoursAgo,
limit: 100
limit: 200
})
logger.debug('[TrendingNotes] Fetched', events.length, 'events from relay', relay)
return events
@ -386,7 +386,8 @@ export default function TrendingNotes() { @@ -386,7 +386,8 @@ export default function TrendingNotes() {
}, []) // Only run once on mount to prevent infinite loop
const relaysFilteredEvents = useMemo(() => {
// Compute filtered events without slicing (for pagination length check)
const relaysFilteredEventsAll = useMemo(() => {
const idSet = new Set<string>()
const sourceEvents = cacheEvents.length > 0 ? cacheEvents : nostrEvents
@ -412,6 +413,9 @@ export default function TrendingNotes() { @@ -412,6 +413,9 @@ export default function TrendingNotes() {
if (!allHashtags.includes(selectedHashtag.toLowerCase())) return false
}
}
}
// Deduplicate events
const id = isReplaceableEvent(evt.kind) ? getReplaceableCoordinateFromEvent(evt) : evt.id
if (idSet.has(id)) {
return false
@ -465,12 +469,11 @@ export default function TrendingNotes() { @@ -465,12 +469,11 @@ export default function TrendingNotes() {
return 0
})
return filtered.slice(0, showCount)
return filtered
}, [
cacheEvents,
nostrEvents,
hideUntrustedNotes,
showCount,
isEventDeleted,
isUserTrusted,
activeTab,
@ -480,6 +483,11 @@ export default function TrendingNotes() { @@ -480,6 +483,11 @@ export default function TrendingNotes() {
zapReplyThreshold
])
// Slice to showCount for display
const relaysFilteredEvents = useMemo(() => {
return relaysFilteredEventsAll.slice(0, showCount)
}, [relaysFilteredEventsAll, showCount])
const filteredEvents = useMemo(() => {
if (activeTab === 'nostr') {
return nostrEvents.slice(0, showCount)
@ -491,7 +499,7 @@ export default function TrendingNotes() { @@ -491,7 +499,7 @@ export default function TrendingNotes() {
// Reset showCount when tab changes
useEffect(() => {
setShowCount(10)
setShowCount(SHOW_COUNT)
}, [activeTab])
// Reset filters when switching tabs
@ -510,10 +518,12 @@ export default function TrendingNotes() { @@ -510,10 +518,12 @@ export default function TrendingNotes() {
useEffect(() => {
// For relays/hashtags tabs, use the filtered length (before slicing)
// For nostr tab, use the raw events length
const totalLength =
activeTab === 'nostr'
? nostrEvents.length
: cacheEvents.length
: relaysFilteredEventsAll.length
if (showCount >= totalLength) return
@ -540,7 +550,7 @@ export default function TrendingNotes() { @@ -540,7 +550,7 @@ export default function TrendingNotes() {
observerInstance.unobserve(currentBottomRef)
}
}
}, [activeTab, cacheEvents.length, nostrEvents.length, showCount, cacheLoading, nostrLoading])
}, [activeTab, nostrEvents.length, relaysFilteredEventsAll.length, showCount, cacheLoading, nostrLoading])
return (
<div className="min-h-screen">
@ -682,15 +692,21 @@ export default function TrendingNotes() { @@ -682,15 +692,21 @@ export default function TrendingNotes() {
<NoteCard key={event.id} className="w-full" event={event} />
))}
{(() => {
const currentDataLength =
const totalAvailableLength =
activeTab === 'nostr'
? nostrEvents.length
: cacheEvents.length
// For relays/hashtags tabs, we need to check the filtered length, not raw cache length
// because filtering might reduce the available items
const actualAvailableLength = activeTab === 'nostr'
? totalAvailableLength
: relaysFilteredEventsAll.length
const shouldShowLoading =
(activeTab === 'nostr' && nostrLoading) ||
((activeTab === 'relays' || activeTab === 'hashtags') && cacheLoading) ||
showCount < currentDataLength
showCount < actualAvailableLength
if (shouldShowLoading) {
return (

7
src/hooks/useProfileTimeline.tsx

@ -112,7 +112,6 @@ export function useProfileTimeline({ @@ -112,7 +112,6 @@ export function useProfileTimeline({
useEffect(() => {
let cancelled = false
const refreshIndex = refreshToken
const subscribe = async () => {
setIsLoading(!timelineCache.has(cacheKey))
@ -134,7 +133,11 @@ export function useProfileTimeline({ @@ -134,7 +133,11 @@ export function useProfileTimeline({
.filter((request) => request.urls.length)
if (!subRequests.length) {
updateCache([])
timelineCache.set(cacheKey, {
events: [],
lastUpdated: Date.now()
})
setEvents([])
setIsLoading(false)
return
}

6
src/lib/draft-event.ts

@ -1034,7 +1034,7 @@ export async function createHighlightDraftEvent( @@ -1034,7 +1034,7 @@ export async function createHighlightDraftEvent(
}
}
} catch (err) {
logger.error('Failed to decode naddr', { error: err, reference: tag })
logger.error('Failed to decode naddr', { error: err, reference: sourceValue })
}
} else if (sourceValue.startsWith('nevent')) {
// Handle nevent
@ -1056,7 +1056,7 @@ export async function createHighlightDraftEvent( @@ -1056,7 +1056,7 @@ export async function createHighlightDraftEvent(
}
}
} catch (err) {
logger.error('Failed to decode nevent', { error: err, reference: tag })
logger.error('Failed to decode nevent', { error: err, reference: sourceValue })
}
} else if (sourceValue.startsWith('note')) {
// Handle note1... (bech32 encoded event ID)
@ -1073,7 +1073,7 @@ export async function createHighlightDraftEvent( @@ -1073,7 +1073,7 @@ export async function createHighlightDraftEvent(
}
}
} catch (err) {
logger.error('Failed to decode note', { error: err, reference: tag })
logger.error('Failed to decode note', { error: err, reference: sourceValue })
}
} else {
// Regular hex event ID

2
src/lib/nip05.ts

@ -69,7 +69,7 @@ export async function fetchPubkeysFromDomain(domain: string): Promise<string[]> @@ -69,7 +69,7 @@ export async function fetchPubkeysFromDomain(domain: string): Promise<string[]>
return true
}) as string[]
} catch (error) {
logger.error('Error fetching pubkeys from domain', { error, nip05Domain })
logger.error('Error fetching pubkeys from domain', { error, domain })
return []
}
}

4
src/services/relay-selection.service.ts

@ -185,7 +185,7 @@ class RelaySelectionService { @@ -185,7 +185,7 @@ class RelaySelectionService {
}
}
} catch (error) {
logger.error('Failed to get contextual relays', { error, relaySets })
logger.error('Failed to get contextual relays', { error })
}
return Array.from(contextualRelays)
@ -440,7 +440,7 @@ class RelaySelectionService { @@ -440,7 +440,7 @@ class RelaySelectionService {
}
}
} catch (error) {
logger.error('Failed to decode nostr address', { error, tag })
logger.error('Failed to decode nostr address', { error, match })
}
}
}

Loading…
Cancel
Save