fix: 事件流被重复 collect

This commit is contained in:
HuanCheng65 2023-03-11 23:52:15 +08:00
parent 055e637681
commit 4673044663
No known key found for this signature in database
GPG Key ID: E9031EF91A805148
6 changed files with 121 additions and 115 deletions

View File

@ -1,9 +1,12 @@
package com.huanchengfly.tieba.post.arch
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
@ -11,11 +14,11 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.cancellable
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
@ -53,18 +56,26 @@ fun <T : UiState, A> Flow<T>.collectPartialAsState(
}
}
inline fun <reified Event : UiEvent> CoroutineScope.onEvent(
viewModel: BaseViewModel<*, *, *, *>,
@Composable
inline fun <reified Event : UiEvent> BaseViewModel<*, *, *, *>.onEvent(
noinline listener: suspend (Event) -> Unit
) {
launch {
viewModel.uiEventFlow
.filterIsInstance<Event>()
.collect {
launch {
listener(it)
}
val coroutineScope = rememberCoroutineScope()
DisposableEffect(key1 = listener, key2 = this) {
with(coroutineScope) {
val job = launch {
uiEventFlow
.filterIsInstance<Event>()
.cancellable()
.collect {
launch {
listener(it)
}
}
}
onDispose { job.cancel() }
}
}
}
@ -73,10 +84,24 @@ inline fun <reified VM : BaseViewModel<*, *, *, *>> pageViewModel(): VM {
return hiltViewModel<VM>().apply {
val context = LocalContext.current
if (context is BaseComposeActivity) {
uiEventFlow.filterIsInstance<CommonUiEvent>()
.collectIn(context) {
context.handleCommonEvent(it)
val coroutineScope = rememberCoroutineScope()
DisposableEffect(key1 = this) {
with(coroutineScope) {
val job =
uiEventFlow
.filterIsInstance<CommonUiEvent>()
.cancellable()
.collectIn(context) {
context.handleCommonEvent(it)
}
onDispose {
Log.i("pageViewModel", "onDispose")
job.cancel()
}
}
}
}
}
}
@ -85,14 +110,7 @@ inline fun <reified VM : BaseViewModel<*, *, *, *>> pageViewModel(): VM {
inline fun <INTENT : UiIntent, reified VM : BaseViewModel<INTENT, *, *, *>> pageViewModel(
initialIntent: List<INTENT> = emptyList(),
): VM {
return hiltViewModel<VM>().apply {
val context = LocalContext.current
if (context is BaseComposeActivity) {
uiEventFlow.filterIsInstance<CommonUiEvent>()
.collectIn(context) {
context.handleCommonEvent(it)
}
}
return pageViewModel<VM>().apply {
if (initialIntent.isNotEmpty()) {
LaunchedEffect(key1 = initialized) {
if (!initialized) {

View File

@ -367,55 +367,53 @@ fun ForumPage(
val scaffoldState = rememberScaffoldState()
val snackbarHostState = scaffoldState.snackbarHostState
LaunchedEffect(null) {
onEvent<ForumUiEvent.SignIn.Success>(viewModel) {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_sign_success,
"${it.signBonusPoint}",
"${it.userSignRank}"
)
viewModel.onEvent<ForumUiEvent.SignIn.Success> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_sign_success,
"${it.signBonusPoint}",
"${it.userSignRank}"
)
}
onEvent<ForumUiEvent.SignIn.Failure>(viewModel) {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_sign_failed,
it.errorMsg
)
)
}
viewModel.onEvent<ForumUiEvent.SignIn.Failure> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_sign_failed,
it.errorMsg
)
}
onEvent<ForumUiEvent.Like.Success>(viewModel) {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_like_success,
it.memberSum,
)
)
}
viewModel.onEvent<ForumUiEvent.Like.Success> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_like_success,
it.memberSum,
)
}
onEvent<ForumUiEvent.Like.Failure>(viewModel) {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_like_failed,
it.errorMsg
)
)
}
viewModel.onEvent<ForumUiEvent.Like.Failure> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_like_failed,
it.errorMsg
)
}
onEvent<ForumUiEvent.Unlike.Success>(viewModel) {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_unlike_success
)
)
}
viewModel.onEvent<ForumUiEvent.Unlike.Success> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_unlike_success
)
}
onEvent<ForumUiEvent.Unlike.Failure>(viewModel) {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_unlike_failed,
it.errorMsg
)
)
}
viewModel.onEvent<ForumUiEvent.Unlike.Failure> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_unlike_failed,
it.errorMsg
)
}
)
}
val isLoading by viewModel.uiState.collectPartialAsState(

View File

@ -132,26 +132,24 @@ fun ForumThreadListPage(
}
}
}
LaunchedEffect(null) {
onEvent<ForumThreadListUiEvent.AgreeFail>(viewModel) {
val snackbarResult = snackbarHostState.showSnackbar(
message = context.getString(
R.string.snackbar_agree_fail,
it.errorCode,
it.errorMsg
),
actionLabel = context.getString(R.string.button_retry)
)
viewModel.onEvent<ForumThreadListUiEvent.AgreeFail> {
val snackbarResult = snackbarHostState.showSnackbar(
message = context.getString(
R.string.snackbar_agree_fail,
it.errorCode,
it.errorMsg
),
actionLabel = context.getString(R.string.button_retry)
)
if (snackbarResult == SnackbarResult.ActionPerformed) {
viewModel.send(
ForumThreadListUiIntent.Agree(
it.threadId,
it.postId,
it.hasAgree
)
if (snackbarResult == SnackbarResult.ActionPerformed) {
viewModel.send(
ForumThreadListUiIntent.Agree(
it.threadId,
it.postId,
it.hasAgree
)
}
)
}
}
val isRefreshing by viewModel.uiState.collectPartialAsState(

View File

@ -91,18 +91,16 @@ fun HistoryListPage(
val context = LocalContext.current
val navigator = LocalNavigator.current
val snackbarHostState = LocalSnackbarHostState.current
LaunchedEffect(null) {
onEvent<HistoryListUiEvent.Delete.Failure>(viewModel) {
snackbarHostState.showSnackbar(
context.getString(
R.string.delete_history_failure,
it.errorMsg
)
viewModel.onEvent<HistoryListUiEvent.Delete.Failure> {
snackbarHostState.showSnackbar(
context.getString(
R.string.delete_history_failure,
it.errorMsg
)
}
onEvent<HistoryListUiEvent.Delete.Success>(viewModel) {
snackbarHostState.showSnackbar(context.getString(R.string.delete_history_success))
}
)
}
viewModel.onEvent<HistoryListUiEvent.Delete.Success> {
snackbarHostState.showSnackbar(context.getString(R.string.delete_history_success))
}
Box(
modifier = Modifier

View File

@ -24,7 +24,6 @@ import androidx.compose.material.icons.outlined.AccountCircle
import androidx.compose.material.icons.outlined.Block
import androidx.compose.material.icons.outlined.CheckCircle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -185,15 +184,13 @@ fun BlockSettingsPage(
}
) { paddingValues ->
val snackbarHostState = LocalSnackbarHostState.current
LaunchedEffect(null) {
onEvent<BlockSettingsUiEvent.Success>(viewModel) {
snackbarHostState.showSnackbar(
when (it) {
is BlockSettingsUiEvent.Success.Add -> context.getString(R.string.toast_add_success)
is BlockSettingsUiEvent.Success.Delete -> context.getString(R.string.toast_delete_success)
}
)
}
viewModel.onEvent<BlockSettingsUiEvent.Success> {
snackbarHostState.showSnackbar(
when (it) {
is BlockSettingsUiEvent.Success.Add -> context.getString(R.string.toast_add_success)
is BlockSettingsUiEvent.Success.Delete -> context.getString(R.string.toast_delete_success)
}
)
}
HorizontalPager(
count = 2,

View File

@ -20,7 +20,6 @@ import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -107,18 +106,16 @@ fun ThreadStorePage(
val pullRefreshState = rememberPullRefreshState(
refreshing = isRefreshing,
onRefresh = { viewModel.send(ThreadStoreUiIntent.Refresh) })
LaunchedEffect(null) {
onEvent<ThreadStoreUiEvent.Delete.Failure>(viewModel) {
scaffoldState.snackbarHostState.showSnackbar(
context.getString(
R.string.delete_store_failure,
it.errorMsg
)
viewModel.onEvent<ThreadStoreUiEvent.Delete.Failure> {
scaffoldState.snackbarHostState.showSnackbar(
context.getString(
R.string.delete_store_failure,
it.errorMsg
)
}
onEvent<ThreadStoreUiEvent.Delete.Success>(viewModel) {
scaffoldState.snackbarHostState.showSnackbar(context.getString(R.string.delete_store_success))
}
)
}
viewModel.onEvent<ThreadStoreUiEvent.Delete.Success> {
scaffoldState.snackbarHostState.showSnackbar(context.getString(R.string.delete_store_success))
}
MyScaffold(
backgroundColor = Color.Transparent,