From 7748ac86bdd340ea030cc4c69e44b2429d215a71 Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sun, 10 May 2026 23:30:39 +0200 Subject: [PATCH] bug-fixes --- src/components/Note/ArticleCardCoverImage.tsx | 57 +++++++++++++++++++ src/components/Note/LongFormCard.tsx | 29 +++++----- .../Note/MarkdownArticle/MarkdownArticle.tsx | 28 +++++++++ src/components/Note/WikiCard.tsx | 31 +++++----- src/components/ReplyNoteList/index.tsx | 3 +- src/lib/relay-list-builder.ts | 4 ++ .../client-replaceable-events.service.ts | 4 +- 7 files changed, 122 insertions(+), 34 deletions(-) create mode 100644 src/components/Note/ArticleCardCoverImage.tsx diff --git a/src/components/Note/ArticleCardCoverImage.tsx b/src/components/Note/ArticleCardCoverImage.tsx new file mode 100644 index 00000000..c362e72d --- /dev/null +++ b/src/components/Note/ArticleCardCoverImage.tsx @@ -0,0 +1,57 @@ +import ContentImage from '@/components/Image' +import UserAvatar from '@/components/UserAvatar' +import { cn } from '@/lib/utils' +import type { Event } from 'nostr-tools' + +/** + * Long-form / wiki–style card cover: NIP-23 `image` tag when present, otherwise the author avatar. + */ +export default function ArticleCardCoverImage({ + event, + imageUrl, + autoLoadMedia, + layout, + hideImageIfError = false +}: { + event: Event + imageUrl?: string + autoLoadMedia: boolean + layout: 'stacked' | 'row' + /** Passed through to {@link ContentImage} when an `image` tag URL exists. */ + hideImageIfError?: boolean +}) { + const trimmed = imageUrl?.trim() + if (trimmed && autoLoadMedia) { + return ( + + ) + } + if (trimmed) return null + + return ( +
+ +
+ ) +} diff --git a/src/components/Note/LongFormCard.tsx b/src/components/Note/LongFormCard.tsx index 4da4e90b..7fbd5c5b 100644 --- a/src/components/Note/LongFormCard.tsx +++ b/src/components/Note/LongFormCard.tsx @@ -8,7 +8,7 @@ import { cn } from '@/lib/utils' import { Event, kinds } from 'nostr-tools' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import Image from '../Image' +import ArticleCardCoverImage from './ArticleCardCoverImage' /** * Feed / embed / preview surface for NIP-23 long-form (kind 30023): title, summary, image, tags — no “Show more” body. @@ -93,13 +93,12 @@ export default function LongFormCard({ return (
- {metadata.image && autoLoadMedia && ( - - )} +
{titleComponent} {summaryComponent} @@ -115,14 +114,12 @@ export default function LongFormCard({
- {metadata.image && autoLoadMedia && ( - - )} +
{titleComponent} {summaryComponent} diff --git a/src/components/Note/MarkdownArticle/MarkdownArticle.tsx b/src/components/Note/MarkdownArticle/MarkdownArticle.tsx index 7f75bd9c..060f27a1 100644 --- a/src/components/Note/MarkdownArticle/MarkdownArticle.tsx +++ b/src/components/Note/MarkdownArticle/MarkdownArticle.tsx @@ -1,5 +1,6 @@ import { useSecondaryPageOptional, useSmartHashtagNavigationOptional, useSmartRelayNavigationOptional } from '@/PageManager' import Image from '@/components/Image' +import UserAvatar from '@/components/UserAvatar' import MediaPlayer from '@/components/MediaPlayer' import Wikilink from '@/components/UniversalContent/Wikilink' import { BookstrContent } from '@/components/Bookstr' @@ -5899,12 +5900,39 @@ export default function MarkdownArticle({

{metadata.summary}

)} + {!hideMetadata && + event.kind === kinds.LongFormArticle && + !metadata.image?.trim() && ( +
+ +
+ )} {hideMetadata && metadata.title && event.kind !== ExtendedKind.DISCUSSION && !isNip52CalendarCardKind(event.kind) && (

{metadata.title}

)} + {hideMetadata && + metadata.title && + event.kind === kinds.LongFormArticle && + !metadata.image?.trim() && + event.kind !== ExtendedKind.DISCUSSION && + !isNip52CalendarCardKind(event.kind) && ( +
+ +
+ )} {/* Metadata image */} {!hideMetadata && metadata.image && (() => { diff --git a/src/components/Note/WikiCard.tsx b/src/components/Note/WikiCard.tsx index 4bdfa3fe..ed18b43a 100644 --- a/src/components/Note/WikiCard.tsx +++ b/src/components/Note/WikiCard.tsx @@ -6,7 +6,7 @@ import { useContentPolicyOptional } from '@/providers/ContentPolicyProvider' import { useScreenSizeOptional } from '@/providers/ScreenSizeProvider' import { Event, kinds } from 'nostr-tools' import { useMemo } from 'react' -import Image from '../Image' +import ArticleCardCoverImage from './ArticleCardCoverImage' export default function WikiCard({ event, @@ -60,13 +60,13 @@ export default function WikiCard({ className="cursor-pointer rounded-lg border p-4 hover:bg-muted/50 transition-colors" onClick={handleCardClick} > - {metadata.image && autoLoadMedia && ( - - )} +
{titleComponent} {summaryComponent} @@ -84,14 +84,13 @@ export default function WikiCard({ onClick={handleCardClick} >
- {metadata.image && autoLoadMedia && ( - - )} +
{titleComponent} {summaryComponent} diff --git a/src/components/ReplyNoteList/index.tsx b/src/components/ReplyNoteList/index.tsx index e00bf82f..92d2ceb7 100644 --- a/src/components/ReplyNoteList/index.tsx +++ b/src/components/ReplyNoteList/index.tsx @@ -775,7 +775,8 @@ function ReplyNoteList({ next.set(pkNorm, { pubkey: pkNorm, npub: pubkeyToNpub(pkNorm) ?? '', - username: formatPubkey(pkNorm) + username: formatPubkey(pkNorm), + batchPlaceholder: true }) } } diff --git a/src/lib/relay-list-builder.ts b/src/lib/relay-list-builder.ts index e13fae85..91665233 100644 --- a/src/lib/relay-list-builder.ts +++ b/src/lib/relay-list-builder.ts @@ -386,6 +386,10 @@ export async function buildReplyReadRelayList( includeFastReadRelays: true, includeSearchableRelays: true, includeLocalRelays: true, + /** Same menu list as timelines — threads often opened from favorites. */ + includeFavoriteRelays: Boolean(userPubkey), + /** FAST_READ + SEARCHABLE before author/user NIP-65 slices so broken personal relays do not starve thread REQ under the global connection cap. */ + preferPublicReadRelaysEarly: true, blockedRelays }) } diff --git a/src/services/client-replaceable-events.service.ts b/src/services/client-replaceable-events.service.ts index 69241c2e..85903805 100644 --- a/src/services/client-replaceable-events.service.ts +++ b/src/services/client-replaceable-events.service.ts @@ -1143,7 +1143,9 @@ export class ReplaceableEventService { profiles.push({ pubkey, npub: pubkeyToNpub(pubkey) ?? '', - username: formatPubkey(pubkey) + username: formatPubkey(pubkey), + /** Lets {@link useFetchProfile} retry per-pubkey when batch REQ missed kind 0. */ + batchPlaceholder: true }) } }