Migrate user blocks to standard NIP-51 Mute List

- Updates `js/userBlocks.js` to treat Kind 10000 (standard Mute List) as the primary write target.
- Public mutes are stored in `p` tags, and private blocks are stored in encrypted content within the same event.
- Legacy `d=user-blocks` lists are still read for backward compatibility (preferring the newest event), but no longer written.
- Updates `js/nostrEventSchemas.js` to mark `USER_BLOCK_LIST` as legacy and standardise `MUTE_LIST`.
- Updates UI controllers (`MoreMenuController`, `ProfileModalController`) to use "Mute/Block List" terminology and delegate all mute/block actions to `userBlocks`.
- Updates `ModerationService` to delegate viewer mute operations to `userBlocks` and disable direct Kind 10000 publishing to prevent overwrites.
- Documentation updated in `docs/nostr-event-schemas.md`.
- Fixes regressions in `more-menu-controller.test.mjs`, `moderation-service.test.mjs`, `trusted-mute-lists.test.mjs`, and `user-blocks.test.mjs`.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-01 21:47:29 +00:00
1 parent 4147211a3f
commit 8383a4f18b
10 files changed
+317 -475

No files matched your search

+7 -17
View File
@@ -1225,9 +1225,7 @@ export default class MoreMenuController {
}
try {
const result = await this.moderationService.addAuthorToViewerMuteList(
targetHex,
);
const result = await this.userBlocks.addMute(targetHex, viewerPubkey);
if (result?.already) {
this.callbacks.showSuccess("You already muted this creator.");
} else {
@@ -1251,21 +1249,18 @@ export default class MoreMenuController {
this.refreshActiveMuteButtons({ author: targetHex });
} catch (error) {
switch (error?.code) {
case "viewer-not-logged-in":
this.callbacks.showError("Please login to manage your mute list.");
break;
case "invalid-target":
case "invalid":
this.callbacks.showError("Unable to determine the creator to mute.");
break;
case "self":
this.callbacks.showError("You cannot mute yourself.");
break;
case "nostr-extension-missing":
case "signer-missing":
this.callbacks.showError(
"A Nostr extension is required to update your mute list.",
);
break;
case "extension-permission-denied":
case "extension-encryption-permission-denied":
this.callbacks.showError(
"Allow your Nostr extension to sign events before muting creators.",
);
@@ -1293,9 +1288,7 @@ export default class MoreMenuController {
}
try {
const result = await this.moderationService.removeAuthorFromViewerMuteList(
targetHex,
);
const result = await this.userBlocks.removeMute(targetHex, viewerPubkey);
if (result?.already) {
this.callbacks.showSuccess("This creator is not on your mute list.");
} else {
@@ -1319,15 +1312,12 @@ export default class MoreMenuController {
this.refreshActiveMuteButtons({ author: targetHex });
} catch (error) {
switch (error?.code) {
case "viewer-not-logged-in":
this.callbacks.showError("Please login to manage your mute list.");
break;
case "nostr-extension-missing":
case "signer-missing":
this.callbacks.showError(
"A Nostr extension is required to update your mute list.",
);
break;
case "extension-permission-denied":
case "extension-encryption-permission-denied":
this.callbacks.showError(
"Allow your Nostr extension to sign events before updating your mute list.",
);