feat: bulk-remove for blocked users and hidden words

Long-press a row in Settings -> Security Filters to enter selection mode,
pick more rows, tap Unblock. The mute list (kind 10000) and block list
are rebuilt and re-broadcast once for all selected entries instead of
once per item.

- Quartz: add `MuteListEvent.removeAll` and `PeopleListEvent.removeAll`
  bulk overloads using the existing `TagArray.removeAny` primitive.
- Account / state classes: add `showUsers(List)` and `showWords(List)`
  that produce a single resigned event per list.
- AccountViewModel: thin wrappers `showUsers` / `showWords`.
- SecurityFiltersScreen: hoist per-tab selection sets, swap top bar
  in selection mode, add a local selectable user list (the shared
  `UserCompose` row was kept untouched).
This commit is contained in:
Claude
2026-05-02 22:38:26 +00:00
committed by greenart7c3
parent 4a789efa63
commit 76b69cd440
7 changed files with 370 additions and 14 deletions
@@ -37,6 +37,7 @@ import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.MuteTag
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
import com.vitorpamplona.quartz.nip51Lists.remove
import com.vitorpamplona.quartz.nip51Lists.removeAny
import com.vitorpamplona.quartz.utils.TimeUtils
@Immutable
@@ -132,6 +133,23 @@ class MuteListEvent(
)
}
suspend fun removeAll(
earlierVersion: MuteListEvent,
mutes: List<MuteTag>,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
): MuteListEvent {
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
val ids = mutes.map { it.toTagIdOnly() }
return resign(
privateTags = privateTags.removeAny(ids),
publicTags = earlierVersion.tags.removeAny(ids),
signer = signer,
createdAt = createdAt,
)
}
suspend fun resign(
publicTags: TagArray,
privateTags: TagArray,
@@ -40,6 +40,7 @@ import com.vitorpamplona.quartz.nip51Lists.encryption.PrivateTagsInContent
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.MuteTag
import com.vitorpamplona.quartz.nip51Lists.muteList.tags.UserTag
import com.vitorpamplona.quartz.nip51Lists.remove
import com.vitorpamplona.quartz.nip51Lists.removeAny
import com.vitorpamplona.quartz.nip51Lists.tags.DescriptionTag
import com.vitorpamplona.quartz.nip51Lists.tags.ImageTag
import com.vitorpamplona.quartz.nip51Lists.tags.NameTag
@@ -195,6 +196,23 @@ class PeopleListEvent(
)
}
suspend fun removeAll(
earlierVersion: PeopleListEvent,
persons: List<MuteTag>,
signer: NostrSigner,
createdAt: Long = TimeUtils.now(),
): PeopleListEvent {
val privateTags = earlierVersion.privateTags(signer) ?: throw SignerExceptions.UnauthorizedDecryptionException()
val ids = persons.map { it.toTagIdOnly() }
return resign(
privateTags = privateTags.removeAny(ids),
publicTags = earlierVersion.tags.removeAny(ids),
signer = signer,
createdAt = createdAt,
)
}
suspend fun remove(
earlierVersion: PeopleListEvent,
person: UserTag,