Browse Source

bug-fix

imwald
Silberengel 3 weeks ago
parent
commit
14871246e8
  1. 16
      src/components/NoteList/index.tsx
  2. 8
      src/components/ReplyNoteList/index.tsx
  3. 91
      src/lib/superchat.test.ts
  4. 53
      src/lib/superchat.ts
  5. 3
      src/pages/primary/SpellsPage/index.tsx
  6. 3
      src/pages/primary/SpellsPage/useSpellsPageFeed.ts

16
src/components/NoteList/index.tsx

@ -798,7 +798,9 @@ const NoteList = forwardRef( @@ -798,7 +798,9 @@ const NoteList = forwardRef(
*/
alexandriaEmptyUrl = null,
/** Notifications feed: show attest-superchat bar on incoming payment cards. */
showPaymentAttestationAction = false
showPaymentAttestationAction = false,
/** Notifications feed: show unattested kind 9734 / 9735 / 9740 / 9736 / 1814 addressed to this pubkey. */
incomingPaymentRecipientPubkey = null
}: {
subRequests: TFeedSubRequest[]
showKinds: number[]
@ -861,6 +863,7 @@ const NoteList = forwardRef( @@ -861,6 +863,7 @@ const NoteList = forwardRef(
/** Optional Alexandria `/events` URL when this feed’s timeline is empty (search / tag browse). */
alexandriaEmptyUrl?: string | null
showPaymentAttestationAction?: boolean
incomingPaymentRecipientPubkey?: string | null
},
ref
) => {
@ -1363,8 +1366,14 @@ const NoteList = forwardRef( @@ -1363,8 +1366,14 @@ const NoteList = forwardRef(
// Filter out expired events
if (shouldFilterEvent(evt)) return true
// Attested superchats only (9741), same as threads / profile walls.
if (!shouldIncludePaymentInFeed(evt, feedAttestedSuperchatIds)) {
// Attested superchats only (9741), except incoming payments in notifications.
if (
!shouldIncludePaymentInFeed(
evt,
feedAttestedSuperchatIds,
incomingPaymentRecipientPubkey
)
) {
return true
}
@ -1395,6 +1404,7 @@ const NoteList = forwardRef( @@ -1395,6 +1404,7 @@ const NoteList = forwardRef(
pinnedEventIds,
isEventDeleted,
feedAttestedSuperchatIds,
incomingPaymentRecipientPubkey,
extraShouldHideEvent,
homeFeedActiveSeenOnAllowlist,
homeFeedListMode

8
src/components/ReplyNoteList/index.tsx

@ -1,15 +1,9 @@ @@ -1,15 +1,9 @@
import { ExtendedKind } from '@/constants'
import { isDiscussionDownvoteEmoji, isDiscussionUpvoteEmoji } from '@/lib/discussion-votes'
import {
canonicalizeRssArticleUrl,
getArticleUrlFromCommentITags
} from '@/lib/rss-article'
import {
getParentETag,
getReplaceableCoordinateFromEvent,
isMentioningMutedUsers,
isNip18RepostKind,
isReplaceableEvent
isNip18RepostKind
} from '@/lib/event'
import logger from '@/lib/logger'
import {

91
src/lib/superchat.test.ts

@ -9,6 +9,7 @@ import { @@ -9,6 +9,7 @@ import {
getSuperchatPaytoType,
getSuperchatReferenceFetchId,
canUserAttestSuperchatPayment,
isIncomingNotificationsPaymentEvent,
isProfileWallPaymentNotification,
isProfileWallZapReceipt,
isNestedThreadReplyParentKind,
@ -289,6 +290,96 @@ describe('shouldIncludePaymentInFeed', () => { @@ -289,6 +290,96 @@ describe('shouldIncludePaymentInFeed', () => {
expect(shouldIncludePaymentInFeed(zap, new Set())).toBe(false)
expect(shouldIncludePaymentInFeed(note, attested)).toBe(true)
})
it('includes unattested incoming payments for the notifications recipient only', () => {
const zap = fakeEvent({
id: ZAP_ID,
kind: kinds.Zap,
tags: [
['P', SENDER],
['p', RECIPIENT],
['bolt11', 'lnbc210n1p0fake'],
[
'description',
JSON.stringify({
pubkey: SENDER,
content: 'Zap!',
tags: [['p', RECIPIENT], ['amount', '21000']]
})
]
]
})
const payment = fakeEvent({
id: PAYMENT_ID,
kind: ExtendedKind.PAYMENT_NOTIFICATION,
tags: [['p', RECIPIENT], ['amount', '100000']]
})
const moneroDisclosure = fakeEvent({
id: 'a'.repeat(64),
kind: ExtendedKind.MONERO_TIP_DISCLOSURE,
tags: [['p', RECIPIENT], ['amount', '0.01']]
})
const moneroReceipt = fakeEvent({
id: 'b'.repeat(64),
kind: ExtendedKind.MONERO_TIP_RECEIPT,
tags: [['p', SENDER], ['p', RECIPIENT]],
content: JSON.stringify({ txid: 'abc', message: 'tip' })
})
const zapRequest = fakeEvent({
id: 'c'.repeat(64),
kind: ExtendedKind.ZAP_REQUEST,
pubkey: SENDER,
tags: [['p', RECIPIENT], ['amount', '21000']]
})
const empty = new Set<string>()
expect(shouldIncludePaymentInFeed(zap, empty, RECIPIENT)).toBe(true)
expect(shouldIncludePaymentInFeed(payment, empty, RECIPIENT)).toBe(true)
expect(shouldIncludePaymentInFeed(moneroDisclosure, empty, RECIPIENT)).toBe(true)
expect(shouldIncludePaymentInFeed(moneroReceipt, empty, RECIPIENT)).toBe(true)
expect(shouldIncludePaymentInFeed(zapRequest, empty, RECIPIENT)).toBe(true)
expect(shouldIncludePaymentInFeed(zap, empty, SENDER)).toBe(false)
expect(shouldIncludePaymentInFeed(zap, empty)).toBe(false)
})
})
describe('isIncomingNotificationsPaymentEvent', () => {
it('matches all payment kinds addressed to the recipient', () => {
expect(
isIncomingNotificationsPaymentEvent(
fakeEvent({
kind: ExtendedKind.ZAP_REQUEST,
tags: [['p', RECIPIENT], ['amount', '21000']]
}),
RECIPIENT
)
).toBe(true)
expect(
isIncomingNotificationsPaymentEvent(
fakeEvent({
kind: ExtendedKind.MONERO_TIP_DISCLOSURE,
tags: [['p', RECIPIENT], ['amount', '0.01']]
}),
RECIPIENT
)
).toBe(true)
expect(
isIncomingNotificationsPaymentEvent(
fakeEvent({
kind: ExtendedKind.MONERO_TIP_RECEIPT,
tags: [['p', SENDER], ['p', RECIPIENT]],
content: '{}'
}),
RECIPIENT
)
).toBe(true)
expect(
isIncomingNotificationsPaymentEvent(
fakeEvent({ kind: kinds.ShortTextNote, tags: [['p', RECIPIENT]] }),
RECIPIENT
)
).toBe(false)
})
})
describe('getPaymentNotificationInfo', () => {

53
src/lib/superchat.ts

@ -205,18 +205,47 @@ export function canUserAttestSuperchatPayment( @@ -205,18 +205,47 @@ export function canUserAttestSuperchatPayment(
if (!isAttestableSuperchatPayment(event)) return false
const resolved = attestationRecipientPubkey ?? getSuperchatPaymentRecipientPubkey(event)
if (resolved && hexPubkeysEqual(resolved, userPubkey)) return true
const pTag = firstTagValue(event.tags, ['p'])
return Boolean(pTag && hexPubkeysEqual(pTag, userPubkey))
return event.tags.some((t) => t[0] === 'p' && t[1] && hexPubkeysEqual(t[1], userPubkey))
}
/** Incoming payment notification or zap receipt addressed to `userPubkey`. */
export function isIncomingPaymentNotificationOrZapReceipt(
/** Payment / tip kinds that may appear in the notifications feed (9734–9736, 1814, 9740). */
export function isIncomingNotificationsPaymentKind(kind: number): boolean {
return (
kind === ExtendedKind.ZAP_REQUEST ||
kind === kinds.Zap ||
kind === ExtendedKind.ZAP_RECEIPT ||
kind === ExtendedKind.PAYMENT_NOTIFICATION ||
isMoneroTipKind(kind)
)
}
/**
* Incoming payment or tip addressed to `userPubkey` shown unattested in notifications only.
* Covers kind 9734 (zap request), 9735, 9740, 9736, and 1814.
*/
export function isIncomingNotificationsPaymentEvent(
event: Event,
userPubkey: string,
attestationRecipientPubkey?: string | null
): boolean {
if (!isIncomingNotificationsPaymentKind(event.kind)) return false
if (event.kind === ExtendedKind.ZAP_REQUEST) {
return event.tags.some((t) => t[0] === 'p' && t[1] && hexPubkeysEqual(t[1], userPubkey))
}
if (isAttestableSuperchatPayment(event)) {
return canUserAttestSuperchatPayment(event, userPubkey, attestationRecipientPubkey)
}
return false
}
/** @deprecated Use {@link isIncomingNotificationsPaymentEvent}. */
export function isIncomingPaymentNotificationOrZapReceipt(
event: Event,
userPubkey: string,
attestationRecipientPubkey?: string | null
): boolean {
return isIncomingNotificationsPaymentEvent(event, userPubkey, attestationRecipientPubkey)
}
/** Target `k` tag value for a kind 9741 attestation pointing at this event. */
export function getSuperchatAttestationTargetKindValue(event: Event): string | null {
@ -324,13 +353,25 @@ export function buildGlobalAttestedSuperchatIdSet(attestations: Event[]): Set<st @@ -324,13 +353,25 @@ export function buildGlobalAttestedSuperchatIdSet(attestations: Event[]): Set<st
/**
* Feeds: kind 9735 / 9740 / 9736 / 1814 only when attested (9741).
* Same attestation rule as threads and profile walls.
*
* When `incomingPaymentRecipientPubkey` is set (notifications feed), unattested kind
* 9734 / 9735 / 9740 / 9736 / 1814 addressed to that pubkey are included so the recipient
* can publish kind 9741 from the card (9734 is shown but not attestable).
*/
export function shouldIncludePaymentInFeed(
event: Event,
attestedIds: ReadonlySet<string>
attestedIds: ReadonlySet<string>,
incomingPaymentRecipientPubkey?: string | null
): boolean {
if (!isSuperchatKind(event.kind)) return true
return isAttestedSuperchat(event, attestedIds)
if (isAttestedSuperchat(event, attestedIds)) return true
if (
incomingPaymentRecipientPubkey &&
isIncomingNotificationsPaymentEvent(event, incomingPaymentRecipientPubkey)
) {
return true
}
return false
}
export function replyFeedSuperchatsFirst(sortedNonSuperchatReplies: Event[], superchats: Event[]) {

3
src/pages/primary/SpellsPage/index.tsx

@ -1102,6 +1102,9 @@ const SpellsPage = forwardRef<TPageRef>(function SpellsPage( @@ -1102,6 +1102,9 @@ const SpellsPage = forwardRef<TPageRef>(function SpellsPage(
: undefined
}
showPaymentAttestationAction={selectedFauxSpell === 'notifications'}
incomingPaymentRecipientPubkey={
selectedFauxSpell === 'notifications' ? notificationsFeedPubkey : null
}
/>
</div>
</>

3
src/pages/primary/SpellsPage/useSpellsPageFeed.ts

@ -17,6 +17,7 @@ import { @@ -17,6 +17,7 @@ import {
parseThreadWatchListRefs,
threadWatchMatchesRefs
} from '@/lib/notification-thread-watch'
import { isIncomingNotificationsPaymentEvent } from '@/lib/superchat'
import {
decodeFollowSetSpellId,
getFollowSetDTag,
@ -600,6 +601,8 @@ export function useSpellsPageFeed(a: UseSpellsPageFeedArgs) { @@ -600,6 +601,8 @@ export function useSpellsPageFeed(a: UseSpellsPageFeedArgs) {
return true
}
if (isIncomingNotificationsPaymentEvent(evt, pk)) return false
if (isUserInEventMentions(evt, pk)) return false
if (

Loading…
Cancel
Save