Adds helper classes to avoid recreating master lists.
This commit is contained in:
@@ -1,29 +1,20 @@
|
|||||||
package com.vitorpamplona.amethyst.model
|
package com.vitorpamplona.amethyst.model
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import com.vitorpamplona.amethyst.lnurl.LnInvoiceUtil
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
|
|
||||||
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
import com.vitorpamplona.amethyst.service.NostrSingleUserDataSource
|
||||||
import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
import com.vitorpamplona.amethyst.service.model.LnZapEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.ReportEvent
|
import com.vitorpamplona.amethyst.service.model.ReportEvent
|
||||||
import com.vitorpamplona.amethyst.service.relays.Client
|
|
||||||
import com.vitorpamplona.amethyst.service.relays.FeedType
|
|
||||||
import com.vitorpamplona.amethyst.service.relays.Relay
|
import com.vitorpamplona.amethyst.service.relays.Relay
|
||||||
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
import com.vitorpamplona.amethyst.ui.note.toShortenHex
|
||||||
import fr.acinq.secp256k1.Hex
|
import fr.acinq.secp256k1.Hex
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.util.Collections
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import kotlin.time.ExperimentalTime
|
|
||||||
import kotlin.time.measureTimedValue
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import nostr.postr.events.ContactListEvent
|
import nostr.postr.events.ContactListEvent
|
||||||
import nostr.postr.events.Event
|
|
||||||
import nostr.postr.events.MetadataEvent
|
import nostr.postr.events.MetadataEvent
|
||||||
import nostr.postr.toNpub
|
import nostr.postr.toNpub
|
||||||
|
|
||||||
@@ -61,10 +52,11 @@ class User(val pubkeyHex: String) {
|
|||||||
var relaysBeingUsed = mapOf<String, RelayInfo>()
|
var relaysBeingUsed = mapOf<String, RelayInfo>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var messages = mapOf<User, Set<Note>>()
|
data class Chatroom(var roomMessages: Set<Note>)
|
||||||
|
|
||||||
|
var privateChatrooms = mapOf<User, Chatroom>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var latestMetadataRequestEOSE: Long? = null
|
|
||||||
var latestReportRequestEOSE: Long? = null
|
var latestReportRequestEOSE: Long? = null
|
||||||
|
|
||||||
fun toBestDisplayName(): String {
|
fun toBestDisplayName(): String {
|
||||||
@@ -103,8 +95,10 @@ class User(val pubkeyHex: String) {
|
|||||||
fun follow(users: Set<User>, followedAt: Long) {
|
fun follow(users: Set<User>, followedAt: Long) {
|
||||||
follows = follows + users
|
follows = follows + users
|
||||||
users.forEach {
|
users.forEach {
|
||||||
it.followers = it.followers + this
|
if (this !in followers) {
|
||||||
it.liveFollows.invalidateData()
|
it.followers = it.followers + this
|
||||||
|
it.liveFollows.invalidateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
liveFollows.invalidateData()
|
liveFollows.invalidateData()
|
||||||
@@ -113,8 +107,10 @@ class User(val pubkeyHex: String) {
|
|||||||
fun unfollow(users: Set<User>) {
|
fun unfollow(users: Set<User>) {
|
||||||
follows = follows - users
|
follows = follows - users
|
||||||
users.forEach {
|
users.forEach {
|
||||||
it.followers = it.followers - this
|
if (this in followers) {
|
||||||
it.liveFollows.invalidateData()
|
it.followers = it.followers - this
|
||||||
|
it.liveFollows.invalidateData()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
liveFollows.invalidateData()
|
liveFollows.invalidateData()
|
||||||
}
|
}
|
||||||
@@ -178,18 +174,18 @@ class User(val pubkeyHex: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getOrCreateChannel(user: User): Set<Note> {
|
fun getOrCreatePrivateChatroom(user: User): Chatroom {
|
||||||
return messages[user] ?: run {
|
return privateChatrooms[user] ?: run {
|
||||||
val channel = setOf<Note>()
|
val privateChatroom = Chatroom(setOf<Note>())
|
||||||
messages = messages + Pair(user, channel)
|
privateChatrooms = privateChatrooms + Pair(user, privateChatroom)
|
||||||
channel
|
privateChatroom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addMessage(user: User, msg: Note) {
|
fun addMessage(user: User, msg: Note) {
|
||||||
val channel = getOrCreateChannel(user)
|
val privateChatroom = getOrCreatePrivateChatroom(user)
|
||||||
if (msg !in channel) {
|
if (msg !in privateChatroom.roomMessages) {
|
||||||
messages = messages + Pair(user, channel + msg)
|
privateChatroom.roomMessages = privateChatroom.roomMessages + msg
|
||||||
liveMessages.invalidateData()
|
liveMessages.invalidateData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -243,9 +239,9 @@ class User(val pubkeyHex: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun hasSentMessagesTo(user: User?): Boolean {
|
fun hasSentMessagesTo(user: User?): Boolean {
|
||||||
val messagesToUser = messages[user] ?: return false
|
val messagesToUser = privateChatrooms[user] ?: return false
|
||||||
|
|
||||||
return messagesToUser.firstOrNull { this == it.author } != null
|
return messagesToUser.roomMessages.any { this == it.author }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hasReport(loggedIn: User, type: ReportEvent.ReportType): Boolean {
|
fun hasReport(loggedIn: User, type: ReportEvent.ReportType): Boolean {
|
||||||
|
|||||||
@@ -56,9 +56,9 @@ object NostrChatRoomDataSource: NostrDataSource<Note>("ChatroomFeed") {
|
|||||||
|
|
||||||
// returns the last Note of each user.
|
// returns the last Note of each user.
|
||||||
override fun feed(): List<Note> {
|
override fun feed(): List<Note> {
|
||||||
val messages = account.userProfile().messages[withUser] ?: return emptyList()
|
val messages = account.userProfile().privateChatrooms[withUser] ?: return emptyList()
|
||||||
|
|
||||||
return messages.filter { account.isAcceptable(it) }.sortedBy { it.event?.createdAt }.reversed()
|
return messages.roomMessages.filter { account.isAcceptable(it) }.sortedBy { it.event?.createdAt }.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateChannelFilters() {
|
override fun updateChannelFilters() {
|
||||||
|
|||||||
@@ -75,11 +75,11 @@ object NostrChatroomListDataSource: NostrDataSource<Note>("MailBoxFeed") {
|
|||||||
|
|
||||||
// returns the last Note of each user.
|
// returns the last Note of each user.
|
||||||
override fun feed(): List<Note> {
|
override fun feed(): List<Note> {
|
||||||
val messages = account.userProfile().messages
|
val privateChatrooms = account.userProfile().privateChatrooms
|
||||||
val messagingWith = messages.keys.filter { account.isAcceptable(it) }
|
val messagingWith = privateChatrooms.keys.filter { account.isAcceptable(it) }
|
||||||
|
|
||||||
val privateMessages = messagingWith.mapNotNull {
|
val privateMessages = messagingWith.mapNotNull {
|
||||||
messages[it]?.sortedBy { it.event?.createdAt }?.lastOrNull { it.event != null }
|
privateChatrooms[it]?.roomMessages?.sortedBy { it.event?.createdAt }?.lastOrNull { it.event != null }
|
||||||
}
|
}
|
||||||
|
|
||||||
val publicChannels = account.followingChannels().map {
|
val publicChannels = account.followingChannels().map {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.vitorpamplona.amethyst.service
|
|||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
|
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
|
||||||
|
import com.vitorpamplona.amethyst.model.toHexKey
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelCreateEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelHideMessageEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelHideMessageEvent
|
||||||
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
|
import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent
|
||||||
@@ -33,11 +34,14 @@ import nostr.postr.events.TextNoteEvent
|
|||||||
|
|
||||||
abstract class NostrDataSource<T>(val debugName: String) {
|
abstract class NostrDataSource<T>(val debugName: String) {
|
||||||
private var subscriptions = mapOf<String, Subscription>()
|
private var subscriptions = mapOf<String, Subscription>()
|
||||||
private var eventCounter = mapOf<String, Int>()
|
|
||||||
|
data class Counter(var counter:Int)
|
||||||
|
|
||||||
|
private var eventCounter = mapOf<String, Counter>()
|
||||||
|
|
||||||
fun printCounter() {
|
fun printCounter() {
|
||||||
eventCounter.forEach {
|
eventCounter.forEach {
|
||||||
println("AAA Count ${it.key}: ${it.value}")
|
println("AAA Count ${it.key}: ${it.value.counter}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,9 +51,9 @@ abstract class NostrDataSource<T>(val debugName: String) {
|
|||||||
val key = "${debugName} ${subscriptionId} ${event.kind}"
|
val key = "${debugName} ${subscriptionId} ${event.kind}"
|
||||||
val keyValue = eventCounter.get(key)
|
val keyValue = eventCounter.get(key)
|
||||||
if (keyValue != null) {
|
if (keyValue != null) {
|
||||||
eventCounter = eventCounter + Pair(key, keyValue + 1)
|
keyValue.counter++
|
||||||
} else {
|
} else {
|
||||||
eventCounter = eventCounter + Pair(key, 1)
|
eventCounter = eventCounter + Pair(key, Counter(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ object NostrSingleEventDataSource: NostrDataSource<Note>("SingleEventFeed") {
|
|||||||
val now = Date().time / 1000
|
val now = Date().time / 1000
|
||||||
|
|
||||||
return reactionsToWatch.filter {
|
return reactionsToWatch.filter {
|
||||||
val lastTime = it.lastReactionsDownloadTime;
|
val lastTime = it.lastReactionsDownloadTime
|
||||||
lastTime == null || lastTime < (now - 10)
|
lastTime == null || lastTime < (now - 10)
|
||||||
}.map {
|
}.map {
|
||||||
TypedFilter(
|
TypedFilter(
|
||||||
@@ -78,7 +78,7 @@ object NostrSingleEventDataSource: NostrDataSource<Note>("SingleEventFeed") {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val singleEventChannel = requestNewChannel() { time ->
|
val singleEventChannel = requestNewChannel { time ->
|
||||||
eventsToWatch.forEach {
|
eventsToWatch.forEach {
|
||||||
LocalCache.getOrCreateNote(it).lastReactionsDownloadTime = time
|
LocalCache.getOrCreateNote(it).lastReactionsDownloadTime = time
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user