fix: apply block/mute list filtering to DM messages and conversations

The user's block/mute list (NIP-51) was not being checked when
processing direct messages, allowing blocked users' messages to appear
in conversations, trigger notifications, and increment unseen counts.

This adds block filtering at four layers:
- nostrService.applyDirectMessage: drops incoming DMs from blocked users
  before they enter the in-memory message cache
- nostrService.persistDirectMessageRecord: prevents blocked DM senders
  from being persisted to IndexedDB or triggering notifications
- profileModalController.buildDmConversationData: filters blocked users
  from the conversation list UI (catches pre-block persisted messages)
- applicationBootstrap: excludes blocked-user conversations from the
  unread DM badge count

The block checker is injected via setDmBlockChecker() to keep
nostrService decoupled from userBlocks, wired up in app.js init().

https://claude.ai/code/session_01EAVLtAM14GN99Eik8otLg2
This commit is contained in:
Claude committed 2026-02-06 19:41:50 +00:00
1 parent 3d9cbe1c08
commit 416a809ae4
5 files changed
+246 -1

No files matched your search

+35
View File
@@ -635,6 +635,7 @@ export class NostrService {
logger: userLogger,
});
this.dmPreviewCache = new LRUCache({ maxSize: DM_PREVIEW_CACHE_LIMIT });
this._dmBlockChecker = null;
try {
if (this.moderationService && typeof this.moderationService.setNostrClient === "function") {
this.moderationService.setNostrClient(this.nostrClient);
@@ -644,6 +645,28 @@ export class NostrService {
}
}
setDmBlockChecker(checker) {
this._dmBlockChecker =
typeof checker === "function" ? checker : null;
}
_isDmRemoteBlocked(message, actorPubkey) {
if (typeof this._dmBlockChecker !== "function") {
return false;
}
try {
const actor = normalizeHexPubkey(actorPubkey || this.dmActorPubkey || "");
const remote = resolveDirectMessageRemotePubkey(message, actor);
if (!remote) {
return false;
}
return this._dmBlockChecker(remote);
} catch (error) {
devLogger.warn("[nostrService] Block check failed for DM", error);
return false;
}
}
log(...args) {
try {
this.logger(...args);
@@ -810,6 +833,13 @@ export class NostrService {
return null;
}
if (this._isDmRemoteBlocked(message, actorPubkey)) {
devLogger.log("[nostrService] Skipping persist for blocked DM sender", {
id: record.id,
});
return null;
}
try {
await writeMessages(record);
const notificationResult = await this.dmNotificationManager.recordMessage({
@@ -876,6 +906,11 @@ export class NostrService {
return;
}
if (this._isDmRemoteBlocked(normalized)) {
devLogger.log("[nostrService] Dropping DM from blocked user", { eventId });
return;
}
this.dmMessageIndex.set(eventId, normalized);
const actor = normalizeHexPubkey(