Add a menu option to load the newest notifications (#3708)

- Create a flow with new items (arbitrary ints) when a reload from the top should happen
- Combine this flow with notificationFilter, so changes to either of them trigger a reload
- Provide a menu item in NotificationsFragment to initiate the reload
- Handle the action in the view model
This commit is contained in:
Nik Clayton 2023-06-11 20:04:49 +02:00 committed by GitHub
parent 4cddd2c5e6
commit 5755801e6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 36 additions and 3 deletions

View file

@ -454,6 +454,10 @@ class NotificationsFragment :
onRefresh() onRefresh()
true true
} }
R.id.load_newest -> {
viewModel.accept(InfallibleUiAction.LoadNewest)
true
}
else -> false else -> false
} }
} }

View file

@ -59,6 +59,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.getAndUpdate
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.onStart
@ -123,6 +124,12 @@ sealed class InfallibleUiAction : UiAction() {
* can do. * can do.
*/ */
data class SaveVisibleId(val visibleId: String) : InfallibleUiAction() data class SaveVisibleId(val visibleId: String) : InfallibleUiAction()
/** Ignore the saved reading position, load the page with the newest items */
// Resets the account's `lastNotificationId`, which can't fail, which is why this is
// infallible. Reloading the data may fail, but that's handled by the paging system /
// adapter refresh logic.
object LoadNewest : InfallibleUiAction()
} }
/** Actions the user can trigger on an individual notification. These may fail. */ /** Actions the user can trigger on an individual notification. These may fail. */
@ -300,6 +307,9 @@ class NotificationsViewModel @Inject constructor(
/** Flow of user actions received from the UI */ /** Flow of user actions received from the UI */
private val uiAction = MutableSharedFlow<UiAction>() private val uiAction = MutableSharedFlow<UiAction>()
/** Flow that can be used to trigger a full reload */
private val reload = MutableStateFlow(0)
/** Flow of successful action results */ /** Flow of successful action results */
// Note: This is a SharedFlow instead of a StateFlow because success state does not need to be // Note: This is a SharedFlow instead of a StateFlow because success state does not need to be
// retained. A message is shown once to a user and then dismissed. Re-collecting the flow // retained. A message is shown once to a user and then dismissed. Re-collecting the flow
@ -342,6 +352,18 @@ class NotificationsViewModel @Inject constructor(
) )
} }
// Reset the last notification ID to "0" to fetch the newest notifications, and
// increment `reload` to trigger creation of a new PagingSource.
viewModelScope.launch {
uiAction
.filterIsInstance<InfallibleUiAction.LoadNewest>()
.collectLatest {
account.lastNotificationId = "0"
accountManager.saveAccount(account)
reload.getAndUpdate { it + 1 }
}
}
// Save the visible notification ID // Save the visible notification ID
viewModelScope.launch { viewModelScope.launch {
uiAction uiAction
@ -465,11 +487,12 @@ class NotificationsViewModel @Inject constructor(
} }
} }
pagingData = notificationFilter // Re-fetch notifications if either of `notificationFilter` or `reload` flows have
// new items.
pagingData = combine(notificationFilter, reload) { action, _ -> action }
.flatMapLatest { action -> .flatMapLatest { action ->
getNotifications(filters = action.filter, initialKey = getInitialKey()) getNotifications(filters = action.filter, initialKey = getInitialKey())
} }.cachedIn(viewModelScope)
.cachedIn(viewModelScope)
uiState = combine(notificationFilter, getUiPrefs()) { filter, prefs -> uiState = combine(notificationFilter, getUiPrefs()) { filter, prefs ->
UiState( UiState(

View file

@ -22,4 +22,9 @@
android:id="@+id/action_refresh" android:id="@+id/action_refresh"
android:title="@string/action_refresh" android:title="@string/action_refresh"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/load_newest"
android:title="@string/load_newest_notifications"
app:showAsAction="never" />
</menu> </menu>

View file

@ -810,4 +810,5 @@
you follow.\n\nTo explore accounts you can either discover them in one of the other timelines. you follow.\n\nTo explore accounts you can either discover them in one of the other timelines.
For example the local timeline of your instance [iconics gmd_group]. Or you can search them For example the local timeline of your instance [iconics gmd_group]. Or you can search them
by name [iconics gmd_search]; for example search for Tusky to find our Mastodon account.</string> by name [iconics gmd_search]; for example search for Tusky to find our Mastodon account.</string>
<string name="load_newest_notifications">Load newest notifications</string>
</resources> </resources>