8 changed files with 174 additions and 45 deletions
@ -0,0 +1,17 @@ |
|||||||
|
import { describe, expect, it } from 'vitest' |
||||||
|
import { kinds } from 'nostr-tools' |
||||||
|
import { filterEventsExcludingMutedAuthors } from './mute-set' |
||||||
|
|
||||||
|
describe('filterEventsExcludingMutedAuthors', () => { |
||||||
|
it('drops events from muted pubkeys', () => { |
||||||
|
const muted = 'a'.repeat(64) |
||||||
|
const other = 'b'.repeat(64) |
||||||
|
const events = [ |
||||||
|
{ kind: kinds.ShortTextNote, pubkey: muted, id: '1'.repeat(64), sig: 's', tags: [], content: '', created_at: 1 }, |
||||||
|
{ kind: kinds.ShortTextNote, pubkey: other, id: '2'.repeat(64), sig: 's', tags: [], content: '', created_at: 1 } |
||||||
|
] |
||||||
|
const out = filterEventsExcludingMutedAuthors(events, new Set([muted])) |
||||||
|
expect(out).toHaveLength(1) |
||||||
|
expect(out[0]?.pubkey).toBe(other) |
||||||
|
}) |
||||||
|
}) |
||||||
@ -1,7 +1,24 @@ |
|||||||
|
import type { Event } from 'nostr-tools' |
||||||
|
|
||||||
/** |
/** |
||||||
* Mute pubkey sets use lowercase hex so lookups match Nostr events and `p` tags regardless of casing. |
* Mute pubkey sets use lowercase hex so lookups match Nostr events and `p` tags regardless of casing. |
||||||
*/ |
*/ |
||||||
export function muteSetHas(mutePubkeySet: Set<string>, pubkey: string | undefined | null): boolean { |
export function muteSetHas(mutePubkeySet: ReadonlySet<string>, pubkey: string | undefined | null): boolean { |
||||||
if (!pubkey) return false |
if (!pubkey) return false |
||||||
return mutePubkeySet.has(pubkey.toLowerCase()) |
return mutePubkeySet.has(pubkey.toLowerCase()) |
||||||
} |
} |
||||||
|
|
||||||
|
/** Drop notes whose author is in the viewer's public or private mute list. */ |
||||||
|
export function filterEventsExcludingMutedAuthors( |
||||||
|
events: readonly Event[], |
||||||
|
mutePubkeySet: ReadonlySet<string> |
||||||
|
): Event[] { |
||||||
|
if (mutePubkeySet.size === 0) return [...events] |
||||||
|
return events.filter((ev) => !muteSetHas(mutePubkeySet, ev.pubkey)) |
||||||
|
} |
||||||
|
|
||||||
|
/** Stable SETTINGS / cache segment when mute lists change. */ |
||||||
|
export function mutePubkeySetFingerprint(mutePubkeySet: ReadonlySet<string>): string { |
||||||
|
if (mutePubkeySet.size === 0) return '0' |
||||||
|
return [...mutePubkeySet].sort().join('\n') |
||||||
|
} |
||||||
|
|||||||
@ -0,0 +1,35 @@ |
|||||||
|
import { describe, expect, it } from 'vitest' |
||||||
|
import { kinds } from 'nostr-tools' |
||||||
|
import { mergeInteractionEvents } from './ProfileInteractionsMap' |
||||||
|
|
||||||
|
function interaction(pubkey: string, pTags: string[]) { |
||||||
|
return { |
||||||
|
kind: kinds.ShortTextNote, |
||||||
|
pubkey, |
||||||
|
tags: pTags.map((p) => ['p', p]), |
||||||
|
content: '', |
||||||
|
id: `${pubkey.slice(0, 8)}${'c'.repeat(56)}`, |
||||||
|
sig: 's'.repeat(128), |
||||||
|
created_at: 1_700_000_000 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
describe('mergeInteractionEvents', () => { |
||||||
|
it('excludes muted partners and events authored by muted pubkeys', () => { |
||||||
|
const profile = 'a'.repeat(64) |
||||||
|
const partner = 'b'.repeat(64) |
||||||
|
const muted = 'f'.repeat(64) |
||||||
|
const cards = mergeInteractionEvents( |
||||||
|
profile, |
||||||
|
[ |
||||||
|
interaction(profile, [partner]), |
||||||
|
interaction(profile, [muted]), |
||||||
|
interaction(muted, [profile]), |
||||||
|
interaction(partner, [profile]) |
||||||
|
], |
||||||
|
new Set([muted]) |
||||||
|
) |
||||||
|
expect(cards.map((c) => c.pubkey)).toEqual([partner]) |
||||||
|
expect(cards[0]?.score).toBe(2) |
||||||
|
}) |
||||||
|
}) |
||||||
Loading…
Reference in new issue