More Performant UpdateFollows

This commit is contained in:
Vitor Pamplona
2023-02-09 17:52:18 -05:00
parent ae8d39df92
commit 50bc55e84b
2 changed files with 64 additions and 21 deletions
@@ -17,6 +17,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import com.vitorpamplona.amethyst.service.relays.Relay import com.vitorpamplona.amethyst.service.relays.Relay
import java.util.Date import java.util.Date
import java.util.concurrent.atomic.AtomicBoolean
import java.util.regex.Matcher import java.util.regex.Matcher
import java.util.regex.Pattern import java.util.regex.Pattern
import nostr.postr.events.Event import nostr.postr.events.Event
@@ -194,17 +195,19 @@ class Note(val idHex: String) {
class NoteLiveData(val note: Note): LiveData<NoteState>(NoteState(note)) { class NoteLiveData(val note: Note): LiveData<NoteState>(NoteState(note)) {
// Refreshes observers in batches. // Refreshes observers in batches.
var handlerWaiting = false var handlerWaiting = AtomicBoolean()
@Synchronized @Synchronized
fun invalidateData() { fun invalidateData() {
if (handlerWaiting) return if (!hasActiveObservers()) return
if (handlerWaiting.getAndSet(true)) return
handlerWaiting = true handlerWaiting.set(true)
val scope = CoroutineScope(Job() + Dispatchers.Default) val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch { scope.launch {
delay(100) delay(100)
refresh() refresh()
handlerWaiting = false handlerWaiting.set(false)
} }
} }
@@ -10,6 +10,9 @@ import com.vitorpamplona.amethyst.ui.note.toShortenHex
import fr.acinq.secp256k1.Hex import fr.acinq.secp256k1.Hex
import java.util.Collections import java.util.Collections
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
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
@@ -81,7 +84,11 @@ class User(val pubkeyHex: String) {
liveFollows.invalidateData() liveFollows.invalidateData()
user.liveFollows.invalidateData() user.liveFollows.invalidateData()
listeners.forEach { updateSubscribers {
it.onFollowsChange()
}
user.updateSubscribers {
it.onFollowsChange() it.onFollowsChange()
} }
} }
@@ -95,6 +102,40 @@ class User(val pubkeyHex: String) {
updateSubscribers { updateSubscribers {
it.onFollowsChange() it.onFollowsChange()
} }
user.updateSubscribers {
it.onFollowsChange()
}
}
fun follow(users: Set<User>, followedAt: Long) {
follows = follows + users
users.forEach {
it.followers = it.followers + this
it.liveFollows.invalidateData()
it.updateSubscribers {
it.onFollowsChange()
}
}
liveFollows.invalidateData()
updateSubscribers {
it.onFollowsChange()
}
}
fun unfollow(users: Set<User>) {
follows = follows - users
users.forEach {
it.followers = it.followers - this
it.liveFollows.invalidateData()
it.updateSubscribers {
it.onFollowsChange()
}
}
liveFollows.invalidateData()
updateSubscribers {
it.onFollowsChange()
}
} }
fun addTaggedPost(note: Note) { fun addTaggedPost(note: Note) {
@@ -169,12 +210,8 @@ class User(val pubkeyHex: String) {
val toBeAdded = newFollows - follows val toBeAdded = newFollows - follows
val toBeRemoved = follows - newFollows val toBeRemoved = follows - newFollows
toBeAdded.forEach { follow(toBeAdded, updateAt)
follow(it, updateAt) unfollow(toBeRemoved)
}
toBeRemoved.forEach {
unfollow(it)
}
updatedFollowsAt = updateAt updatedFollowsAt = updateAt
} }
@@ -239,19 +276,20 @@ class User(val pubkeyHex: String) {
} }
// Refreshes observers in batches. // Refreshes observers in batches.
var modelHandlerWaiting = false var modelHandlerWaiting = AtomicBoolean()
@Synchronized @Synchronized
fun updateSubscribers(on: (Listener) -> Unit) { fun updateSubscribers(on: (Listener) -> Unit) {
if (modelHandlerWaiting) return if (modelHandlerWaiting.getAndSet(true)) return
modelHandlerWaiting = true modelHandlerWaiting.set(true)
val scope = CoroutineScope(Job() + Dispatchers.Default) val scope = CoroutineScope(Job() + Dispatchers.Default)
scope.launch { scope.launch {
delay(100) delay(100)
listeners.forEach { listeners.forEach {
on(it) on(it)
} }
modelHandlerWaiting = false modelHandlerWaiting.set(false)
} }
} }
@@ -292,17 +330,19 @@ class UserMetadata {
class UserLiveData(val user: User): LiveData<UserState>(UserState(user)) { class UserLiveData(val user: User): LiveData<UserState>(UserState(user)) {
// Refreshes observers in batches. // Refreshes observers in batches.
var handlerWaiting = false var handlerWaiting = AtomicBoolean()
@Synchronized @Synchronized
fun invalidateData() { fun invalidateData() {
if (handlerWaiting) return if (!hasActiveObservers()) return
if (handlerWaiting.getAndSet(true)) return
handlerWaiting = true handlerWaiting.set(true)
val scope = CoroutineScope(Job() + Dispatchers.Main) val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch { scope.launch {
delay(100) delay(100)
refresh() refresh()
handlerWaiting = false handlerWaiting.set(false)
} }
} }