From 02ad85a740172b61508e16521cf64acd4127feef Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Sun, 28 May 2023 17:53:08 -0400 Subject: [PATCH] Avoids running through the entire filter when collection is Empty --- .../amethyst/ui/screen/CardFeedViewModel.kt | 1 + .../amethyst/ui/screen/FeedState.kt | 3 +- .../amethyst/ui/screen/FeedViewModel.kt | 33 +++++++++++++------ .../ui/screen/loggedIn/VideoScreen.kt | 3 +- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt index bae39a5fe..3d70f2d47 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/CardFeedViewModel.kt @@ -290,6 +290,7 @@ open class CardFeedViewModel(val localFilter: FeedFilter) : ViewModel() { } fun equalImmutableLists(list1: ImmutableList, list2: ImmutableList): Boolean { + if (list1 === list2) return true if (list1.size != list2.size) return false for (i in 0 until list1.size) { if (list1[i] !== list2[i]) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedState.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedState.kt index e0adc1bb7..7c03300d4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedState.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedState.kt @@ -2,10 +2,11 @@ package com.vitorpamplona.amethyst.ui.screen import androidx.compose.runtime.MutableState import com.vitorpamplona.amethyst.model.Note +import kotlinx.collections.immutable.ImmutableList sealed class FeedState { object Loading : FeedState() - class Loaded(val feed: MutableState>) : FeedState() + class Loaded(val feed: MutableState>) : FeedState() object Empty : FeedState() class FeedError(val errorMessage: String) : FeedState() } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt index 1686410bf..07dbf3e68 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt @@ -25,6 +25,8 @@ import com.vitorpamplona.amethyst.ui.dal.UserProfileConversationsFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileNewThreadFeedFilter import com.vitorpamplona.amethyst.ui.dal.UserProfileReportsFeedFilter import com.vitorpamplona.amethyst.ui.dal.VideoFeedFilter +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -55,8 +57,8 @@ abstract class FeedViewModel(val localFilter: FeedFilter) : ViewModel() { private val _feedContent = MutableStateFlow(FeedState.Loading) val feedContent = _feedContent.asStateFlow() - fun newListFromDataSource(): List { - return localFilter.loadTop() + fun newListFromDataSource(): ImmutableList { + return localFilter.loadTop().toImmutableList() } private fun refresh() { @@ -71,8 +73,7 @@ abstract class FeedViewModel(val localFilter: FeedFilter) : ViewModel() { val oldNotesState = _feedContent.value if (oldNotesState is FeedState.Loaded) { - // Using size as a proxy for has changed. - if (notes != oldNotesState.feed.value) { + if (!equalImmutableLists(notes, oldNotesState.feed.value)) { updateFeed(notes) } } else { @@ -80,7 +81,7 @@ abstract class FeedViewModel(val localFilter: FeedFilter) : ViewModel() { } } - private fun updateFeed(notes: List) { + private fun updateFeed(notes: ImmutableList) { val scope = CoroutineScope(Job() + Dispatchers.Main) scope.launch { val currentState = _feedContent.value @@ -97,10 +98,20 @@ abstract class FeedViewModel(val localFilter: FeedFilter) : ViewModel() { fun refreshFromOldState(newItems: Set) { val oldNotesState = _feedContent.value - if (localFilter is AdditiveFeedFilter && oldNotesState is FeedState.Loaded) { - val newList = localFilter.updateListWith(oldNotesState.feed.value, newItems.toSet()) - if (newList !== oldNotesState.feed.value) { - updateFeed(newList) + if (localFilter is AdditiveFeedFilter) { + if (oldNotesState is FeedState.Loaded) { + val newList = localFilter.updateListWith(oldNotesState.feed.value, newItems.toSet()).toImmutableList() + if (!equalImmutableLists(newList, oldNotesState.feed.value)) { + updateFeed(newList) + } + } else if (oldNotesState is FeedState.Empty) { + val newList = localFilter.updateListWith(emptyList(), newItems.toSet()).toImmutableList() + if (newList.isNotEmpty()) { + updateFeed(newList) + } + } else { + // Refresh Everything + refreshSuspended() } } else { // Refresh Everything @@ -130,7 +141,9 @@ abstract class FeedViewModel(val localFilter: FeedFilter) : ViewModel() { init { collectorJob = viewModelScope.launch(Dispatchers.IO) { LocalCache.live.newEventBundles.collect { newNotes -> - if (localFilter is AdditiveFeedFilter && _feedContent.value is FeedState.Loaded) { + if (localFilter is AdditiveFeedFilter && + (_feedContent.value is FeedState.Loaded || _feedContent.value is FeedState.Empty) + ) { invalidateInsertData(newNotes) } else { // Refresh Everything diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt index 3d0405b30..259540d76 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/VideoScreen.kt @@ -86,6 +86,7 @@ import com.vitorpamplona.amethyst.ui.screen.LoadingFeed import com.vitorpamplona.amethyst.ui.screen.NostrVideoFeedViewModel import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys import com.vitorpamplona.amethyst.ui.screen.rememberForeverPagerState +import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -198,7 +199,7 @@ fun FeedView( @OptIn(ExperimentalFoundationApi::class) @Composable fun SlidingCarousel( - feed: MutableState>, + feed: MutableState>, accountViewModel: AccountViewModel, nav: (String) -> Unit, scrollStateKey: String? = null,