Removing the Duplicated Observer infrastructure on User: It wasn't updating observers correctly due to the batch update we use.

pull/141/head
Vitor Pamplona 2023-02-14 11:01:08 -05:00
rodzic 8e897762dd
commit c3aa37534e
6 zmienionych plików z 62 dodań i 119 usunięć

Wyświetl plik

@ -20,6 +20,7 @@ import java.util.Locale
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -391,13 +392,13 @@ class Account(
}
init {
userProfile().subscribe(object: User.Listener() {
override fun onRelayChange() {
userProfile().liveRelays.observeForever {
GlobalScope.launch(Dispatchers.IO) {
Client.disconnect()
Client.connect(activeRelays() ?: convertLocalRelays())
RelayPool.requestAndWatch()
}
})
}
}
// Observers line up here.

Wyświetl plik

@ -431,7 +431,7 @@ object LocalCache {
return
}
Log.d("ZP", "New ZapEvent ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
//Log.d("ZP", "New ZapEvent ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
// Adds notifications to users.
mentions.forEach {
@ -461,7 +461,7 @@ object LocalCache {
note.loadEvent(event, author, mentions, repliesTo)
Log.d("ZP", "New Zap Request ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
//Log.d("ZP", "New Zap Request ${event.content} (${notes.size},${users.size}) ${note.author?.toBestDisplayName()} ${formattedDateTime(event.createdAt)}")
// Adds notifications to users.
mentions.forEach {

Wyświetl plik

@ -2,6 +2,7 @@ package com.vitorpamplona.amethyst.model
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.model.LnZapEvent
import com.vitorpamplona.amethyst.service.model.ReportEvent
@ -89,29 +90,14 @@ class User(val pubkeyHex: String) {
liveFollows.invalidateData()
user.liveFollows.invalidateData()
updateSubscribers {
it.onFollowsChange()
}
user.updateSubscribers {
it.onFollowsChange()
}
}
fun unfollow(user: User) {
follows = follows - user
user.followers = user.followers - this
liveFollows.invalidateData()
user.liveFollows.invalidateData()
updateSubscribers {
it.onFollowsChange()
}
user.updateSubscribers {
it.onFollowsChange()
}
}
fun follow(users: Set<User>, followedAt: Long) {
@ -119,42 +105,31 @@ class User(val pubkeyHex: String) {
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) {
if (note !in taggedPosts) {
taggedPosts = taggedPosts + note
updateSubscribers { it.onNewTaggedPosts() }
// No need for Listener yet
}
}
fun addNote(note: Note) {
if (note !in notes) {
notes = notes + note
updateSubscribers { it.onNewNotes() }
// No need for Listener yet
}
}
@ -216,7 +191,6 @@ class User(val pubkeyHex: String) {
if (msg !in channel) {
messages = messages + Pair(user, channel + msg)
liveMessages.invalidateData()
updateSubscribers { it.onNewMessage() }
}
}
@ -237,7 +211,6 @@ class User(val pubkeyHex: String) {
here.counter++
}
updateSubscribers { it.onNewRelayInfo() }
liveRelayInfo.invalidateData()
}
@ -251,17 +224,11 @@ class User(val pubkeyHex: String) {
updatedFollowsAt = updateAt
}
data class RelayMetadata(val read: Boolean, val write: Boolean, val activeTypes: Set<FeedType>)
fun updateRelays(relayUse: Map<String, ContactListEvent.ReadWrite>) {
if (relays != relayUse) {
relays = relayUse
listeners.forEach {
it.onRelayChange()
}
liveRelays.invalidateData()
}
liveRelays.invalidateData()
}
fun updateUserInfo(newUserInfo: UserMetadata, updateAt: Long) {
@ -287,45 +254,6 @@ class User(val pubkeyHex: String) {
} != null
}
// Model Observers
private var listeners = setOf<Listener>()
fun subscribe(listener: Listener) {
listeners = listeners.plus(listener)
}
fun unsubscribe(listener: Listener) {
listeners = listeners.minus(listener)
}
abstract class Listener {
open fun onRelayChange() = Unit
open fun onFollowsChange() = Unit
open fun onNewTaggedPosts() = Unit
open fun onNewNotes() = Unit
open fun onNewMessage() = Unit
open fun onNewRelayInfo() = Unit
open fun onNewReports() = Unit
}
// Refreshes observers in batches.
var modelHandlerWaiting = AtomicBoolean()
@Synchronized
fun updateSubscribers(on: (Listener) -> Unit) {
if (modelHandlerWaiting.getAndSet(true)) return
modelHandlerWaiting.set(true)
val scope = CoroutineScope(Job() + Dispatchers.Default)
scope.launch {
delay(100)
listeners.forEach {
on(it)
}
modelHandlerWaiting.set(false)
}
}
// UI Observers line up here.
val liveFollows: UserLiveData = UserLiveData(this)
val liveReports: UserLiveData = UserLiveData(this)

Wyświetl plik

@ -2,11 +2,16 @@ package com.vitorpamplona.amethyst.service
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.LocalCacheState
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.UserState
import com.vitorpamplona.amethyst.service.model.RepostEvent
import com.vitorpamplona.amethyst.service.relays.FeedType
import com.vitorpamplona.amethyst.service.relays.TypedFilter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import nostr.postr.JsonFilter
import nostr.postr.events.TextNoteEvent
import nostr.postr.toHex
@ -14,22 +19,26 @@ import nostr.postr.toHex
object NostrHomeDataSource: NostrDataSource<Note>("HomeFeed") {
lateinit var account: Account
object cacheListener: User.Listener() {
override fun onFollowsChange() {
resetFilters()
}
private val cacheListener: (UserState) -> Unit = {
resetFilters()
}
override fun start() {
if (this::account.isInitialized)
account.userProfile().subscribe(cacheListener)
if (this::account.isInitialized) {
GlobalScope.launch(Dispatchers.Main) {
account.userProfile().liveFollows.observeForever(cacheListener)
}
}
super.start()
}
override fun stop() {
super.stop()
if (this::account.isInitialized)
account.userProfile().unsubscribe(cacheListener)
if (this::account.isInitialized) {
GlobalScope.launch(Dispatchers.Main) {
account.userProfile().liveFollows.removeObserver(cacheListener)
}
}
}
fun createFollowAccountsFilter(): TypedFilter {

Wyświetl plik

@ -78,34 +78,38 @@ abstract class FeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel()
}
fun refresh() {
viewModelScope.launch(Dispatchers.Default) {
val notes = newListFromDataSource()
val scope = CoroutineScope(Job() + Dispatchers.Default)
scope.launch {
refreshSuspended()
}
}
val oldNotesState = feedContent.value
if (oldNotesState is FeedState.Loaded) {
if (notes != oldNotesState.feed) {
withContext(Dispatchers.Main) {
updateFeed(notes)
}
}
} else {
withContext(Dispatchers.Main) {
updateFeed(notes)
}
fun refreshSuspended() {
val notes = newListFromDataSource()
val oldNotesState = feedContent.value
if (oldNotesState is FeedState.Loaded) {
if (notes != oldNotesState.feed) {
updateFeed(notes)
}
} else {
updateFeed(notes)
}
}
private fun updateFeed(notes: List<Note>) {
val currentState = feedContent.value
val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch {
val currentState = feedContent.value
if (notes.isEmpty()) {
_feedContent.update { FeedState.Empty }
} else if (currentState is FeedState.Loaded) {
// updates the current list
currentState.feed.value = notes
} else {
_feedContent.update { FeedState.Loaded(mutableStateOf(notes)) }
if (notes.isEmpty()) {
_feedContent.update { FeedState.Empty }
} else if (currentState is FeedState.Loaded) {
// updates the current list
currentState.feed.value = notes
} else {
_feedContent.update { FeedState.Loaded(mutableStateOf(notes)) }
}
}
}

Wyświetl plik

@ -20,6 +20,8 @@ import androidx.navigation.NavController
import com.google.accompanist.swiperefresh.SwipeRefresh
import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.UserState
import com.vitorpamplona.amethyst.service.NostrHomeDataSource
import com.vitorpamplona.amethyst.ui.actions.NewRelayListView
import com.vitorpamplona.amethyst.ui.note.RelayCompose
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
@ -59,21 +61,20 @@ class RelayFeedViewModel: ViewModel() {
}
}
inner class CacheListener: User.Listener() {
override fun onNewRelayInfo() { invalidateData() }
override fun onRelayChange() { invalidateData() }
val listener: (UserState) -> Unit = {
invalidateData()
}
val listener = CacheListener()
fun subscribeTo(user: User) {
currentUser = user
user.subscribe(listener)
user.liveRelays.observeForever(listener)
user.liveRelayInfo.observeForever(listener)
invalidateData()
}
fun unsubscribeTo(user: User) {
user.unsubscribe(listener)
user.liveRelays.removeObserver(listener)
user.liveRelayInfo.removeObserver(listener)
currentUser = null
}