Migrates to MuteList 10000

This commit is contained in:
Vitor Pamplona
2023-11-23 10:46:56 -05:00
parent 954330064d
commit 70964e680a
22 changed files with 340 additions and 164 deletions
@@ -41,6 +41,7 @@ import com.vitorpamplona.quartz.events.Event
import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.FileStorageEvent import com.vitorpamplona.quartz.events.FileStorageEvent
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
import com.vitorpamplona.quartz.events.GeneralListEvent
import com.vitorpamplona.quartz.events.GenericRepostEvent import com.vitorpamplona.quartz.events.GenericRepostEvent
import com.vitorpamplona.quartz.events.GiftWrapEvent import com.vitorpamplona.quartz.events.GiftWrapEvent
import com.vitorpamplona.quartz.events.HTTPAuthorizationEvent import com.vitorpamplona.quartz.events.HTTPAuthorizationEvent
@@ -51,6 +52,7 @@ import com.vitorpamplona.quartz.events.LnZapPaymentRequestEvent
import com.vitorpamplona.quartz.events.LnZapPaymentResponseEvent import com.vitorpamplona.quartz.events.LnZapPaymentResponseEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent import com.vitorpamplona.quartz.events.LnZapRequestEvent
import com.vitorpamplona.quartz.events.MetadataEvent import com.vitorpamplona.quartz.events.MetadataEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.NIP24Factory import com.vitorpamplona.quartz.events.NIP24Factory
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.events.PollNoteEvent import com.vitorpamplona.quartz.events.PollNoteEvent
@@ -71,6 +73,7 @@ import com.vitorpamplona.quartz.signers.NostrSignerInternal
import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.persistentSetOf
import kotlinx.collections.immutable.toImmutableSet import kotlinx.collections.immutable.toImmutableSet
import kotlinx.collections.immutable.toPersistentSet
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -299,7 +302,7 @@ class Account(
} }
private fun decryptLiveFollows(peopleListFollows: NoteState?, onReady: (LiveFollowLists) -> Unit) { private fun decryptLiveFollows(peopleListFollows: NoteState?, onReady: (LiveFollowLists) -> Unit) {
val listEvent = (peopleListFollows?.note?.event as? PeopleListEvent) val listEvent = (peopleListFollows?.note?.event as? GeneralListEvent)
listEvent?.privateTags(signer) { privateTagList -> listEvent?.privateTags(signer) { privateTagList ->
onReady( onReady(
LiveFollowLists( LiveFollowLists(
@@ -321,27 +324,37 @@ class Account(
) )
val flowHiddenUsers: StateFlow<LiveHiddenUsers> by lazy { val flowHiddenUsers: StateFlow<LiveHiddenUsers> by lazy {
combineTransform(live.asFlow(), getBlockListNote().flow().metadata.stateFlow) { localLive, blockList -> combineTransform(
live.asFlow(),
getBlockListNote().flow().metadata.stateFlow,
getMuteListNote().flow().metadata.stateFlow
) { localLive, blockList, muteList ->
checkNotInMainThread() checkNotInMainThread()
val result = withTimeoutOrNull(1000) { val resultBlockList = withTimeoutOrNull(1000) {
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
(blockList.note.event as? PeopleListEvent)?.publicAndPrivateUsersAndWords(signer) { (blockList.note.event as? PeopleListEvent)?.publicAndPrivateUsersAndWords(signer) {
continuation.resume(it) continuation.resume(it)
} }
} }
} } ?: PeopleListEvent.UsersAndWords()
result?.let { val resultMuteList = withTimeoutOrNull(1000) {
emit( suspendCancellableCoroutine { continuation ->
LiveHiddenUsers( (muteList.note.event as? MuteListEvent)?.publicAndPrivateUsersAndWords(signer) {
hiddenUsers = it.users, continuation.resume(it)
hiddenWords = it.words, }
spammers = localLive.account.transientHiddenUsers, }
showSensitiveContent = localLive.account.showSensitiveContent } ?: PeopleListEvent.UsersAndWords()
)
emit(
LiveHiddenUsers(
hiddenUsers = (resultBlockList.users + resultMuteList.users).toPersistentSet(),
hiddenWords = (resultBlockList.words + resultMuteList.words).toPersistentSet(),
spammers = localLive.account.transientHiddenUsers,
showSensitiveContent = localLive.account.showSensitiveContent
) )
} )
}.stateIn( }.stateIn(
scope, scope,
SharingStarted.Eagerly, SharingStarted.Eagerly,
@@ -1425,38 +1438,45 @@ class Account(
return LocalCache.getOrCreateAddressableNote(aTag) return LocalCache.getOrCreateAddressableNote(aTag)
} }
fun getMuteListNote(): AddressableNote {
val aTag = ATag(
MuteListEvent.kind,
userProfile().pubkeyHex,
"",
null
)
return LocalCache.getOrCreateAddressableNote(aTag)
}
fun getBlockList(): PeopleListEvent? { fun getBlockList(): PeopleListEvent? {
return getBlockListNote().event as? PeopleListEvent return getBlockListNote().event as? PeopleListEvent
} }
fun hideWord(word: String) { fun getMuteList(): MuteListEvent? {
val blockList = getBlockList() return getMuteListNote().event as? MuteListEvent
}
if (blockList != null) { fun hideWord(word: String) {
PeopleListEvent.addWord( val muteList = getMuteList()
earlierVersion = blockList,
if (muteList != null) {
MuteListEvent.addWord(
earlierVersion = muteList,
word = word, word = word,
isPrivate = true, isPrivate = true,
signer = signer signer = signer
) { ) {
Client.send(it) Client.send(it)
LocalCache.consume(it) LocalCache.consume(it)
live.invalidateData()
saveable.invalidateData()
} }
} else { } else {
PeopleListEvent.createListWithWord( MuteListEvent.createListWithWord(
name = PeopleListEvent.blockList,
word = word, word = word,
isPrivate = true, isPrivate = true,
signer = signer signer = signer
) { ) {
Client.send(it) Client.send(it)
LocalCache.consume(it) LocalCache.consume(it)
live.invalidateData()
saveable.invalidateData()
} }
} }
} }
@@ -1473,46 +1493,50 @@ class Account(
) { ) {
Client.send(it) Client.send(it)
LocalCache.consume(it) LocalCache.consume(it)
}
}
live.invalidateData() val muteList = getMuteList()
saveable.invalidateData()
if (muteList != null) {
MuteListEvent.removeWord(
earlierVersion = muteList,
word = word,
isPrivate = true,
signer = signer
) {
Client.send(it)
LocalCache.consume(it)
} }
} }
} }
suspend fun hideUser(pubkeyHex: String) { fun hideUser(pubkeyHex: String) {
val blockList = getBlockList() val muteList = getMuteList()
if (blockList != null) { if (muteList != null) {
PeopleListEvent.addUser( MuteListEvent.addUser(
earlierVersion = blockList, earlierVersion = muteList,
pubKeyHex = pubkeyHex, pubKeyHex = pubkeyHex,
isPrivate = true, isPrivate = true,
signer = signer signer = signer
) { ) {
Client.send(it) Client.send(it)
LocalCache.consume(it) LocalCache.consume(it)
live.invalidateData()
saveable.invalidateData()
} }
} else { } else {
PeopleListEvent.createListWithUser( MuteListEvent.createListWithUser(
name = PeopleListEvent.blockList,
pubKeyHex = pubkeyHex, pubKeyHex = pubkeyHex,
isPrivate = true, isPrivate = true,
signer = signer signer = signer
) { ) {
Client.send(it) Client.send(it)
LocalCache.consume(it) LocalCache.consume(it)
live.invalidateData()
saveable.invalidateData()
} }
} }
} }
suspend fun showUser(pubkeyHex: String) { fun showUser(pubkeyHex: String) {
val blockList = getBlockList() val blockList = getBlockList()
if (blockList != null) { if (blockList != null) {
@@ -1524,12 +1548,26 @@ class Account(
) { ) {
Client.send(it) Client.send(it)
LocalCache.consume(it) LocalCache.consume(it)
transientHiddenUsers = (transientHiddenUsers - pubkeyHex).toImmutableSet()
live.invalidateData()
saveable.invalidateData()
} }
} }
val muteList = getMuteList()
if (muteList != null) {
MuteListEvent.removeUser(
earlierVersion = muteList,
pubKeyHex = pubkeyHex,
isPrivate = true,
signer = signer
) {
Client.send(it)
LocalCache.consume(it)
}
}
transientHiddenUsers = (transientHiddenUsers - pubkeyHex).toImmutableSet()
live.invalidateData()
saveable.invalidateData()
} }
fun changeDefaultZapType(zapType: LnZapEvent.ZapType) { fun changeDefaultZapType(zapType: LnZapEvent.ZapType) {
@@ -57,6 +57,7 @@ import com.vitorpamplona.quartz.events.LnZapPaymentResponseEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent import com.vitorpamplona.quartz.events.LnZapRequestEvent
import com.vitorpamplona.quartz.events.LongTextNoteEvent import com.vitorpamplona.quartz.events.LongTextNoteEvent
import com.vitorpamplona.quartz.events.MetadataEvent import com.vitorpamplona.quartz.events.MetadataEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.NNSEvent import com.vitorpamplona.quartz.events.NNSEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.events.PinListEvent import com.vitorpamplona.quartz.events.PinListEvent
@@ -425,6 +426,7 @@ object LocalCache {
} }
} }
fun consume(event: MuteListEvent) { consumeBaseReplaceable(event) }
fun consume(event: PeopleListEvent) { consumeBaseReplaceable(event) } fun consume(event: PeopleListEvent) { consumeBaseReplaceable(event) }
private fun consume(event: AdvertisedRelayListEvent) { consumeBaseReplaceable(event) } private fun consume(event: AdvertisedRelayListEvent) { consumeBaseReplaceable(event) }
private fun consume(event: CommunityDefinitionEvent, relay: Relay?) { consumeBaseReplaceable(event) } private fun consume(event: CommunityDefinitionEvent, relay: Relay?) { consumeBaseReplaceable(event) }
@@ -696,7 +698,7 @@ object LocalCache {
val author = getOrCreateUser(event.pubKey) val author = getOrCreateUser(event.pubKey)
val repliesTo = event.originalPost().mapNotNull { checkGetOrCreateNote(it) } + val repliesTo = event.originalPost().mapNotNull { checkGetOrCreateNote(it) } +
event.taggedAddresses().mapNotNull { getOrCreateAddressableNote(it) } event.taggedAddresses().map { getOrCreateAddressableNote(it) }
note.loadEvent(event, author, repliesTo) note.loadEvent(event, author, repliesTo)
@@ -1561,6 +1563,7 @@ object LocalCache {
is LnZapPaymentResponseEvent -> consume(event) is LnZapPaymentResponseEvent -> consume(event)
is LongTextNoteEvent -> consume(event, relay) is LongTextNoteEvent -> consume(event, relay)
is MetadataEvent -> consume(event) is MetadataEvent -> consume(event)
is MuteListEvent -> consume(event)
is NNSEvent -> comsume(event) is NNSEvent -> comsume(event)
is PrivateDmEvent -> consume(event, relay) is PrivateDmEvent -> consume(event, relay)
is PinListEvent -> consume(event) is PinListEvent -> consume(event)
@@ -23,6 +23,7 @@ import com.vitorpamplona.quartz.events.GiftWrapEvent
import com.vitorpamplona.quartz.events.LnZapEvent import com.vitorpamplona.quartz.events.LnZapEvent
import com.vitorpamplona.quartz.events.LnZapPaymentResponseEvent import com.vitorpamplona.quartz.events.LnZapPaymentResponseEvent
import com.vitorpamplona.quartz.events.MetadataEvent import com.vitorpamplona.quartz.events.MetadataEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.events.PollNoteEvent import com.vitorpamplona.quartz.events.PollNoteEvent
import com.vitorpamplona.quartz.events.ReactionEvent import com.vitorpamplona.quartz.events.ReactionEvent
@@ -87,7 +88,7 @@ object NostrAccountDataSource : NostrDataSource("AccountData") {
return TypedFilter( return TypedFilter(
types = COMMON_FEED_TYPES, types = COMMON_FEED_TYPES,
filter = JsonFilter( filter = JsonFilter(
kinds = listOf(BookmarkListEvent.kind, PeopleListEvent.kind), kinds = listOf(BookmarkListEvent.kind, PeopleListEvent.kind, MuteListEvent.kind),
authors = listOf(account.userProfile().pubkeyHex), authors = listOf(account.userProfile().pubkeyHex),
limit = 100 limit = 100
) )
@@ -167,9 +167,10 @@ class MainActivity : AppCompatActivity() {
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
GlobalScope.launch(Dispatchers.Default) { // Graph doesn't completely clear.
serviceManager.trimMemory() // GlobalScope.launch(Dispatchers.Default) {
} // serviceManager.trimMemory()
// }
Log.d("Lifetime Event", "MainActivity.onStop") Log.d("Lifetime Event", "MainActivity.onStop")
} }
@@ -7,6 +7,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.ParticipantListBuilder import com.vitorpamplona.amethyst.model.ParticipantListBuilder
import com.vitorpamplona.quartz.events.ChannelCreateEvent import com.vitorpamplona.quartz.events.ChannelCreateEvent
import com.vitorpamplona.quartz.events.IsInPublicChatChannel import com.vitorpamplona.quartz.events.IsInPublicChatChannel
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -16,7 +17,8 @@ open class DiscoverChatFeedFilter(val account: Account) : AdditiveFeedFilter<Not
} }
override fun showHiddenKey(): Boolean { override fun showHiddenKey(): Boolean {
return account.defaultDiscoveryFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) return account.defaultDiscoveryFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
account.defaultDiscoveryFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
} }
override fun feed(): List<Note> { override fun feed(): List<Note> {
@@ -7,6 +7,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.ParticipantListBuilder import com.vitorpamplona.amethyst.model.ParticipantListBuilder
import com.vitorpamplona.quartz.events.CommunityDefinitionEvent import com.vitorpamplona.quartz.events.CommunityDefinitionEvent
import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent import com.vitorpamplona.quartz.events.CommunityPostApprovalEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -16,7 +17,8 @@ open class DiscoverCommunityFeedFilter(val account: Account) : AdditiveFeedFilte
} }
override fun showHiddenKey(): Boolean { override fun showHiddenKey(): Boolean {
return account.defaultDiscoveryFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) return account.defaultDiscoveryFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
account.defaultDiscoveryFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
} }
override fun feed(): List<Note> { override fun feed(): List<Note> {
@@ -9,6 +9,7 @@ import com.vitorpamplona.quartz.events.LiveActivitiesEvent
import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_ENDED import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_ENDED
import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_LIVE
import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_PLANNED import com.vitorpamplona.quartz.events.LiveActivitiesEvent.Companion.STATUS_PLANNED
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -24,7 +25,8 @@ open class DiscoverLiveFeedFilter(
} }
override fun showHiddenKey(): Boolean { override fun showHiddenKey(): Boolean {
return followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) return followList() == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
followList() == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
} }
override fun feed(): List<Note> { override fun feed(): List<Note> {
@@ -14,9 +14,9 @@ class HiddenAccountsFeedFilter(val account: Account) : FeedFilter<User>() {
} }
override fun feed(): List<User> { override fun feed(): List<User> {
return account.liveHiddenUsers.value?.hiddenUsers?.map { return account.flowHiddenUsers.value.hiddenUsers.map {
LocalCache.getOrCreateUser(it) LocalCache.getOrCreateUser(it)
} ?: emptyList() }
} }
} }
@@ -30,7 +30,7 @@ class HiddenWordsFeedFilter(val account: Account) : FeedFilter<String>() {
} }
override fun feed(): List<String> { override fun feed(): List<String> {
return account.liveHiddenUsers.value?.hiddenWords?.toList() ?: emptyList() return account.flowHiddenUsers.value.hiddenWords.toList()
} }
} }
@@ -6,6 +6,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.quartz.events.ChannelMessageEvent import com.vitorpamplona.quartz.events.ChannelMessageEvent
import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent import com.vitorpamplona.quartz.events.LiveActivitiesChatMessageEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.events.PollNoteEvent import com.vitorpamplona.quartz.events.PollNoteEvent
import com.vitorpamplona.quartz.events.TextNoteEvent import com.vitorpamplona.quartz.events.TextNoteEvent
@@ -18,7 +19,8 @@ class HomeConversationsFeedFilter(val account: Account) : AdditiveFeedFilter<Not
} }
override fun showHiddenKey(): Boolean { override fun showHiddenKey(): Boolean {
return account.defaultHomeFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) return account.defaultHomeFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
account.defaultHomeFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
} }
override fun feed(): List<Note> { override fun feed(): List<Note> {
@@ -10,6 +10,7 @@ import com.vitorpamplona.quartz.events.ClassifiedsEvent
import com.vitorpamplona.quartz.events.GenericRepostEvent import com.vitorpamplona.quartz.events.GenericRepostEvent
import com.vitorpamplona.quartz.events.HighlightEvent import com.vitorpamplona.quartz.events.HighlightEvent
import com.vitorpamplona.quartz.events.LongTextNoteEvent import com.vitorpamplona.quartz.events.LongTextNoteEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.events.PollNoteEvent import com.vitorpamplona.quartz.events.PollNoteEvent
import com.vitorpamplona.quartz.events.RepostEvent import com.vitorpamplona.quartz.events.RepostEvent
@@ -23,7 +24,8 @@ class HomeNewThreadFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
} }
override fun showHiddenKey(): Boolean { override fun showHiddenKey(): Boolean {
return account.defaultHomeFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) return account.defaultHomeFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
account.defaultHomeFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
} }
override fun feed(): List<Note> { override fun feed(): List<Note> {
@@ -14,6 +14,7 @@ import com.vitorpamplona.quartz.events.GenericRepostEvent
import com.vitorpamplona.quartz.events.GiftWrapEvent import com.vitorpamplona.quartz.events.GiftWrapEvent
import com.vitorpamplona.quartz.events.LnZapEvent import com.vitorpamplona.quartz.events.LnZapEvent
import com.vitorpamplona.quartz.events.LnZapRequestEvent import com.vitorpamplona.quartz.events.LnZapRequestEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.events.ReactionEvent import com.vitorpamplona.quartz.events.ReactionEvent
import com.vitorpamplona.quartz.events.RepostEvent import com.vitorpamplona.quartz.events.RepostEvent
@@ -24,7 +25,8 @@ class NotificationFeedFilter(val account: Account) : AdditiveFeedFilter<Note>()
} }
override fun showHiddenKey(): Boolean { override fun showHiddenKey(): Boolean {
return account.defaultNotificationFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) return account.defaultNotificationFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
account.defaultNotificationFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
} }
override fun feed(): List<Note> { override fun feed(): List<Note> {
@@ -6,6 +6,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.quartz.events.FileHeaderEvent import com.vitorpamplona.quartz.events.FileHeaderEvent
import com.vitorpamplona.quartz.events.FileStorageHeaderEvent import com.vitorpamplona.quartz.events.FileStorageHeaderEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import com.vitorpamplona.quartz.utils.TimeUtils import com.vitorpamplona.quartz.utils.TimeUtils
@@ -15,7 +16,8 @@ class VideoFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
} }
override fun showHiddenKey(): Boolean { override fun showHiddenKey(): Boolean {
return account.defaultStoriesFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) return account.defaultStoriesFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
account.defaultStoriesFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
} }
override fun feed(): List<Note> { override fun feed(): List<Note> {
@@ -31,7 +33,8 @@ class VideoFeedFilter(val account: Account) : AdditiveFeedFilter<Note>() {
private fun innerApplyFilter(collection: Collection<Note>): Set<Note> { private fun innerApplyFilter(collection: Collection<Note>): Set<Note> {
val now = TimeUtils.now() val now = TimeUtils.now()
val isGlobal = account.defaultStoriesFollowList.value == GLOBAL_FOLLOWS val isGlobal = account.defaultStoriesFollowList.value == GLOBAL_FOLLOWS
val isHiddenList = account.defaultStoriesFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) val isHiddenList = account.defaultStoriesFollowList.value == PeopleListEvent.blockListFor(account.userProfile().pubkeyHex) ||
account.defaultStoriesFollowList.value == MuteListEvent.blockListFor(account.userProfile().pubkeyHex)
val followingKeySet = account.liveStoriesFollowLists.value?.users ?: emptySet() val followingKeySet = account.liveStoriesFollowLists.value?.users ?: emptySet()
val followingTagSet = account.liveStoriesFollowLists.value?.hashtags ?: emptySet() val followingTagSet = account.liveStoriesFollowLists.value?.hashtags ?: emptySet()
@@ -118,6 +118,7 @@ import com.vitorpamplona.amethyst.ui.theme.Size40dp
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import com.vitorpamplona.quartz.events.ChatroomKey import com.vitorpamplona.quartz.events.ChatroomKey
import com.vitorpamplona.quartz.events.ContactListEvent import com.vitorpamplona.quartz.events.ContactListEvent
import com.vitorpamplona.quartz.events.MuteListEvent
import com.vitorpamplona.quartz.events.PeopleListEvent import com.vitorpamplona.quartz.events.PeopleListEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@@ -589,6 +590,11 @@ data class CodeName(val code: String, val name: Name, val type: CodeNameType)
class FollowListViewModel(val account: Account) : ViewModel() { class FollowListViewModel(val account: Account) : ViewModel() {
val kind3Follow = CodeName(KIND3_FOLLOWS, ResourceName(R.string.follow_list_kind3follows), CodeNameType.HARDCODED) val kind3Follow = CodeName(KIND3_FOLLOWS, ResourceName(R.string.follow_list_kind3follows), CodeNameType.HARDCODED)
val globalFollow = CodeName(GLOBAL_FOLLOWS, ResourceName(R.string.follow_list_global), CodeNameType.HARDCODED) val globalFollow = CodeName(GLOBAL_FOLLOWS, ResourceName(R.string.follow_list_global), CodeNameType.HARDCODED)
val muteListFollow = CodeName(
MuteListEvent.blockListFor(account.userProfile().pubkeyHex),
ResourceName(R.string.follow_list_mute_list),
CodeNameType.HARDCODED
)
private var _kind3GlobalPeopleRoutes = MutableStateFlow<ImmutableList<CodeName>>(emptyList<CodeName>().toPersistentList()) private var _kind3GlobalPeopleRoutes = MutableStateFlow<ImmutableList<CodeName>>(emptyList<CodeName>().toPersistentList())
val kind3GlobalPeopleRoutes = _kind3GlobalPeopleRoutes.asStateFlow() val kind3GlobalPeopleRoutes = _kind3GlobalPeopleRoutes.asStateFlow()
@@ -634,13 +640,13 @@ class FollowListViewModel(val account: Account) : ViewModel() {
val routeList = (communities + hashtags + geotags).sortedBy { it.name.name() } val routeList = (communities + hashtags + geotags).sortedBy { it.name.name() }
val kind3GlobalPeopleRouteList = listOf(listOf(kind3Follow, globalFollow), newFollowLists, routeList).flatten().toImmutableList() val kind3GlobalPeopleRouteList = listOf(listOf(kind3Follow, globalFollow), newFollowLists, routeList, listOf(muteListFollow)).flatten().toImmutableList()
if (!equalImmutableLists(_kind3GlobalPeopleRoutes.value, kind3GlobalPeopleRouteList)) { if (!equalImmutableLists(_kind3GlobalPeopleRoutes.value, kind3GlobalPeopleRouteList)) {
_kind3GlobalPeopleRoutes.emit(kind3GlobalPeopleRouteList) _kind3GlobalPeopleRoutes.emit(kind3GlobalPeopleRouteList)
} }
val kind3GlobalPeopleList = listOf(listOf(kind3Follow, globalFollow), newFollowLists).flatten().toImmutableList() val kind3GlobalPeopleList = listOf(listOf(kind3Follow, globalFollow), newFollowLists, listOf(muteListFollow)).flatten().toImmutableList()
if (!equalImmutableLists(_kind3GlobalPeople.value, kind3GlobalPeopleList)) { if (!equalImmutableLists(_kind3GlobalPeople.value, kind3GlobalPeopleList)) {
_kind3GlobalPeople.emit(kind3GlobalPeopleList) _kind3GlobalPeople.emit(kind3GlobalPeopleList)
@@ -656,7 +662,7 @@ class FollowListViewModel(val account: Account) : ViewModel() {
LocalCache.live.newEventBundles.collect { newNotes -> LocalCache.live.newEventBundles.collect { newNotes ->
checkNotInMainThread() checkNotInMainThread()
if (newNotes.any { if (newNotes.any {
it.event?.pubKey() == account.userProfile().pubkeyHex && (it.event is PeopleListEvent || it.event is ContactListEvent) it.event?.pubKey() == account.userProfile().pubkeyHex && (it.event is PeopleListEvent || it.event is MuteListEvent || it.event is ContactListEvent)
} }
) { ) {
refresh() refresh()
@@ -153,8 +153,7 @@ class AccountViewModel(val account: Account, val settings: SettingsState) : View
} }
fun isNoteHidden(note: Note): Boolean { fun isNoteHidden(note: Note): Boolean {
val isSensitive = note.event?.isSensitive() ?: false return note.isHiddenFor(account.flowHiddenUsers.value)
return account.isHidden(note.author!!) || (isSensitive && account.showSensitiveContent == false)
} }
fun hasReactedTo(baseNote: Note, reaction: String): Boolean { fun hasReactedTo(baseNote: Note, reaction: String): Boolean {
@@ -42,6 +42,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.distinctUntilChanged
import androidx.lifecycle.map import androidx.lifecycle.map
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@@ -214,7 +215,9 @@ private fun AddMuteWordTextField(accountViewModel: AccountViewModel) {
value = currentWordToAdd.value, value = currentWordToAdd.value,
onValueChange = { currentWordToAdd.value = it }, onValueChange = { currentWordToAdd.value = it },
label = { Text(text = stringResource(R.string.hide_new_word_label)) }, label = { Text(text = stringResource(R.string.hide_new_word_label)) },
modifier = Modifier.fillMaxWidth().padding(10.dp), modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
placeholder = { placeholder = {
Text( Text(
text = stringResource(R.string.hide_new_word_label), text = stringResource(R.string.hide_new_word_label),
@@ -260,7 +263,7 @@ fun WatchAccountAndBlockList(
accountViewModel: AccountViewModel accountViewModel: AccountViewModel
) { ) {
val accountState by accountViewModel.accountLiveData.observeAsState() val accountState by accountViewModel.accountLiveData.observeAsState()
val blockListState by accountViewModel.account.getBlockListNote().live().metadata.observeAsState() val blockListState by accountViewModel.account.flowHiddenUsers.collectAsStateWithLifecycle()
LaunchedEffect(accountViewModel, accountState, blockListState) { LaunchedEffect(accountViewModel, accountState, blockListState) {
feedViewModel.invalidateData() feedViewModel.invalidateData()
@@ -142,7 +142,7 @@ fun WatchAccountForNotifications(
notifFeedViewModel: NotificationViewModel, notifFeedViewModel: NotificationViewModel,
accountViewModel: AccountViewModel accountViewModel: AccountViewModel
) { ) {
val listState by accountViewModel.account.liveStoriesFollowLists.collectAsStateWithLifecycle() val listState by accountViewModel.account.liveNotificationFollowLists.collectAsStateWithLifecycle()
LaunchedEffect(accountViewModel, listState) { LaunchedEffect(accountViewModel, listState) {
NostrAccountDataSource.account = accountViewModel.account NostrAccountDataSource.account = accountViewModel.account
+1
View File
@@ -386,6 +386,7 @@
<string name="follow_list_selection">Follow List</string> <string name="follow_list_selection">Follow List</string>
<string name="follow_list_kind3follows">All Follows</string> <string name="follow_list_kind3follows">All Follows</string>
<string name="follow_list_global">Global</string> <string name="follow_list_global">Global</string>
<string name="follow_list_mute_list">Mute List</string>
<string name="connect_through_your_orbot_setup_markdown"> <string name="connect_through_your_orbot_setup_markdown">
## Connect through Tor with Orbot ## Connect through Tor with Orbot
\n\n1. Install [Orbot](https://play.google.com/store/apps/details?id=org.torproject.android) \n\n1. Install [Orbot](https://play.google.com/store/apps/details?id=org.torproject.android)
@@ -18,8 +18,11 @@ class EmojiPackSelectionEvent(
content: String, content: String,
sig: HexKey sig: HexKey
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) { ) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) {
override fun dTag() = fixedDTag
companion object { companion object {
const val kind = 10030 const val kind = 10030
const val fixedDTag = ""
fun create( fun create(
listOfEmojiPacks: List<ATag>?, listOfEmojiPacks: List<ATag>?,
@@ -66,6 +66,7 @@ class EventFactory {
LnZapRequestEvent.kind -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig) LnZapRequestEvent.kind -> LnZapRequestEvent(id, pubKey, createdAt, tags, content, sig)
LongTextNoteEvent.kind -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig) LongTextNoteEvent.kind -> LongTextNoteEvent(id, pubKey, createdAt, tags, content, sig)
MetadataEvent.kind -> MetadataEvent(id, pubKey, createdAt, tags, content, sig) MetadataEvent.kind -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
MuteListEvent.kind -> MuteListEvent(id, pubKey, createdAt, tags, content, sig)
NNSEvent.kind -> NNSEvent(id, pubKey, createdAt, tags, content, sig) NNSEvent.kind -> NNSEvent(id, pubKey, createdAt, tags, content, sig)
PeopleListEvent.kind -> PeopleListEvent(id, pubKey, createdAt, tags, content, sig) PeopleListEvent.kind -> PeopleListEvent(id, pubKey, createdAt, tags, content, sig)
PinListEvent.kind -> PinListEvent(id, pubKey, createdAt, tags, content, sig) PinListEvent.kind -> PinListEvent(id, pubKey, createdAt, tags, content, sig)
@@ -8,6 +8,8 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import kotlinx.collections.immutable.ImmutableSet
import kotlinx.collections.immutable.toImmutableSet
@Immutable @Immutable
abstract class GeneralListEvent( abstract class GeneralListEvent(
@@ -34,6 +36,27 @@ abstract class GeneralListEvent(
return privateTagsCache return privateTagsCache
} }
fun filterTagList(key: String, privateTags: List<List<String>>?): ImmutableSet<String> {
val privateUserList = privateTags?.let {
it.filter { it.size > 1 && it[0] == key }.map { it[1] }.toSet()
} ?: emptySet()
val publicUserList = tags.filter { it.size > 1 && it[0] == key }.map { it[1] }.toSet()
return (privateUserList + publicUserList).toImmutableSet()
}
fun isTagged(key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) {
return if (isPrivate) {
privateTagsOrEmpty(signer = signer) {
onReady(
it.any { it.size > 1 && it[0] == key && it[1] == tag }
)
}
} else {
onReady(isTagged(key, tag))
}
}
fun privateTags(signer: NostrSigner, onReady: (List<List<String>>) -> Unit) { fun privateTags(signer: NostrSigner, onReady: (List<List<String>>) -> Unit) {
if (content.isBlank()) return if (content.isBlank()) return
@@ -10,6 +10,7 @@ import com.vitorpamplona.quartz.crypto.CryptoUtils
import com.vitorpamplona.quartz.encoders.ATag import com.vitorpamplona.quartz.encoders.ATag
import com.vitorpamplona.quartz.encoders.HexKey import com.vitorpamplona.quartz.encoders.HexKey
import com.vitorpamplona.quartz.signers.NostrSigner import com.vitorpamplona.quartz.signers.NostrSigner
import kotlinx.collections.immutable.ImmutableSet
import java.util.UUID import java.util.UUID
@Immutable @Immutable
@@ -20,96 +21,195 @@ class MuteListEvent(
tags: List<List<String>>, tags: List<List<String>>,
content: String, content: String,
sig: HexKey sig: HexKey
) : BaseAddressableEvent(id, pubKey, createdAt, kind, tags, content, sig) { ) : GeneralListEvent(id, pubKey, createdAt, kind, tags, content, sig) {
@Transient @Transient
private var privateTagsCache: List<List<String>>? = null var publicAndPrivateUserCache: ImmutableSet<HexKey>? = null
@Transient
var publicAndPrivateWordCache: ImmutableSet<String>? = null
private fun privateTags(signer: NostrSigner, onReady: (List<List<String>>) -> Unit) { override fun dTag() = fixedDTag
if (content.isBlank()) return
privateTagsCache?.let { fun publicAndPrivateUsersAndWords(signer: NostrSigner, onReady: (PeopleListEvent.UsersAndWords) -> Unit) {
onReady(it) publicAndPrivateUserCache?.let { userList ->
return publicAndPrivateWordCache?.let { wordList ->
onReady(PeopleListEvent.UsersAndWords(userList, wordList))
return
}
} }
try { privateTagsOrEmpty(signer) {
signer.nip04Decrypt(content, pubKey) { publicAndPrivateUserCache = filterTagList("p", it)
privateTagsCache = mapper.readValue<List<List<String>>>(it) publicAndPrivateWordCache = filterTagList("word", it)
privateTagsCache?.let {
onReady(it) publicAndPrivateUserCache?.let { userList ->
publicAndPrivateWordCache?.let { wordList ->
onReady(
PeopleListEvent.UsersAndWords(userList, wordList)
)
} }
} }
} catch (e: Throwable) {
Log.w("MuteList", "Error parsing the JSON ${e.message}")
} }
} }
fun privateTaggedUsers(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "p" }.map { it[1] } )
}
fun privateHashtags(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "t" }.map { it[1] } )
}
fun privateGeohashes(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "g" }.map { it[1] } )
}
fun privateTaggedEvents(signer: NostrSigner, onReady: (List<String>) -> Unit) = privateTags(signer) {
onReady(it.filter { it.size > 1 && it[0] == "e" }.map { it[1] } )
}
fun privateTaggedAddresses(signer: NostrSigner, onReady: (List<ATag>) -> Unit) = privateTags(signer) {
onReady(
it.filter { it.firstOrNull() == "a" }.mapNotNull {
val aTagValue = it.getOrNull(1)
val relay = it.getOrNull(2)
if (aTagValue != null) ATag.parse(aTagValue, relay) else null
}
)
}
companion object { companion object {
const val kind = 10000 const val kind = 10000
const val fixedDTag = ""
fun blockListFor(pubKeyHex: HexKey): String {
return "10000:$pubKeyHex:"
}
fun createListWithTag(key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
if (isPrivate) {
encryptTags(listOf(listOf(key, tag)), signer) { encryptedTags ->
create(
content = encryptedTags,
tags = emptyList(),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
} else {
create(
content = "",
tags = listOf(listOf(key, tag)),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
fun createListWithUser(pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return createListWithTag("p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun createListWithWord(word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return createListWithTag("word", word, isPrivate, signer, createdAt, onReady)
}
fun addUsers(earlierVersion: MuteListEvent, listPubKeyHex: List<String>, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.plus(
listPubKeyHex.map {
listOf("p", it)
}
),
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags,
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(
listPubKeyHex.map {
listOf("p", it)
}
),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
fun addWord(earlierVersion: MuteListEvent, word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return addTag(earlierVersion, "word", word, isPrivate, signer, createdAt, onReady)
}
fun addUser(earlierVersion: MuteListEvent, pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return addTag(earlierVersion, "p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun addTag(earlierVersion: MuteListEvent, key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
earlierVersion.isTagged(key, tag, isPrivate, signer) { isTagged ->
if (!isTagged) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.plus(element = listOf(key, tag)),
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags,
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.plus(element = listOf(key, tag)),
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
}
}
fun removeWord(earlierVersion: MuteListEvent, word: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return removeTag(earlierVersion, "word", word, isPrivate, signer, createdAt, onReady)
}
fun removeUser(earlierVersion: MuteListEvent, pubKeyHex: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
return removeTag(earlierVersion, "p", pubKeyHex, isPrivate, signer, createdAt, onReady)
}
fun removeTag(earlierVersion: MuteListEvent, key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, createdAt: Long = TimeUtils.now(), onReady: (MuteListEvent) -> Unit) {
earlierVersion.isTagged(key, tag, isPrivate, signer) { isTagged ->
if (isTagged) {
if (isPrivate) {
earlierVersion.privateTagsOrEmpty(signer) { privateTags ->
encryptTags(
privateTags = privateTags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer
) { encryptedTags ->
create(
content = encryptedTags,
tags = earlierVersion.tags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
} else {
create(
content = earlierVersion.content,
tags = earlierVersion.tags.filter { it.size > 1 && !(it[0] == key && it[1] == tag) },
signer = signer,
createdAt = createdAt,
onReady = onReady
)
}
}
}
}
fun create( fun create(
events: List<String>? = null, content: String,
users: List<String>? = null, tags: List<List<String>>,
addresses: List<ATag>? = null,
privEvents: List<String>? = null,
privUsers: List<String>? = null,
privAddresses: List<ATag>? = null,
signer: NostrSigner, signer: NostrSigner,
createdAt: Long = TimeUtils.now(), createdAt: Long = TimeUtils.now(),
onReady: (MuteListEvent) -> Unit onReady: (MuteListEvent) -> Unit
) { ) {
val privTags = mutableListOf<List<String>>() signer.sign(createdAt, kind, tags, content, onReady)
privEvents?.forEach {
privTags.add(listOf("e", it))
}
privUsers?.forEach {
privTags.add(listOf("p", it))
}
privAddresses?.forEach {
privTags.add(listOf("a", it.toTag()))
}
val msg = mapper.writeValueAsString(privTags)
val tags = mutableListOf<List<String>>()
events?.forEach {
tags.add(listOf("e", it))
}
users?.forEach {
tags.add(listOf("p", it))
}
addresses?.forEach {
tags.add(listOf("a", it.toTag()))
}
signer.nip04Encrypt(msg, signer.pubKey) { content ->
signer.sign(createdAt, kind, tags, content, onReady)
}
} }
} }
} }
@@ -24,15 +24,6 @@ class PeopleListEvent(
@Transient @Transient
var publicAndPrivateWordCache: ImmutableSet<String>? = null var publicAndPrivateWordCache: ImmutableSet<String>? = null
fun filterTagList(key: String, privateTags: List<List<String>>?): ImmutableSet<String> {
val privateUserList = privateTags?.let {
it.filter { it.size > 1 && it[0] == key }.map { it[1] }.toSet()
} ?: emptySet()
val publicUserList = tags.filter { it.size > 1 && it[0] == key }.map { it[1] }.toSet()
return (privateUserList + publicUserList).toImmutableSet()
}
fun publicAndPrivateWords(signer: NostrSigner, onReady: (ImmutableSet<String>) -> Unit) { fun publicAndPrivateWords(signer: NostrSigner, onReady: (ImmutableSet<String>) -> Unit) {
publicAndPrivateWordCache?.let { publicAndPrivateWordCache?.let {
onReady(it) onReady(it)
@@ -62,7 +53,10 @@ class PeopleListEvent(
} }
@Immutable @Immutable
data class UsersAndWords(val users: ImmutableSet<String>, val words: ImmutableSet<String>) data class UsersAndWords(
val users: ImmutableSet<String> = persistentSetOf(),
val words: ImmutableSet<String> = persistentSetOf()
)
fun publicAndPrivateUsersAndWords(signer: NostrSigner, onReady: (UsersAndWords) -> Unit) { fun publicAndPrivateUsersAndWords(signer: NostrSigner, onReady: (UsersAndWords) -> Unit) {
publicAndPrivateUserCache?.let { userList -> publicAndPrivateUserCache?.let { userList ->
@@ -86,18 +80,6 @@ class PeopleListEvent(
} }
} }
fun isTagged(key: String, tag: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) {
return if (isPrivate) {
privateTagsOrEmpty(signer = signer) {
onReady(
it.any { it.size > 1 && it[0] == key && it[1] == tag }
)
}
} else {
onReady(isTagged(key, tag))
}
}
fun isTaggedWord(word: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) = isTagged( "word", word, isPrivate, signer, onReady) fun isTaggedWord(word: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) = isTagged( "word", word, isPrivate, signer, onReady)
fun isTaggedUser(idHex: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) = isTagged( "p", idHex, isPrivate, signer, onReady) fun isTaggedUser(idHex: String, isPrivate: Boolean, signer: NostrSigner, onReady: (Boolean) -> Unit) = isTagged( "p", idHex, isPrivate, signer, onReady)