11import '@bacons/text-decoder/install' ;
22import { createStore } from 'zustand/vanilla' ;
33import { useStore } from 'zustand' ;
4- import NDK , { NDKEvent , NDKFilter , NDKKind , NDKRelaySet , NDKSubscription , NDKSubscriptionOptions , wrapEvent } from '@nostr-dev-kit/ndk' ;
4+ import { NDKEvent , NDKFilter , NDKRelaySet , NDKSubscription , NDKSubscriptionOptions , wrapEvent } from '@nostr-dev-kit/ndk' ;
55import { useCallback , useEffect , useMemo , useRef } from 'react' ;
66import { useNDK } from './ndk.js' ;
77import { useNDKSession } from '../stores/session/index.js' ;
@@ -173,6 +173,7 @@ const createSubscribeStore = <T extends NDKEvent>(bufferMs: number | false = 30)
173173 * @returns {boolean } eose - End of stored events flag
174174 * @returns {boolean } isSubscribed - Subscription status
175175 */
176+
176177export const useSubscribe = < T extends NDKEvent > (
177178 filters : NDKFilter [ ] | false ,
178179 opts : UseSubscribeOptions = { } ,
@@ -181,7 +182,6 @@ export const useSubscribe = <T extends NDKEvent>(
181182 dependencies . push ( ! ! filters ) ;
182183
183184 const { ndk } = useNDK ( ) ;
184- const muteList = useNDKSession ( s => s . muteList ) ;
185185 const store = useMemo ( ( ) => createSubscribeStore < T > ( opts ?. bufferMs ) , dependencies ) ;
186186 const storeInstance = useStore ( store ) ;
187187
@@ -202,22 +202,23 @@ export const useSubscribe = <T extends NDKEvent>(
202202 return undefined ;
203203 } , [ ndk , opts . relays ] ) ;
204204
205+ const isMutedEvent = useMuteFilter ( ) ;
206+
205207 useEffect ( ( ) => {
206208 // go through the events and remove any that are from muted pubkeys
207209 storeInstance . events . forEach ( ( event ) => {
208- if ( muteList . has ( event . pubkey ) ) {
210+ if ( isMutedEvent ( event ) ) {
209211 storeInstance . removeEventId ( event . id ) ;
210212 }
211213 } ) ;
212-
213- } , [ muteList . size ] )
214+ } , [ isMutedEvent ] )
214215
215216 const handleEvent = useCallback (
216217 ( event : NDKEvent ) => {
217218 const id = event . tagId ( ) ;
218219
219220 // if it's from a muted pubkey, we don't accept it
220- if ( opts ?. includeMuted !== true && muteList . has ( event . pubkey ) ) return false ;
221+ if ( opts ?. includeMuted !== true && isMutedEvent ( event ) ) return false ;
221222
222223 if ( opts ?. includeDeleted !== true && event . isParamReplaceable ( ) && event . hasTag ( 'deleted' ) ) {
223224 // We mark the event but we don't add the actual event, since
@@ -240,7 +241,7 @@ export const useSubscribe = <T extends NDKEvent>(
240241 storeInstance . addEvent ( event as T ) ;
241242 eventIds . current . set ( id , event . created_at ! ) ;
242243 } ,
243- [ muteList , ...dependencies ]
244+ [ isMutedEvent , ...dependencies ]
244245 ) ;
245246
246247 const handleEose = ( ) => {
@@ -285,3 +286,50 @@ export const useSubscribe = <T extends NDKEvent>(
285286 subscription : storeInstance . subscriptionRef ,
286287 } ;
287288} ;
289+
290+
291+ /**
292+ * Provides a function that filters events, according to the user's mute list.
293+ */
294+ export function useMuteFilter ( ) {
295+ const mutedPubkeys = useNDKSession ( s => s . mutedPubkeys ) ;
296+ const mutedHashtags = useNDKSession ( s => s . mutedHashtags ) ;
297+ const mutedWords = useNDKSession ( s => s . mutedWords ) ;
298+ const mutedEventIds = useNDKSession ( s => s . mutedEventIds ) ;
299+
300+ const isMutedEvent = useMemo ( ( ) => {
301+ const mutedWordsRegex = mutedWords . size > 0 ? new RegExp ( Array . from ( mutedWords ) . join ( '|' ) , 'i' ) : null ;
302+ const _mutedHashtags = new Set < string > ( ) ;
303+ mutedHashtags . forEach ( h => _mutedHashtags . add ( h . toLowerCase ( ) ) ) ;
304+
305+ return ( event : NDKEvent ) => {
306+ const start = performance . now ( ) ;
307+ const tags = new Set ( event . getMatchingTags ( 't' ) . map ( tag => tag [ 1 ] . toLowerCase ( ) ) ) ;
308+ const taggedEvents = new Set ( event . getMatchingTags ( 'e' ) . map ( tag => tag [ 1 ] ) ) ;
309+ taggedEvents . add ( event . id ) ;
310+ const hasMutedHashtag = setHasAnyIntersection ( _mutedHashtags , tags ) ;
311+ let res = false ;
312+
313+ if (
314+ hasMutedHashtag ||
315+ mutedPubkeys . has ( event . pubkey ) ||
316+ ( mutedWordsRegex && event . content . match ( mutedWordsRegex ) )
317+ ) res = true ;
318+
319+ if ( ! res && setHasAnyIntersection ( mutedEventIds , taggedEvents ) ) {
320+ res = true ;
321+ }
322+
323+ return res ;
324+ }
325+ } , [ mutedPubkeys , mutedHashtags , mutedWords , mutedEventIds ] ) ;
326+
327+ return isMutedEvent ;
328+ }
329+
330+ const setHasAnyIntersection = ( set1 : Set < string > , set2 : Set < string > ) => {
331+ for ( const item of set1 ) {
332+ if ( set2 . has ( item ) ) return true ;
333+ }
334+ return false ;
335+ }
0 commit comments