diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/FeedContentState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/FeedContentState.kt index 002f78f5a..c1dd8c15d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/FeedContentState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/FeedContentState.kt @@ -21,7 +21,11 @@ package com.vitorpamplona.amethyst.ui.feeds import android.util.Log +import androidx.compose.runtime.MutableState import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.service.checkNotInMainThread @@ -54,6 +58,8 @@ class FeedContentState( private var lastFeedKey: String? = null + override val isRefreshing: MutableState = mutableStateOf(false) + fun sendToTop() { if (scrolltoTopPending) return @@ -72,16 +78,21 @@ class FeedContentState( fun refreshSuspended() { checkNotInMainThread() - lastFeedKey = localFilter.feedKey() - val notes = localFilter.loadTop().distinctBy { it.idHex }.toImmutableList() + isRefreshing.value = true + try { + lastFeedKey = localFilter.feedKey() + val notes = localFilter.loadTop().distinctBy { it.idHex }.toImmutableList() - val oldNotesState = _feedContent.value - if (oldNotesState is FeedState.Loaded) { - if (!equalImmutableLists(notes, oldNotesState.feed.value.list)) { + val oldNotesState = _feedContent.value + if (oldNotesState is FeedState.Loaded) { + if (!equalImmutableLists(notes, oldNotesState.feed.value.list)) { + updateFeed(notes) + } + } else { updateFeed(notes) } - } else { - updateFeed(notes) + } finally { + isRefreshing.value = false } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/InvalidatableContent.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/InvalidatableContent.kt index 4837aaa4a..8a0f4a610 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/InvalidatableContent.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/feeds/InvalidatableContent.kt @@ -20,6 +20,10 @@ */ package com.vitorpamplona.amethyst.ui.feeds +import androidx.compose.runtime.MutableState + interface InvalidatableContent { fun invalidateData(ignoreIfDoing: Boolean = false) + + val isRefreshing: MutableState } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt index 8b410cb6f..b8c5e8eb1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/FeedViewModel.kt @@ -260,6 +260,8 @@ abstract class FeedViewModel( InvalidatableContent { val feedState = FeedContentState(localFilter, viewModelScope) + override val isRefreshing = feedState.isRefreshing + fun sendToTop() = feedState.sendToTop() suspend fun sentToTop() = feedState.sentToTop() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt index 84e916f80..9873b40f3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/UserFeedViewModel.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen import android.util.Log +import androidx.compose.runtime.MutableState import androidx.compose.runtime.Stable import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel @@ -107,19 +108,27 @@ open class UserFeedViewModel( viewModelScope.launch(Dispatchers.Default) { refreshSuspended() } } + override val isRefreshing: MutableState = mutableStateOf(false) + private fun refreshSuspended() { checkNotInMainThread() - val notes = dataSource.loadTop().toImmutableList() + try { + isRefreshing.value = true - val oldNotesState = _feedContent.value - if (oldNotesState is UserFeedState.Loaded) { - // Using size as a proxy for has changed. - if (!equalImmutableLists(notes, oldNotesState.feed.value)) { + val notes = dataSource.loadTop().toImmutableList() + + val oldNotesState = _feedContent.value + if (oldNotesState is UserFeedState.Loaded) { + // Using size as a proxy for has changed. + if (!equalImmutableLists(notes, oldNotesState.feed.value)) { + updateFeed(notes) + } + } else { updateFeed(notes) } - } else { - updateFeed(notes) + } finally { + isRefreshing.value = false } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt index b01cd84c7..02600d693 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/CardFeedContentState.kt @@ -22,7 +22,9 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications import android.util.Log import androidx.compose.runtime.Immutable +import androidx.compose.runtime.MutableState import androidx.compose.runtime.Stable +import androidx.compose.runtime.mutableStateOf import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note @@ -72,6 +74,8 @@ class CardFeedContentState( private var lastFeedKey: String? = null + override val isRefreshing: MutableState = mutableStateOf(false) + fun sendToTop() { if (scrolltoTopPending) return @@ -94,41 +98,47 @@ class CardFeedContentState( private fun refreshSuspended() { checkNotInMainThread() - val notes = localFilter.feed() - lastFeedKey = localFilter.feedKey() + try { + isRefreshing.value = true - val thisAccount = (localFilter as? NotificationFeedFilter)?.account - val lastNotesCopy = if (thisAccount == lastAccount) lastNotes else null + val notes = localFilter.feed() + lastFeedKey = localFilter.feedKey() - val oldNotesState = _feedContent.value - if (lastNotesCopy != null && oldNotesState is CardFeedState.Loaded) { - val newCards = convertToCard(notes.minus(lastNotesCopy)) - if (newCards.isNotEmpty()) { + val thisAccount = (localFilter as? NotificationFeedFilter)?.account + val lastNotesCopy = if (thisAccount == lastAccount) lastNotes else null + + val oldNotesState = _feedContent.value + if (lastNotesCopy != null && oldNotesState is CardFeedState.Loaded) { + val newCards = convertToCard(notes.minus(lastNotesCopy)) + if (newCards.isNotEmpty()) { + lastNotes = notes.toSet() + lastAccount = (localFilter as? NotificationFeedFilter)?.account + + val updatedCards = + (oldNotesState.feed.value.list + newCards) + .distinctBy { it.id() } + .sortedWith(DefaultFeedOrderCard) + .take(localFilter.limit()) + .toImmutableList() + + if (!equalImmutableLists(oldNotesState.feed.value.list, updatedCards)) { + updateFeed(updatedCards) + } + } + } else { lastNotes = notes.toSet() lastAccount = (localFilter as? NotificationFeedFilter)?.account - val updatedCards = - (oldNotesState.feed.value.list + newCards) - .distinctBy { it.id() } + val cards = + convertToCard(notes) .sortedWith(DefaultFeedOrderCard) .take(localFilter.limit()) .toImmutableList() - if (!equalImmutableLists(oldNotesState.feed.value.list, updatedCards)) { - updateFeed(updatedCards) - } + updateFeed(cards) } - } else { - lastNotes = notes.toSet() - lastAccount = (localFilter as? NotificationFeedFilter)?.account - - val cards = - convertToCard(notes) - .sortedWith(DefaultFeedOrderCard) - .take(localFilter.limit()) - .toImmutableList() - - updateFeed(cards) + } finally { + isRefreshing.value = false } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/RelayFeedViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/RelayFeedViewModel.kt index 227ca70e4..c41726832 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/RelayFeedViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/RelayFeedViewModel.kt @@ -21,7 +21,9 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.profile import android.util.Log +import androidx.compose.runtime.MutableState import androidx.compose.runtime.Stable +import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.vitorpamplona.amethyst.model.RelayInfo @@ -50,28 +52,36 @@ class RelayFeedViewModel : var currentUser: User? = null + override val isRefreshing: MutableState = mutableStateOf(false) + fun refresh() { viewModelScope.launch(Dispatchers.Default) { refreshSuspended() } } fun refreshSuspended() { - val beingUsed = currentUser?.relaysBeingUsed?.values ?: emptyList() - val beingUsedSet = currentUser?.relaysBeingUsed?.keys ?: emptySet() + try { + isRefreshing.value = true - val newRelaysFromRecord = - currentUser?.latestContactList?.relays()?.entries?.mapNotNullTo(HashSet()) { - val url = RelayUrlFormatter.normalize(it.key) - if (url !in beingUsedSet) { - RelayInfo(url, 0, 0) - } else { - null + val beingUsed = currentUser?.relaysBeingUsed?.values ?: emptyList() + val beingUsedSet = currentUser?.relaysBeingUsed?.keys ?: emptySet() + + val newRelaysFromRecord = + currentUser?.latestContactList?.relays()?.entries?.mapNotNullTo(HashSet()) { + val url = RelayUrlFormatter.normalize(it.key) + if (url !in beingUsedSet) { + RelayInfo(url, 0, 0) + } else { + null + } } - } - ?: emptyList() + ?: emptyList() - val newList = (beingUsed + newRelaysFromRecord).sortedWith(order) + val newList = (beingUsed + newRelaysFromRecord).sortedWith(order) - _feedContent.update { newList } + _feedContent.update { newList } + } finally { + isRefreshing.value = false + } } val listener: (UserState) -> Unit = { invalidateData() } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedViewModel.kt index 2fb82dd6f..54746bf31 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/StringFeedViewModel.kt @@ -21,6 +21,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings import android.util.Log +import androidx.compose.runtime.MutableState import androidx.compose.runtime.Stable import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel @@ -63,6 +64,8 @@ open class StringFeedViewModel( private val _feedContent = MutableStateFlow(StringFeedState.Loading) val feedContent = _feedContent.asStateFlow() + override val isRefreshing: MutableState = mutableStateOf(false) + private fun refresh() { viewModelScope.launch(Dispatchers.Default) { refreshSuspended() } } @@ -70,16 +73,22 @@ open class StringFeedViewModel( private fun refreshSuspended() { checkNotInMainThread() - val notes = dataSource.loadTop().toImmutableList() + try { + isRefreshing.value = true - val oldNotesState = _feedContent.value - if (oldNotesState is StringFeedState.Loaded) { - // Using size as a proxy for has changed. - if (!equalImmutableLists(notes, oldNotesState.feed.value)) { + val notes = dataSource.loadTop().toImmutableList() + + val oldNotesState = _feedContent.value + if (oldNotesState is StringFeedState.Loaded) { + // Using size as a proxy for has changed. + if (!equalImmutableLists(notes, oldNotesState.feed.value)) { + updateFeed(notes) + } + } else { updateFeed(notes) } - } else { - updateFeed(notes) + } finally { + isRefreshing.value = false } }