fix: 事件流被重复 collect
This commit is contained in:
parent
055e637681
commit
4673044663
|
|
@ -1,9 +1,12 @@
|
||||||
package com.huanchengfly.tieba.post.arch
|
package com.huanchengfly.tieba.post.arch
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
|
@ -11,11 +14,11 @@ import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.flowWithLifecycle
|
import androidx.lifecycle.flowWithLifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
import kotlinx.coroutines.flow.cancellable
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.filterIsInstance
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
|
@ -53,19 +56,27 @@ fun <T : UiState, A> Flow<T>.collectPartialAsState(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <reified Event : UiEvent> CoroutineScope.onEvent(
|
@Composable
|
||||||
viewModel: BaseViewModel<*, *, *, *>,
|
inline fun <reified Event : UiEvent> BaseViewModel<*, *, *, *>.onEvent(
|
||||||
noinline listener: suspend (Event) -> Unit
|
noinline listener: suspend (Event) -> Unit
|
||||||
) {
|
) {
|
||||||
launch {
|
val coroutineScope = rememberCoroutineScope()
|
||||||
viewModel.uiEventFlow
|
DisposableEffect(key1 = listener, key2 = this) {
|
||||||
|
with(coroutineScope) {
|
||||||
|
val job = launch {
|
||||||
|
uiEventFlow
|
||||||
.filterIsInstance<Event>()
|
.filterIsInstance<Event>()
|
||||||
|
.cancellable()
|
||||||
.collect {
|
.collect {
|
||||||
launch {
|
launch {
|
||||||
listener(it)
|
listener(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDispose { job.cancel() }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -73,10 +84,24 @@ inline fun <reified VM : BaseViewModel<*, *, *, *>> pageViewModel(): VM {
|
||||||
return hiltViewModel<VM>().apply {
|
return hiltViewModel<VM>().apply {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
if (context is BaseComposeActivity) {
|
if (context is BaseComposeActivity) {
|
||||||
uiEventFlow.filterIsInstance<CommonUiEvent>()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
DisposableEffect(key1 = this) {
|
||||||
|
with(coroutineScope) {
|
||||||
|
val job =
|
||||||
|
uiEventFlow
|
||||||
|
.filterIsInstance<CommonUiEvent>()
|
||||||
|
.cancellable()
|
||||||
.collectIn(context) {
|
.collectIn(context) {
|
||||||
context.handleCommonEvent(it)
|
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(
|
inline fun <INTENT : UiIntent, reified VM : BaseViewModel<INTENT, *, *, *>> pageViewModel(
|
||||||
initialIntent: List<INTENT> = emptyList(),
|
initialIntent: List<INTENT> = emptyList(),
|
||||||
): VM {
|
): VM {
|
||||||
return hiltViewModel<VM>().apply {
|
return pageViewModel<VM>().apply {
|
||||||
val context = LocalContext.current
|
|
||||||
if (context is BaseComposeActivity) {
|
|
||||||
uiEventFlow.filterIsInstance<CommonUiEvent>()
|
|
||||||
.collectIn(context) {
|
|
||||||
context.handleCommonEvent(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (initialIntent.isNotEmpty()) {
|
if (initialIntent.isNotEmpty()) {
|
||||||
LaunchedEffect(key1 = initialized) {
|
LaunchedEffect(key1 = initialized) {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
|
|
|
||||||
|
|
@ -367,8 +367,7 @@ fun ForumPage(
|
||||||
|
|
||||||
val scaffoldState = rememberScaffoldState()
|
val scaffoldState = rememberScaffoldState()
|
||||||
val snackbarHostState = scaffoldState.snackbarHostState
|
val snackbarHostState = scaffoldState.snackbarHostState
|
||||||
LaunchedEffect(null) {
|
viewModel.onEvent<ForumUiEvent.SignIn.Success> {
|
||||||
onEvent<ForumUiEvent.SignIn.Success>(viewModel) {
|
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
message = context.getString(
|
message = context.getString(
|
||||||
R.string.toast_sign_success,
|
R.string.toast_sign_success,
|
||||||
|
|
@ -377,7 +376,7 @@ fun ForumPage(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onEvent<ForumUiEvent.SignIn.Failure>(viewModel) {
|
viewModel.onEvent<ForumUiEvent.SignIn.Failure> {
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
message = context.getString(
|
message = context.getString(
|
||||||
R.string.toast_sign_failed,
|
R.string.toast_sign_failed,
|
||||||
|
|
@ -385,7 +384,7 @@ fun ForumPage(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onEvent<ForumUiEvent.Like.Success>(viewModel) {
|
viewModel.onEvent<ForumUiEvent.Like.Success> {
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
message = context.getString(
|
message = context.getString(
|
||||||
R.string.toast_like_success,
|
R.string.toast_like_success,
|
||||||
|
|
@ -393,7 +392,7 @@ fun ForumPage(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onEvent<ForumUiEvent.Like.Failure>(viewModel) {
|
viewModel.onEvent<ForumUiEvent.Like.Failure> {
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
message = context.getString(
|
message = context.getString(
|
||||||
R.string.toast_like_failed,
|
R.string.toast_like_failed,
|
||||||
|
|
@ -401,14 +400,14 @@ fun ForumPage(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onEvent<ForumUiEvent.Unlike.Success>(viewModel) {
|
viewModel.onEvent<ForumUiEvent.Unlike.Success> {
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
message = context.getString(
|
message = context.getString(
|
||||||
R.string.toast_unlike_success
|
R.string.toast_unlike_success
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onEvent<ForumUiEvent.Unlike.Failure>(viewModel) {
|
viewModel.onEvent<ForumUiEvent.Unlike.Failure> {
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
message = context.getString(
|
message = context.getString(
|
||||||
R.string.toast_unlike_failed,
|
R.string.toast_unlike_failed,
|
||||||
|
|
@ -416,7 +415,6 @@ fun ForumPage(
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val isLoading by viewModel.uiState.collectPartialAsState(
|
val isLoading by viewModel.uiState.collectPartialAsState(
|
||||||
prop1 = ForumUiState::isLoading,
|
prop1 = ForumUiState::isLoading,
|
||||||
|
|
|
||||||
|
|
@ -132,8 +132,7 @@ fun ForumThreadListPage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LaunchedEffect(null) {
|
viewModel.onEvent<ForumThreadListUiEvent.AgreeFail> {
|
||||||
onEvent<ForumThreadListUiEvent.AgreeFail>(viewModel) {
|
|
||||||
val snackbarResult = snackbarHostState.showSnackbar(
|
val snackbarResult = snackbarHostState.showSnackbar(
|
||||||
message = context.getString(
|
message = context.getString(
|
||||||
R.string.snackbar_agree_fail,
|
R.string.snackbar_agree_fail,
|
||||||
|
|
@ -153,7 +152,6 @@ fun ForumThreadListPage(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
val isRefreshing by viewModel.uiState.collectPartialAsState(
|
||||||
prop1 = ForumThreadListUiState::isRefreshing,
|
prop1 = ForumThreadListUiState::isRefreshing,
|
||||||
initial = false
|
initial = false
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,7 @@ fun HistoryListPage(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val navigator = LocalNavigator.current
|
val navigator = LocalNavigator.current
|
||||||
val snackbarHostState = LocalSnackbarHostState.current
|
val snackbarHostState = LocalSnackbarHostState.current
|
||||||
LaunchedEffect(null) {
|
viewModel.onEvent<HistoryListUiEvent.Delete.Failure> {
|
||||||
onEvent<HistoryListUiEvent.Delete.Failure>(viewModel) {
|
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.delete_history_failure,
|
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))
|
snackbarHostState.showSnackbar(context.getString(R.string.delete_history_success))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import androidx.compose.material.icons.outlined.AccountCircle
|
||||||
import androidx.compose.material.icons.outlined.Block
|
import androidx.compose.material.icons.outlined.Block
|
||||||
import androidx.compose.material.icons.outlined.CheckCircle
|
import androidx.compose.material.icons.outlined.CheckCircle
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -185,8 +184,7 @@ fun BlockSettingsPage(
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
val snackbarHostState = LocalSnackbarHostState.current
|
val snackbarHostState = LocalSnackbarHostState.current
|
||||||
LaunchedEffect(null) {
|
viewModel.onEvent<BlockSettingsUiEvent.Success> {
|
||||||
onEvent<BlockSettingsUiEvent.Success>(viewModel) {
|
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState.showSnackbar(
|
||||||
when (it) {
|
when (it) {
|
||||||
is BlockSettingsUiEvent.Success.Add -> context.getString(R.string.toast_add_success)
|
is BlockSettingsUiEvent.Success.Add -> context.getString(R.string.toast_add_success)
|
||||||
|
|
@ -194,7 +192,6 @@ fun BlockSettingsPage(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
count = 2,
|
count = 2,
|
||||||
state = pagerState,
|
state = pagerState,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
import androidx.compose.material.rememberScaffoldState
|
import androidx.compose.material.rememberScaffoldState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
|
@ -107,8 +106,7 @@ fun ThreadStorePage(
|
||||||
val pullRefreshState = rememberPullRefreshState(
|
val pullRefreshState = rememberPullRefreshState(
|
||||||
refreshing = isRefreshing,
|
refreshing = isRefreshing,
|
||||||
onRefresh = { viewModel.send(ThreadStoreUiIntent.Refresh) })
|
onRefresh = { viewModel.send(ThreadStoreUiIntent.Refresh) })
|
||||||
LaunchedEffect(null) {
|
viewModel.onEvent<ThreadStoreUiEvent.Delete.Failure> {
|
||||||
onEvent<ThreadStoreUiEvent.Delete.Failure>(viewModel) {
|
|
||||||
scaffoldState.snackbarHostState.showSnackbar(
|
scaffoldState.snackbarHostState.showSnackbar(
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.delete_store_failure,
|
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))
|
scaffoldState.snackbarHostState.showSnackbar(context.getString(R.string.delete_store_success))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
MyScaffold(
|
MyScaffold(
|
||||||
backgroundColor = Color.Transparent,
|
backgroundColor = Color.Transparent,
|
||||||
scaffoldState = scaffoldState,
|
scaffoldState = scaffoldState,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue