Browse Source

bug-fixes

imwald
Silberengel 1 month ago
parent
commit
7748ac86bd
  1. 57
      src/components/Note/ArticleCardCoverImage.tsx
  2. 25
      src/components/Note/LongFormCard.tsx
  3. 28
      src/components/Note/MarkdownArticle/MarkdownArticle.tsx
  4. 27
      src/components/Note/WikiCard.tsx
  5. 3
      src/components/ReplyNoteList/index.tsx
  6. 4
      src/lib/relay-list-builder.ts
  7. 4
      src/services/client-replaceable-events.service.ts

57
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 / wikistyle 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 (
<ContentImage
image={{ url: trimmed, pubkey: event.pubkey }}
className={
layout === 'stacked'
? 'mb-3 aspect-video w-full max-w-[400px]'
: 'h-44 max-w-[400px] shrink rounded-lg bg-foreground object-cover aspect-[4/3] xl:aspect-video'
}
classNames={layout === 'row' ? { wrapper: 'w-auto max-w-[400px] shrink-0' } : undefined}
hideIfError={hideImageIfError}
/>
)
}
if (trimmed) return null
return (
<div
className={cn(
'flex items-center justify-center rounded-lg bg-muted',
layout === 'stacked'
? 'mb-3 h-44 w-full max-w-[400px]'
: 'h-44 w-auto max-w-[400px] shrink-0 aspect-[4/3] xl:aspect-video'
)}
>
<UserAvatar
userId={event.pubkey}
size="large"
deferRemoteAvatar={false}
className="!h-[7.5rem] !w-[7.5rem] rounded-xl"
/>
</div>
)
}

25
src/components/Note/LongFormCard.tsx

@ -8,7 +8,7 @@ import { cn } from '@/lib/utils'
import { Event, kinds } from 'nostr-tools' import { Event, kinds } from 'nostr-tools'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useTranslation } from 'react-i18next' 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. * 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 ( return (
<div className={shellClass}> <div className={shellClass}>
<div className={cardClass} onClick={interactive ? handleCardClick : undefined}> <div className={cardClass} onClick={interactive ? handleCardClick : undefined}>
{metadata.image && autoLoadMedia && ( <ArticleCardCoverImage
<Image event={event}
image={{ url: metadata.image, pubkey: event.pubkey }} imageUrl={metadata.image}
className="mb-3 aspect-video w-full max-w-[400px]" autoLoadMedia={autoLoadMedia}
hideIfError layout="stacked"
/> />
)}
<div className="space-y-2"> <div className="space-y-2">
{titleComponent} {titleComponent}
{summaryComponent} {summaryComponent}
@ -115,14 +114,12 @@ export default function LongFormCard({
<div className={cn('w-full min-w-0', shellClass)}> <div className={cn('w-full min-w-0', shellClass)}>
<div className={cn(cardClass, 'min-w-0')} onClick={interactive ? handleCardClick : undefined}> <div className={cn(cardClass, 'min-w-0')} onClick={interactive ? handleCardClick : undefined}>
<div className="flex min-w-0 gap-4"> <div className="flex min-w-0 gap-4">
{metadata.image && autoLoadMedia && ( <ArticleCardCoverImage
<Image event={event}
image={{ url: metadata.image, pubkey: event.pubkey }} imageUrl={metadata.image}
classNames={{ wrapper: 'w-auto max-w-[400px] shrink-0' }} autoLoadMedia={autoLoadMedia}
className="h-44 max-w-[400px] shrink rounded-lg bg-foreground object-cover aspect-[4/3] xl:aspect-video" layout="row"
hideIfError
/> />
)}
<div className="min-w-0 flex-1 basis-0 space-y-2 overflow-hidden"> <div className="min-w-0 flex-1 basis-0 space-y-2 overflow-hidden">
{titleComponent} {titleComponent}
{summaryComponent} {summaryComponent}

28
src/components/Note/MarkdownArticle/MarkdownArticle.tsx

@ -1,5 +1,6 @@
import { useSecondaryPageOptional, useSmartHashtagNavigationOptional, useSmartRelayNavigationOptional } from '@/PageManager' import { useSecondaryPageOptional, useSmartHashtagNavigationOptional, useSmartRelayNavigationOptional } from '@/PageManager'
import Image from '@/components/Image' import Image from '@/components/Image'
import UserAvatar from '@/components/UserAvatar'
import MediaPlayer from '@/components/MediaPlayer' import MediaPlayer from '@/components/MediaPlayer'
import Wikilink from '@/components/UniversalContent/Wikilink' import Wikilink from '@/components/UniversalContent/Wikilink'
import { BookstrContent } from '@/components/Bookstr' import { BookstrContent } from '@/components/Bookstr'
@ -5899,12 +5900,39 @@ export default function MarkdownArticle({
<p className="break-words">{metadata.summary}</p> <p className="break-words">{metadata.summary}</p>
</blockquote> </blockquote>
)} )}
{!hideMetadata &&
event.kind === kinds.LongFormArticle &&
!metadata.image?.trim() && (
<div className="not-prose my-4 flex max-w-[400px] justify-center rounded-lg bg-muted p-6">
<UserAvatar
userId={event.pubkey}
size="large"
deferRemoteAvatar={false}
className="!h-36 !w-36 rounded-xl"
/>
</div>
)}
{hideMetadata && {hideMetadata &&
metadata.title && metadata.title &&
event.kind !== ExtendedKind.DISCUSSION && event.kind !== ExtendedKind.DISCUSSION &&
!isNip52CalendarCardKind(event.kind) && ( !isNip52CalendarCardKind(event.kind) && (
<h2 className="text-2xl font-bold mb-4 leading-tight break-words">{metadata.title}</h2> <h2 className="text-2xl font-bold mb-4 leading-tight break-words">{metadata.title}</h2>
)} )}
{hideMetadata &&
metadata.title &&
event.kind === kinds.LongFormArticle &&
!metadata.image?.trim() &&
event.kind !== ExtendedKind.DISCUSSION &&
!isNip52CalendarCardKind(event.kind) && (
<div className="not-prose mb-4 flex max-w-[400px] justify-center rounded-lg bg-muted p-6">
<UserAvatar
userId={event.pubkey}
size="large"
deferRemoteAvatar={false}
className="!h-36 !w-36 rounded-xl"
/>
</div>
)}
{/* Metadata image */} {/* Metadata image */}
{!hideMetadata && metadata.image && (() => { {!hideMetadata && metadata.image && (() => {

27
src/components/Note/WikiCard.tsx

@ -6,7 +6,7 @@ import { useContentPolicyOptional } from '@/providers/ContentPolicyProvider'
import { useScreenSizeOptional } from '@/providers/ScreenSizeProvider' import { useScreenSizeOptional } from '@/providers/ScreenSizeProvider'
import { Event, kinds } from 'nostr-tools' import { Event, kinds } from 'nostr-tools'
import { useMemo } from 'react' import { useMemo } from 'react'
import Image from '../Image' import ArticleCardCoverImage from './ArticleCardCoverImage'
export default function WikiCard({ export default function WikiCard({
event, event,
@ -60,13 +60,13 @@ export default function WikiCard({
className="cursor-pointer rounded-lg border p-4 hover:bg-muted/50 transition-colors" className="cursor-pointer rounded-lg border p-4 hover:bg-muted/50 transition-colors"
onClick={handleCardClick} onClick={handleCardClick}
> >
{metadata.image && autoLoadMedia && ( <ArticleCardCoverImage
<Image event={event}
image={{ url: metadata.image, pubkey: event.pubkey }} imageUrl={metadata.image}
className="w-full max-w-[400px] aspect-video mb-3" autoLoadMedia={autoLoadMedia}
hideIfError layout="stacked"
hideImageIfError
/> />
)}
<div className="space-y-2"> <div className="space-y-2">
{titleComponent} {titleComponent}
{summaryComponent} {summaryComponent}
@ -84,14 +84,13 @@ export default function WikiCard({
onClick={handleCardClick} onClick={handleCardClick}
> >
<div className="flex gap-4"> <div className="flex gap-4">
{metadata.image && autoLoadMedia && ( <ArticleCardCoverImage
<Image event={event}
image={{ url: metadata.image, pubkey: event.pubkey }} imageUrl={metadata.image}
classNames={{ wrapper: 'w-auto max-w-[400px] shrink-0' }} autoLoadMedia={autoLoadMedia}
className="rounded-lg aspect-[4/3] xl:aspect-video object-cover bg-foreground h-44 max-w-[400px]" layout="row"
hideIfError hideImageIfError
/> />
)}
<div className="flex-1 w-0 space-y-2"> <div className="flex-1 w-0 space-y-2">
{titleComponent} {titleComponent}
{summaryComponent} {summaryComponent}

3
src/components/ReplyNoteList/index.tsx

@ -775,7 +775,8 @@ function ReplyNoteList({
next.set(pkNorm, { next.set(pkNorm, {
pubkey: pkNorm, pubkey: pkNorm,
npub: pubkeyToNpub(pkNorm) ?? '', npub: pubkeyToNpub(pkNorm) ?? '',
username: formatPubkey(pkNorm) username: formatPubkey(pkNorm),
batchPlaceholder: true
}) })
} }
} }

4
src/lib/relay-list-builder.ts

@ -386,6 +386,10 @@ export async function buildReplyReadRelayList(
includeFastReadRelays: true, includeFastReadRelays: true,
includeSearchableRelays: true, includeSearchableRelays: true,
includeLocalRelays: 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 blockedRelays
}) })
} }

4
src/services/client-replaceable-events.service.ts

@ -1143,7 +1143,9 @@ export class ReplaceableEventService {
profiles.push({ profiles.push({
pubkey, pubkey,
npub: pubkeyToNpub(pubkey) ?? '', npub: pubkeyToNpub(pubkey) ?? '',
username: formatPubkey(pubkey) username: formatPubkey(pubkey),
/** Lets {@link useFetchProfile} retry per-pubkey when batch REQ missed kind 0. */
batchPlaceholder: true
}) })
} }
} }

Loading…
Cancel
Save