diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..4d86ddb47
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt
index b426e658f..1fe879bea 100644
--- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt
+++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt
@@ -154,7 +154,7 @@ class Account(
if (!isWriteable()) return null
note.event?.let {
- return LnZapRequestEvent.create(it, userProfile().relays?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!)
+ return LnZapRequestEvent.create(it, userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!)
}
return null
@@ -167,7 +167,7 @@ class Account(
fun createZapRequestFor(userPubKeyHex: String): LnZapRequestEvent? {
if (!isWriteable()) return null
- return LnZapRequestEvent.create(userPubKeyHex, userProfile().relays?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!)
+ return LnZapRequestEvent.create(userPubKeyHex, userProfile().latestContactList?.relays()?.keys?.ifEmpty { null } ?: localRelays.map { it.url }.toSet(), loggedIn.privKey!!)
}
fun report(note: Note, type: ReportEvent.ReportType) {
@@ -250,7 +250,7 @@ class Account(
val event = if (contactList != null && follows.isNotEmpty()) {
ContactListEvent.create(
follows.plus(Contact(user.pubkeyHex, null)),
- userProfile().relays,
+ contactList.relays(),
loggedIn.privKey!!
)
} else {
@@ -275,7 +275,7 @@ class Account(
if (contactList != null && follows.isNotEmpty()) {
val event = ContactListEvent.create(
follows.filter { it.pubKeyHex != user.pubkeyHex },
- userProfile().relays,
+ contactList.relays(),
loggedIn.privKey!!
)
@@ -454,7 +454,7 @@ class Account(
}
private fun updateContactListTo(newContactList: ContactListEvent?) {
- if (newContactList?.follows().isNullOrEmpty()) return
+ if (newContactList?.unverifiedFollowKeySet().isNullOrEmpty()) return
// Events might be different objects, we have to compare their ids.
if (backupContactList?.id != newContactList?.id) {
@@ -465,7 +465,7 @@ class Account(
// Takes a User's relay list and adds the types of feeds they are active for.
fun activeRelays(): Array? {
- var usersRelayList = userProfile().relays?.map {
+ var usersRelayList = userProfile().latestContactList?.relays()?.map {
val localFeedTypes = localRelays.firstOrNull() { localRelay -> localRelay.url == it.key }?.feedTypes ?: FeedType.values().toSet()
Relay(it.key, it.value.read, it.value.write, localFeedTypes)
} ?: return null
@@ -501,15 +501,23 @@ class Account(
fun isHidden(user: User) = user.pubkeyHex in hiddenUsers || user.pubkeyHex in transientHiddenUsers
+ fun followingKeySet(): Set {
+ return userProfile().latestContactList?.verifiedFollowKeySet ?: emptySet()
+ }
+
fun isAcceptable(user: User): Boolean {
return !isHidden(user) && // if user hasn't hided this author
user.reportsBy(userProfile()).isEmpty() && // if user has not reported this post
- user.countReportAuthorsBy(userProfile().follows) < 5
+ user.countReportAuthorsBy(followingKeySet()) < 5
}
fun isAcceptableDirect(note: Note): Boolean {
return note.reportsBy(userProfile()).isEmpty() && // if user has not reported this post
- note.countReportAuthorsBy(userProfile().follows) < 5 // if it has 5 reports by reliable users
+ note.countReportAuthorsBy(followingKeySet()) < 5 // if it has 5 reports by reliable users
+ }
+
+ fun isFollowing(user: User): Boolean {
+ return user.pubkeyHex in followingKeySet()
}
fun isAcceptable(note: Note): Boolean {
@@ -522,7 +530,7 @@ class Account(
}
fun getRelevantReports(note: Note): Set {
- val followsPlusMe = userProfile().follows + userProfile()
+ val followsPlusMe = userProfile().latestContactList?.verifiedFollowKeySetAndMe ?: emptySet()
val innerReports = if (note.event is RepostEvent) {
note.replyTo?.map { getRelevantReports(it) }?.flatten() ?: emptyList()
@@ -532,8 +540,9 @@ class Account(
return (
note.reportsBy(followsPlusMe) +
- (note.author?.reportsBy(followsPlusMe) ?: emptyList()) +
- innerReports
+ (
+ note.author?.reportsBy(followsPlusMe) ?: emptyList()
+ ) + innerReports
).toSet()
}
@@ -575,7 +584,7 @@ class Account(
it.cache.spamMessages.snapshot().values.forEach {
if (it.pubkeyHex !in transientHiddenUsers && it.duplicatedMessages.size >= 5) {
val userToBlock = LocalCache.getOrCreateUser(it.pubkeyHex)
- if (userToBlock != userProfile() && userToBlock !in userProfile().follows) {
+ if (userToBlock != userProfile() && userToBlock.pubkeyHex !in followingKeySet()) {
transientHiddenUsers = transientHiddenUsers + it.pubkeyHex
}
}
diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
index 431a70470..91e8f9ffd 100644
--- a/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
+++ b/app/src/main/java/com/vitorpamplona/amethyst/model/LocalCache.kt
@@ -4,8 +4,27 @@ import android.util.Log
import androidx.lifecycle.LiveData
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
-import com.google.gson.reflect.TypeToken
-import com.vitorpamplona.amethyst.service.model.*
+import com.vitorpamplona.amethyst.service.model.ATag
+import com.vitorpamplona.amethyst.service.model.BadgeAwardEvent
+import com.vitorpamplona.amethyst.service.model.BadgeDefinitionEvent
+import com.vitorpamplona.amethyst.service.model.BadgeProfilesEvent
+import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
+import com.vitorpamplona.amethyst.service.model.ChannelHideMessageEvent
+import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
+import com.vitorpamplona.amethyst.service.model.ChannelMetadataEvent
+import com.vitorpamplona.amethyst.service.model.ChannelMuteUserEvent
+import com.vitorpamplona.amethyst.service.model.ContactListEvent
+import com.vitorpamplona.amethyst.service.model.DeletionEvent
+import com.vitorpamplona.amethyst.service.model.LnZapEvent
+import com.vitorpamplona.amethyst.service.model.LnZapRequestEvent
+import com.vitorpamplona.amethyst.service.model.LongTextNoteEvent
+import com.vitorpamplona.amethyst.service.model.MetadataEvent
+import com.vitorpamplona.amethyst.service.model.PrivateDmEvent
+import com.vitorpamplona.amethyst.service.model.ReactionEvent
+import com.vitorpamplona.amethyst.service.model.RecommendRelayEvent
+import com.vitorpamplona.amethyst.service.model.ReportEvent
+import com.vitorpamplona.amethyst.service.model.RepostEvent
+import com.vitorpamplona.amethyst.service.model.TextNoteEvent
import com.vitorpamplona.amethyst.service.relays.Relay
import fr.acinq.secp256k1.Hex
import kotlinx.coroutines.*
@@ -162,24 +181,15 @@ object LocalCache {
return
}
- val mentions = event.mentions().mapNotNull { checkGetOrCreateUser(it) }
- val replyTo = tagsWithoutCitations(event).mapNotNull { checkGetOrCreateNote(it) }
+ val replyTo = event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
- note.loadEvent(event, author, mentions, replyTo)
+ note.loadEvent(event, author, replyTo)
// Log.d("TN", "New Note (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${note.event?.content()?.take(100)} ${formattedDateTime(event.createdAt)}")
// Prepares user's profile view.
author.addNote(note)
- // Adds notifications to users.
- mentions.forEach {
- it.addTaggedPost(note)
- }
- replyTo.forEach {
- it.author?.addTaggedPost(note)
- }
-
// Counts the replies
replyTo.forEach {
it.addReply(note)
@@ -207,22 +217,13 @@ object LocalCache {
return
}
- val mentions = event.mentions().mapNotNull { checkGetOrCreateUser(it) }
- val replyTo = tagsWithoutCitations(event).mapNotNull { checkGetOrCreateNote(it) }
+ val replyTo = event.tagsWithoutCitations().mapNotNull { checkGetOrCreateNote(it) }
if (event.createdAt > (note.createdAt() ?: 0)) {
- note.loadEvent(event, author, mentions, replyTo)
+ note.loadEvent(event, author, replyTo)
author.addNote(note)
- // Adds notifications to users.
- mentions.forEach {
- it.addTaggedPost(note)
- }
- replyTo.forEach {
- it.author?.addTaggedPost(note)
- }
-
refreshObservers()
}
}
@@ -235,7 +236,7 @@ object LocalCache {
if (note.event?.id() == event.id()) return
if (event.createdAt > (note.createdAt() ?: 0)) {
- note.loadEvent(event, author, emptyList(), emptyList())
+ note.loadEvent(event, author, emptyList())
refreshObservers()
}
@@ -252,7 +253,7 @@ object LocalCache {
event.badgeAwardDefinitions().mapNotNull { getOrCreateAddressableNote(it) }
if (event.createdAt > (note.createdAt() ?: 0)) {
- note.loadEvent(event, author, emptyList(), replyTo)
+ note.loadEvent(event, author, replyTo)
author.updateAcceptedBadges(note)
@@ -272,12 +273,7 @@ object LocalCache {
val awardees = event.awardees().mapNotNull { checkGetOrCreateUser(it) }
val awardDefinition = event.awardDefinition().map { getOrCreateAddressableNote(it) }
- note.loadEvent(event, author, awardees, awardDefinition)
-
- // Adds notifications to users.
- awardees.forEach {
- it.addTaggedPost(note)
- }
+ note.loadEvent(event, author, awardDefinition)
// Replies of an Badge Definition are Award Events
awardDefinition.forEach {
@@ -287,53 +283,6 @@ object LocalCache {
refreshObservers()
}
- private fun findCitations(event: Event): Set {
- var citations = mutableSetOf()
- // Removes citations from replies:
- val matcher = tagSearch.matcher(event.content)
- while (matcher.find()) {
- try {
- val tag = matcher.group(1)?.let { event.tags[it.toInt()] }
- if (tag != null && tag[0] == "e") {
- citations.add(tag[1])
- }
- if (tag != null && tag[0] == "a") {
- citations.add(tag[1])
- }
- } catch (e: Exception) {
- }
- }
- return citations
- }
-
- private fun tagsWithoutCitations(event: TextNoteEvent): List {
- val repliesTo = event.replyTos()
- val tagAddresses = event.taggedAddresses().map { it.toTag() }
- if (repliesTo.isEmpty() && tagAddresses.isEmpty()) return emptyList()
-
- val citations = findCitations(event)
-
- return if (citations.isEmpty()) {
- repliesTo + tagAddresses
- } else {
- repliesTo.filter { it !in citations }
- }
- }
-
- private fun tagsWithoutCitations(event: LongTextNoteEvent): List {
- val repliesTo = event.replyTos()
- val tagAddresses = event.taggedAddresses().map { it.toTag() }
- if (repliesTo.isEmpty() && tagAddresses.isEmpty()) return emptyList()
-
- val citations = findCitations(event)
-
- return if (citations.isEmpty()) {
- repliesTo + tagAddresses
- } else {
- repliesTo.filter { it !in citations }
- }
- }
-
@Suppress("UNUSED_PARAMETER")
fun consume(event: RecommendRelayEvent) {
// // Log.d("RR", event.toJson())
@@ -341,41 +290,11 @@ object LocalCache {
fun consume(event: ContactListEvent) {
val user = getOrCreateUser(event.pubKey)
- val follows = event.follows()
+ val follows = event.unverifiedFollowKeySet()
- if (event.createdAt > user.updatedFollowsAt && !follows.isNullOrEmpty()) {
+ if (event.createdAt > (user.latestContactList?.createdAt ?: 0) && !follows.isNullOrEmpty()) {
// Saves relay list only if it's a user that is currently been seen
- user.latestContactList = event
-
- user.updateFollows(
- follows.map {
- try {
- val pubKey = decodePublicKey(it.pubKeyHex)
- getOrCreateUser(pubKey.toHexKey())
- } catch (e: Exception) {
- Log.w("ContactList Parser", "Ignoring: Could not parse Hex key: ${it.pubKeyHex} in ${event.toJson()}")
- // e.printStackTrace()
- null
- }
- }.filterNotNull().toSet(),
- event.createdAt
- )
-
- // Saves relay list only if it's a user that is currently been seen
- try {
- if (event.content.isNotEmpty()) {
- val relays: Map =
- Event.gson.fromJson(
- event.content,
- object : TypeToken