Merge pull request #75 from clackbib/habib/threading-perf-fixes

Scroll Perf Improvements.
This commit is contained in:
Vitor Pamplona
2023-02-05 18:18:29 -05:00
committed by GitHub
4 changed files with 51 additions and 62 deletions
@@ -11,6 +11,9 @@ import nostr.postr.events.Event
* RelayPool manages the connection to multiple Relays and lets consumers deal with simple events. * RelayPool manages the connection to multiple Relays and lets consumers deal with simple events.
*/ */
object RelayPool: Relay.Listener { object RelayPool: Relay.Listener {
val scope = CoroutineScope(Job() + Dispatchers.IO)
private var relays = listOf<Relay>() private var relays = listOf<Relay>()
private var listeners = setOf<Listener>() private var listeners = setOf<Listener>()
@@ -114,7 +117,6 @@ object RelayPool: Relay.Listener {
val live: RelayPoolLiveData = RelayPoolLiveData(this) val live: RelayPoolLiveData = RelayPoolLiveData(this)
private fun refreshObservers() { private fun refreshObservers() {
val scope = CoroutineScope(Job() + Dispatchers.Main)
scope.launch { scope.launch {
live.refresh() live.refresh()
} }
@@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class CardFeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() { class CardFeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() {
private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading) private val _feedContent = MutableStateFlow<CardFeedState>(CardFeedState.Loading)
@@ -29,11 +30,8 @@ class CardFeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() {
private var lastNotes: List<Note>? = null private var lastNotes: List<Note>? = null
fun refresh() { suspend fun refresh() = withContext(Dispatchers.IO) {
val scope = CoroutineScope(Job() + Dispatchers.Default) refreshSuspended()
scope.launch {
refreshSuspended()
}
} }
private fun refreshSuspended() { private fun refreshSuspended() {
@@ -83,19 +81,16 @@ class CardFeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel() {
return (reactionCards + boostCards + textNoteCards).sortedBy { it.createdAt() }.reversed() return (reactionCards + boostCards + textNoteCards).sortedBy { it.createdAt() }.reversed()
} }
fun updateFeed(notes: List<Card>) { private fun updateFeed(notes: List<Card>) {
val scope = CoroutineScope(Job() + Dispatchers.Main) val currentState = feedContent.value
scope.launch {
val currentState = feedContent.value
if (notes.isEmpty()) { if (notes.isEmpty()) {
_feedContent.update { CardFeedState.Empty } _feedContent.update { CardFeedState.Empty }
} else if (currentState is CardFeedState.Loaded) { } else if (currentState is CardFeedState.Loaded) {
// updates the current list // updates the current list
currentState.feed.value = notes currentState.feed.value = notes
} else { } else {
_feedContent.update { CardFeedState.Loaded(mutableStateOf(notes)) } _feedContent.update { CardFeedState.Loaded(mutableStateOf(notes)) }
}
} }
} }
@@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import nostr.postr.events.TextNoteEvent import nostr.postr.events.TextNoteEvent
import java.util.concurrent.atomic.AtomicBoolean
class NostrChannelFeedViewModel: FeedViewModel(NostrChannelDataSource) class NostrChannelFeedViewModel: FeedViewModel(NostrChannelDataSource)
class NostrChatRoomFeedViewModel: FeedViewModel(NostrChatRoomDataSource) class NostrChatRoomFeedViewModel: FeedViewModel(NostrChatRoomDataSource)
@@ -82,25 +83,21 @@ abstract class FeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel()
} }
fun refresh() { fun refresh() {
viewModelScope.launch(Dispatchers.Default) { viewModelScope.launch(Dispatchers.IO) {
val notes = newListFromDataSource() val notes = newListFromDataSource()
val oldNotesState = feedContent.value val oldNotesState = feedContent.value
if (oldNotesState is FeedState.Loaded) { if (oldNotesState is FeedState.Loaded) {
if (notes != oldNotesState.feed) { if (notes != oldNotesState.feed) {
withContext(Dispatchers.Main) {
updateFeed(notes)
}
}
} else {
withContext(Dispatchers.Main) {
updateFeed(notes) updateFeed(notes)
} }
} else {
updateFeed(notes)
} }
} }
} }
fun updateFeed(notes: List<Note>) { private fun updateFeed(notes: List<Note>) {
val currentState = feedContent.value val currentState = feedContent.value
if (notes.isEmpty()) { if (notes.isEmpty()) {
@@ -113,19 +110,18 @@ abstract class FeedViewModel(val dataSource: NostrDataSource<Note>): ViewModel()
} }
} }
var handlerWaiting = false private var handlerWaiting = AtomicBoolean()
fun invalidateData() { @Synchronized
synchronized(handlerWaiting) { private fun invalidateData() {
if (handlerWaiting) return if (handlerWaiting.get()) return
handlerWaiting = true handlerWaiting.set(true)
val scope = CoroutineScope(Job() + Dispatchers.Default) val scope = CoroutineScope(Job() + Dispatchers.Default)
scope.launch { scope.launch {
delay(100) delay(100)
refresh() refresh()
handlerWaiting = false handlerWaiting.set(false)
} }
}
} }
private val cacheListener: (LocalCacheState) -> Unit = { private val cacheListener: (LocalCacheState) -> Unit = {
@@ -22,6 +22,8 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.atomic.AtomicBoolean
class NostrUserProfileFollowsUserFeedViewModel(): UserFeedViewModel( class NostrUserProfileFollowsUserFeedViewModel(): UserFeedViewModel(
NostrUserProfileFollowsDataSource NostrUserProfileFollowsDataSource
@@ -39,13 +41,11 @@ open class UserFeedViewModel(val dataSource: NostrDataSource<User>): ViewModel()
private val _feedContent = MutableStateFlow<UserFeedState>(UserFeedState.Loading) private val _feedContent = MutableStateFlow<UserFeedState>(UserFeedState.Loading)
val feedContent = _feedContent.asStateFlow() val feedContent = _feedContent.asStateFlow()
fun refresh() { suspend fun refresh() = withContext(Dispatchers.IO) {
val scope = CoroutineScope(Job() + Dispatchers.Default) refreshSuspended()
scope.launch {
refreshSuspended()
}
} }
private fun refreshSuspended() { private fun refreshSuspended() {
val notes = dataSource.loadTop() val notes = dataSource.loadTop()
@@ -59,34 +59,30 @@ open class UserFeedViewModel(val dataSource: NostrDataSource<User>): ViewModel()
} }
} }
fun updateFeed(notes: List<User>) { private fun updateFeed(notes: List<User>) {
val scope = CoroutineScope(Job() + Dispatchers.Main) val currentState = feedContent.value
scope.launch { if (notes.isEmpty()) {
val currentState = feedContent.value _feedContent.update { UserFeedState.Empty }
} else if (currentState is UserFeedState.Loaded) {
if (notes.isEmpty()) { // updates the current list
_feedContent.update { UserFeedState.Empty } currentState.feed.value = notes
} else if (currentState is UserFeedState.Loaded) { } else {
// updates the current list _feedContent.update { UserFeedState.Loaded(mutableStateOf(notes)) }
currentState.feed.value = notes
} else {
_feedContent.update { UserFeedState.Loaded(mutableStateOf(notes)) }
}
} }
} }
var handlerWaiting = false var handlerWaiting = AtomicBoolean()
fun invalidateData() {
synchronized(handlerWaiting) {
if (handlerWaiting) return
handlerWaiting = true @Synchronized
val scope = CoroutineScope(Job() + Dispatchers.Default) private fun invalidateData() {
scope.launch { if (handlerWaiting.get()) return
delay(100)
refresh() handlerWaiting.set(true)
handlerWaiting = false val scope = CoroutineScope(Job() + Dispatchers.Default)
} scope.launch {
delay(100)
refresh()
handlerWaiting.set(false)
} }
} }