Prevent parallel loading and fix duplicate ViewModel state collection in FiltersActivity (#4472)

This pull request fixes the following issues:

- `FiltersActivity` launches a new coroutine to collect the ViewModel
state every time the Activity is resumed, without cancelling the
previous coroutine.
- `FiltersActivity` reloads the filters in `onResume()`, even if loading
is already in progress (without cancelling the current loading). This
can lead to inconsistent state.

List of improvements:
- Implement `launchAndRepeatOnLifecycle()` to combine
`coroutineScope.launch()` with `repeatOnLifecycle()` for the same
`Lifecycle`. Use it in `FiltersActivity` to update the view only when
the Activity is visible.
- Optimize the filters loading: load them when `FiltersViewModel` is
created and when returning from `EditFilterActivity` (when receiving the
Activity result). Cancel the load already in progress, if any.
- use `MutableStateFlow.update()` to update the state in a thread-safe
way.
- Turn `FiltersViewModel.deleteFilter()` into a suspending function in
order to perform the update in the coroutinescope of the Activity
lifecycle, so the View passed as argument doesn't leak.
- Wait for an ongoing load operation to complete before performing a
delete filter operation, so the state stays consistent.
- Add `Intent.withSlideInAnimation()` as a simpler and more flexible
alternative to `Activity.startActivityWithSlideInAnimation(Intent)`.
This commit is contained in:
Christophe Beyls 2024-05-31 13:42:21 +02:00 committed by GitHub
commit d200d1e15e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 101 additions and 57 deletions

View file

@ -12,10 +12,13 @@ import androidx.lifecycle.LifecycleEventObserver
import com.keylesspalace.tusky.BaseActivity
fun Activity.startActivityWithSlideInAnimation(intent: Intent) {
startActivity(intent.withSlideInAnimation())
}
fun Intent.withSlideInAnimation(): Intent {
// the new transition api needs to be called by the activity that is the result of the transition,
// so we pass a flag that BaseActivity will respect.
intent.putExtra(BaseActivity.OPEN_WITH_SLIDE_IN, true)
startActivity(intent)
return putExtra(BaseActivity.OPEN_WITH_SLIDE_IN, true)
}
/**

View file

@ -0,0 +1,21 @@
package com.keylesspalace.tusky.util
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
fun Lifecycle.launchAndRepeatOnLifecycle(
state: Lifecycle.State = Lifecycle.State.STARTED,
block: suspend CoroutineScope.() -> Unit
): Job = coroutineScope.launch {
repeatOnLifecycle(state, block)
}
fun LifecycleOwner.launchAndRepeatOnLifecycle(
state: Lifecycle.State = Lifecycle.State.STARTED,
block: suspend CoroutineScope.() -> Unit
): Job = lifecycle.launchAndRepeatOnLifecycle(state, block)