fix: 🐛
This commit is contained in:
@@ -1,7 +1,11 @@
|
|||||||
import NewNotesButton from '@/components/NewNotesButton'
|
import NewNotesButton from '@/components/NewNotesButton'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
||||||
import { isReplyNoteEvent } from '@/lib/event'
|
import {
|
||||||
|
getReplaceableCoordinateFromEvent,
|
||||||
|
isReplaceableEvent,
|
||||||
|
isReplyNoteEvent
|
||||||
|
} from '@/lib/event'
|
||||||
import { checkAlgoRelay } from '@/lib/relay'
|
import { checkAlgoRelay } from '@/lib/relay'
|
||||||
import { isSafari } from '@/lib/utils'
|
import { isSafari } from '@/lib/utils'
|
||||||
import { useMuteList } from '@/providers/MuteListProvider'
|
import { useMuteList } from '@/providers/MuteListProvider'
|
||||||
@@ -296,6 +300,8 @@ export default function NoteList({
|
|||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const idSet = new Set<string>()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Tabs
|
<Tabs
|
||||||
@@ -338,11 +344,17 @@ export default function NoteList({
|
|||||||
<div className="min-h-screen">
|
<div className="min-h-screen">
|
||||||
{events
|
{events
|
||||||
.slice(0, showCount)
|
.slice(0, showCount)
|
||||||
.filter(
|
.filter((event: Event) => {
|
||||||
(event: Event) =>
|
const id = isReplaceableEvent(event.kind)
|
||||||
|
? getReplaceableCoordinateFromEvent(event)
|
||||||
|
: event.id
|
||||||
|
if (idSet.has(id)) return false
|
||||||
|
idSet.add(id)
|
||||||
|
return (
|
||||||
(listMode !== 'posts' || !isReplyNoteEvent(event)) &&
|
(listMode !== 'posts' || !isReplyNoteEvent(event)) &&
|
||||||
(skipTrustCheck || !hideUntrustedNotes || isUserTrusted(event.pubkey))
|
(skipTrustCheck || !hideUntrustedNotes || isUserTrusted(event.pubkey))
|
||||||
)
|
)
|
||||||
|
})
|
||||||
.map((event) => (
|
.map((event) => (
|
||||||
<NoteCard
|
<NoteCard
|
||||||
key={event.id}
|
key={event.id}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
||||||
import {
|
import {
|
||||||
getParentETag,
|
getParentETag,
|
||||||
getReplaceableEventCoordinate,
|
getReplaceableCoordinateFromEvent,
|
||||||
getRootATag,
|
getRootATag,
|
||||||
getRootETag,
|
getRootETag,
|
||||||
getRootEventHexId,
|
getRootEventHexId,
|
||||||
@@ -37,7 +37,7 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||||||
const replyIdSet = new Set<string>()
|
const replyIdSet = new Set<string>()
|
||||||
const replyEvents: NEvent[] = []
|
const replyEvents: NEvent[] = []
|
||||||
const currentEventKey = isReplaceableEvent(event.kind)
|
const currentEventKey = isReplaceableEvent(event.kind)
|
||||||
? getReplaceableEventCoordinate(event)
|
? getReplaceableCoordinateFromEvent(event)
|
||||||
: event.id
|
: event.id
|
||||||
let parentEventKeys = [currentEventKey]
|
let parentEventKeys = [currentEventKey]
|
||||||
while (parentEventKeys.length > 0) {
|
while (parentEventKeys.length > 0) {
|
||||||
@@ -64,7 +64,7 @@ export default function ReplyNoteList({ index, event }: { index?: number; event:
|
|||||||
let root: TRootInfo = isReplaceableEvent(event.kind)
|
let root: TRootInfo = isReplaceableEvent(event.kind)
|
||||||
? {
|
? {
|
||||||
type: 'A',
|
type: 'A',
|
||||||
id: getReplaceableEventCoordinate(event),
|
id: getReplaceableCoordinateFromEvent(event),
|
||||||
eventId: event.id,
|
eventId: event.id,
|
||||||
pubkey: event.pubkey,
|
pubkey: event.pubkey,
|
||||||
relay: client.getEventHint(event.id)
|
relay: client.getEventHint(event.id)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Event, kinds, nip19 } from 'nostr-tools'
|
import { Event, kinds, nip19 } from 'nostr-tools'
|
||||||
import {
|
import {
|
||||||
getReplaceableEventCoordinate,
|
getReplaceableCoordinateFromEvent,
|
||||||
getRootETag,
|
getRootETag,
|
||||||
isProtectedEvent,
|
isProtectedEvent,
|
||||||
isReplaceableEvent
|
isReplaceableEvent
|
||||||
@@ -552,7 +552,7 @@ function extractImagesFromContent(content: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildATag(event: Event, upperCase: boolean = false) {
|
function buildATag(event: Event, upperCase: boolean = false) {
|
||||||
const coordinate = getReplaceableEventCoordinate(event)
|
const coordinate = getReplaceableCoordinateFromEvent(event)
|
||||||
const hint = client.getEventHint(event.id)
|
const hint = client.getEventHint(event.id)
|
||||||
return trimTagEnd([upperCase ? 'A' : 'a', coordinate, hint])
|
return trimTagEnd([upperCase ? 'A' : 'a', coordinate, hint])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,9 +149,13 @@ export function getRootBech32Id(event?: Event) {
|
|||||||
return generateBech32IdFromETag(eTag)
|
return generateBech32IdFromETag(eTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getReplaceableEventCoordinate(event: Event) {
|
export function getReplaceableCoordinate(kind: number, pubkey: string, d: string = '') {
|
||||||
|
return `${kind}:${pubkey}:${d}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getReplaceableCoordinateFromEvent(event: Event) {
|
||||||
const d = event.tags.find(tagNameEquals('d'))?.[1]
|
const d = event.tags.find(tagNameEquals('d'))?.[1]
|
||||||
return `${event.kind}:${event.pubkey}:${d ?? ''}`
|
return getReplaceableCoordinate(event.kind, event.pubkey, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNoteBech32Id(event: Event) {
|
export function getNoteBech32Id(event: Event) {
|
||||||
@@ -242,3 +246,26 @@ export function createFakeEvent(event: Partial<Event>): Event {
|
|||||||
...event
|
...event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Legacy compare function for sorting compatibility
|
||||||
|
// If return 0, it means the two events are equal.
|
||||||
|
// If return a negative number, it means `b` should be retained, and `a` should be discarded.
|
||||||
|
// If return a positive number, it means `a` should be retained, and `b` should be discarded.
|
||||||
|
export function compareEvents(a: Event, b: Event): number {
|
||||||
|
if (a.created_at !== b.created_at) {
|
||||||
|
return a.created_at - b.created_at
|
||||||
|
}
|
||||||
|
// In case of replaceable events with the same timestamp, the event with the lowest id (first in lexical order) should be retained, and the other discarded.
|
||||||
|
if (a.id !== b.id) {
|
||||||
|
return a.id < b.id ? 1 : -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the event that should be retained when comparing two events
|
||||||
|
export function getRetainedEvent(a: Event, b: Event): Event {
|
||||||
|
if (compareEvents(a, b) > 0) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
import { BIG_RELAY_URLS, ExtendedKind } from '@/constants'
|
||||||
import { getLatestEvent } from '@/lib/event'
|
import {
|
||||||
|
compareEvents,
|
||||||
|
getLatestEvent,
|
||||||
|
getReplaceableCoordinate,
|
||||||
|
getReplaceableCoordinateFromEvent,
|
||||||
|
isReplaceableEvent
|
||||||
|
} from '@/lib/event'
|
||||||
import { getProfileFromEvent, getRelayListFromEvent } from '@/lib/event-metadata'
|
import { getProfileFromEvent, getRelayListFromEvent } from '@/lib/event-metadata'
|
||||||
import { formatPubkey, pubkeyToNpub, userIdToPubkey } from '@/lib/pubkey'
|
import { formatPubkey, pubkeyToNpub, userIdToPubkey } from '@/lib/pubkey'
|
||||||
import { getPubkeysFromPTags, getServersFromServerTags } from '@/lib/tag'
|
import { getPubkeysFromPTags, getServersFromServerTags } from '@/lib/tag'
|
||||||
@@ -42,6 +48,7 @@ class ClientService extends EventTarget {
|
|||||||
| string[]
|
| string[]
|
||||||
| undefined
|
| undefined
|
||||||
> = {}
|
> = {}
|
||||||
|
private replaceableEventCacheMap = new Map<string, NEvent>()
|
||||||
private eventCacheMap = new Map<string, Promise<NEvent | undefined>>()
|
private eventCacheMap = new Map<string, Promise<NEvent | undefined>>()
|
||||||
private eventDataLoader = new DataLoader<string, NEvent | undefined>(
|
private eventDataLoader = new DataLoader<string, NEvent | undefined>(
|
||||||
(ids) => Promise.all(ids.map((id) => this._fetchEvent(id))),
|
(ids) => Promise.all(ids.map((id) => this._fetchEvent(id))),
|
||||||
@@ -438,6 +445,13 @@ class ClientService extends EventTarget {
|
|||||||
startLogin,
|
startLogin,
|
||||||
onevent: (evt: NEvent) => {
|
onevent: (evt: NEvent) => {
|
||||||
that.eventDataLoader.prime(evt.id, Promise.resolve(evt))
|
that.eventDataLoader.prime(evt.id, Promise.resolve(evt))
|
||||||
|
if (isReplaceableEvent(evt.kind)) {
|
||||||
|
const coordinate = getReplaceableCoordinateFromEvent(evt)
|
||||||
|
const cachedEvent = that.replaceableEventCacheMap.get(coordinate)
|
||||||
|
if (!cachedEvent || compareEvents(evt, cachedEvent) > 0) {
|
||||||
|
that.replaceableEventCacheMap.set(coordinate, evt)
|
||||||
|
}
|
||||||
|
}
|
||||||
// not eosed yet, push to events
|
// not eosed yet, push to events
|
||||||
if (!eosedAt) {
|
if (!eosedAt) {
|
||||||
return events.push(evt)
|
return events.push(evt)
|
||||||
@@ -635,6 +649,7 @@ class ClientService extends EventTarget {
|
|||||||
async fetchEvent(id: string): Promise<NEvent | undefined> {
|
async fetchEvent(id: string): Promise<NEvent | undefined> {
|
||||||
if (!/^[0-9a-f]{64}$/.test(id)) {
|
if (!/^[0-9a-f]{64}$/.test(id)) {
|
||||||
let eventId: string | undefined
|
let eventId: string | undefined
|
||||||
|
let coordinate: string | undefined
|
||||||
const { type, data } = nip19.decode(id)
|
const { type, data } = nip19.decode(id)
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'note':
|
case 'note':
|
||||||
@@ -643,8 +658,16 @@ class ClientService extends EventTarget {
|
|||||||
case 'nevent':
|
case 'nevent':
|
||||||
eventId = data.id
|
eventId = data.id
|
||||||
break
|
break
|
||||||
|
case 'naddr':
|
||||||
|
coordinate = getReplaceableCoordinate(data.kind, data.pubkey, data.identifier)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
if (eventId) {
|
if (coordinate) {
|
||||||
|
const cache = this.replaceableEventCacheMap.get(coordinate)
|
||||||
|
if (cache) {
|
||||||
|
return cache
|
||||||
|
}
|
||||||
|
} else if (eventId) {
|
||||||
const cache = this.eventCacheMap.get(eventId)
|
const cache = this.eventCacheMap.get(eventId)
|
||||||
if (cache) {
|
if (cache) {
|
||||||
return cache
|
return cache
|
||||||
|
|||||||
Reference in New Issue
Block a user