fix: 事件流被重复 collect
This commit is contained in:
parent
055e637681
commit
4673044663
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue