Don't use mutable shared flows in UI (#4346)

This commit is contained in:
Zongle Wang 2024-03-30 03:02:12 +08:00 committed by GitHub
parent 06f283575d
commit e865ffafde
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 45 additions and 36 deletions

View File

@ -15,11 +15,11 @@ interface Event
@Singleton
class EventHub @Inject constructor() {
private val sharedEventFlow = MutableSharedFlow<Event>()
val events: SharedFlow<Event> = sharedEventFlow.asSharedFlow()
private val _events = MutableSharedFlow<Event>()
val events: SharedFlow<Event> = _events.asSharedFlow()
suspend fun dispatch(event: Event) {
sharedEventFlow.emit(event)
_events.emit(event)
}
// TODO remove as soon as NotificationsFragment is Kotlin

View File

@ -28,6 +28,7 @@ import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
@ -53,13 +54,13 @@ class ListsForAccountViewModel @Inject constructor(
) : ViewModel() {
private val _states = MutableSharedFlow<List<AccountListState>>(1)
val states: SharedFlow<List<AccountListState>> = _states
val states: SharedFlow<List<AccountListState>> = _states.asSharedFlow()
private val _loadError = MutableSharedFlow<Throwable>(1)
val loadError: SharedFlow<Throwable> = _loadError
val loadError: SharedFlow<Throwable> = _loadError.asSharedFlow()
private val _actionError = MutableSharedFlow<ActionError>(1)
val actionError: SharedFlow<ActionError> = _actionError
val actionError: SharedFlow<ActionError> = _actionError.asSharedFlow()
fun load(accountId: String?) {
_loadError.resetReplayCache()

View File

@ -47,6 +47,7 @@ import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.shareIn
@ -107,12 +108,12 @@ class ComposeViewModel @Inject constructor(
private val _media = MutableStateFlow(emptyList<QueuedMedia>())
val media: StateFlow<List<QueuedMedia>> = _media.asStateFlow()
val uploadError =
MutableSharedFlow<Throwable>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
private val _uploadError = MutableSharedFlow<Throwable>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val uploadError: SharedFlow<Throwable> = _uploadError.asSharedFlow()
private val _closeConfirmation = MutableStateFlow(ConfirmationKind.NONE)
val closeConfirmation: StateFlow<ConfirmationKind> = _closeConfirmation.asStateFlow()
@ -202,7 +203,7 @@ class ComposeViewModel @Inject constructor(
)
is UploadEvent.ErrorEvent -> {
_media.update { mediaList -> mediaList.filter { it.localId != mediaItem.localId } }
uploadError.emit(event.error)
_uploadError.emit(event.error)
return@collect
}
}

View File

@ -10,6 +10,8 @@ import at.connyduck.calladapter.networkresult.onFailure
import com.keylesspalace.tusky.R
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
class DomainBlocksViewModel @Inject constructor(
@ -18,12 +20,13 @@ class DomainBlocksViewModel @Inject constructor(
val domainPager = repo.domainPager.cachedIn(viewModelScope)
val uiEvents = MutableSharedFlow<SnackbarEvent>()
private val _uiEvents = MutableSharedFlow<SnackbarEvent>()
val uiEvents: SharedFlow<SnackbarEvent> = _uiEvents.asSharedFlow()
fun block(domain: String) {
viewModelScope.launch {
repo.block(domain).onFailure { e ->
uiEvents.emit(
_uiEvents.emit(
SnackbarEvent(
message = R.string.error_blocking_domain,
domain = domain,
@ -39,7 +42,7 @@ class DomainBlocksViewModel @Inject constructor(
fun unblock(domain: String) {
viewModelScope.launch {
repo.unblock(domain).fold({
uiEvents.emit(
_uiEvents.emit(
SnackbarEvent(
message = R.string.confirmation_domain_unmuted,
domain = domain,
@ -49,7 +52,7 @@ class DomainBlocksViewModel @Inject constructor(
)
)
}, { e ->
uiEvents.emit(
_uiEvents.emit(
SnackbarEvent(
message = R.string.error_unblocking_domain,
domain = domain,

View File

@ -11,8 +11,9 @@ import com.keylesspalace.tusky.entity.Filter
import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.isHttpNotFound
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class FiltersViewModel @Inject constructor(
@ -30,8 +31,8 @@ class FiltersViewModel @Inject constructor(
data class State(val filters: List<Filter>, val loadingState: LoadingState)
val state: Flow<State> get() = _state
private val _state = MutableStateFlow(State(emptyList(), LoadingState.INITIAL))
val state: StateFlow<State> = _state.asStateFlow()
fun load() {
this@FiltersViewModel._state.value = _state.value.copy(loadingState = LoadingState.LOADING)

View File

@ -51,6 +51,8 @@ import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@ -68,14 +70,12 @@ class ViewThreadViewModel @Inject constructor(
private val _uiState = MutableStateFlow(ThreadUiState.Loading as ThreadUiState)
val uiState: Flow<ThreadUiState> = _uiState.asStateFlow()
private val _errors =
MutableSharedFlow<Throwable>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val errors: Flow<Throwable>
get() = _errors
private val _errors = MutableSharedFlow<Throwable>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val errors: SharedFlow<Throwable> = _errors.asSharedFlow()
var isInitialLoad: Boolean = true

View File

@ -27,9 +27,12 @@ import java.io.IOException
import java.net.ConnectException
import javax.inject.Inject
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
internal class ListsViewModel @Inject constructor(private val api: MastodonApi) : ViewModel() {
@ -49,15 +52,15 @@ internal class ListsViewModel @Inject constructor(private val api: MastodonApi)
data class State(val lists: List<MastoList>, val loadingState: LoadingState)
val state: Flow<State> get() = _state
val events: Flow<Event> get() = _events
private val _state = MutableStateFlow(State(listOf(), LoadingState.INITIAL))
private val _events =
MutableSharedFlow<Event>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val state: StateFlow<State> = _state.asStateFlow()
private val _events = MutableSharedFlow<Event>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val events: SharedFlow<Event> = _events.asSharedFlow()
fun retryLoading() {
loadIfNeeded()