Merge pull request #75 from clackbib/habib/threading-perf-fixes
Scroll Perf Improvements.
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user