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,19 +56,27 @@ 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
val coroutineScope = rememberCoroutineScope()
DisposableEffect(key1 = listener, key2 = this) {
with(coroutineScope) {
val job = launch {
uiEventFlow
.filterIsInstance<Event>()
.cancellable()
.collect {
launch {
listener(it)
}
}
}
onDispose { job.cancel() }
}
}
}
@Composable
@ -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>()
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,8 +367,7 @@ fun ForumPage(
val scaffoldState = rememberScaffoldState()
val snackbarHostState = scaffoldState.snackbarHostState
LaunchedEffect(null) {
onEvent<ForumUiEvent.SignIn.Success>(viewModel) {
viewModel.onEvent<ForumUiEvent.SignIn.Success> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_sign_success,
@ -377,7 +376,7 @@ fun ForumPage(
)
)
}
onEvent<ForumUiEvent.SignIn.Failure>(viewModel) {
viewModel.onEvent<ForumUiEvent.SignIn.Failure> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_sign_failed,
@ -385,7 +384,7 @@ fun ForumPage(
)
)
}
onEvent<ForumUiEvent.Like.Success>(viewModel) {
viewModel.onEvent<ForumUiEvent.Like.Success> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_like_success,
@ -393,7 +392,7 @@ fun ForumPage(
)
)
}
onEvent<ForumUiEvent.Like.Failure>(viewModel) {
viewModel.onEvent<ForumUiEvent.Like.Failure> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_like_failed,
@ -401,14 +400,14 @@ fun ForumPage(
)
)
}
onEvent<ForumUiEvent.Unlike.Success>(viewModel) {
viewModel.onEvent<ForumUiEvent.Unlike.Success> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_unlike_success
)
)
}
onEvent<ForumUiEvent.Unlike.Failure>(viewModel) {
viewModel.onEvent<ForumUiEvent.Unlike.Failure> {
snackbarHostState.showSnackbar(
message = context.getString(
R.string.toast_unlike_failed,
@ -416,7 +415,6 @@ fun ForumPage(
)
)
}
}
val isLoading by viewModel.uiState.collectPartialAsState(
prop1 = ForumUiState::isLoading,

View File

@ -132,8 +132,7 @@ fun ForumThreadListPage(
}
}
}
LaunchedEffect(null) {
onEvent<ForumThreadListUiEvent.AgreeFail>(viewModel) {
viewModel.onEvent<ForumThreadListUiEvent.AgreeFail> {
val snackbarResult = snackbarHostState.showSnackbar(
message = context.getString(
R.string.snackbar_agree_fail,
@ -153,7 +152,6 @@ fun ForumThreadListPage(
)
}
}
}
val isRefreshing by viewModel.uiState.collectPartialAsState(
prop1 = ForumThreadListUiState::isRefreshing,
initial = false

View File

@ -91,8 +91,7 @@ fun HistoryListPage(
val context = LocalContext.current
val navigator = LocalNavigator.current
val snackbarHostState = LocalSnackbarHostState.current
LaunchedEffect(null) {
onEvent<HistoryListUiEvent.Delete.Failure>(viewModel) {
viewModel.onEvent<HistoryListUiEvent.Delete.Failure> {
snackbarHostState.showSnackbar(
context.getString(
R.string.delete_history_failure,
@ -100,10 +99,9 @@ fun HistoryListPage(
)
)
}
onEvent<HistoryListUiEvent.Delete.Success>(viewModel) {
viewModel.onEvent<HistoryListUiEvent.Delete.Success> {
snackbarHostState.showSnackbar(context.getString(R.string.delete_history_success))
}
}
Box(
modifier = Modifier
.fillMaxSize()

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,8 +184,7 @@ fun BlockSettingsPage(
}
) { paddingValues ->
val snackbarHostState = LocalSnackbarHostState.current
LaunchedEffect(null) {
onEvent<BlockSettingsUiEvent.Success>(viewModel) {
viewModel.onEvent<BlockSettingsUiEvent.Success> {
snackbarHostState.showSnackbar(
when (it) {
is BlockSettingsUiEvent.Success.Add -> context.getString(R.string.toast_add_success)
@ -194,7 +192,6 @@ fun BlockSettingsPage(
}
)
}
}
HorizontalPager(
count = 2,
state = pagerState,

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,8 +106,7 @@ fun ThreadStorePage(
val pullRefreshState = rememberPullRefreshState(
refreshing = isRefreshing,
onRefresh = { viewModel.send(ThreadStoreUiIntent.Refresh) })
LaunchedEffect(null) {
onEvent<ThreadStoreUiEvent.Delete.Failure>(viewModel) {
viewModel.onEvent<ThreadStoreUiEvent.Delete.Failure> {
scaffoldState.snackbarHostState.showSnackbar(
context.getString(
R.string.delete_store_failure,
@ -116,10 +114,9 @@ fun ThreadStorePage(
)
)
}
onEvent<ThreadStoreUiEvent.Delete.Success>(viewModel) {
viewModel.onEvent<ThreadStoreUiEvent.Delete.Success> {
scaffoldState.snackbarHostState.showSnackbar(context.getString(R.string.delete_store_success))
}
}
MyScaffold(
backgroundColor = Color.Transparent,
scaffoldState = scaffoldState,