Migrates to MuteList 10000
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
+5
-2
@@ -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()
|
||||||
|
|||||||
+1
-1
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user