diff --git a/app/src/main/java/com/huanchengfly/tieba/post/arch/Extensions.kt b/app/src/main/java/com/huanchengfly/tieba/post/arch/Extensions.kt index fe9f0899..f19fdef1 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/arch/Extensions.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/arch/Extensions.kt @@ -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 Flow.collectPartialAsState( } } -inline fun CoroutineScope.onEvent( - viewModel: BaseViewModel<*, *, *, *>, +@Composable +inline fun BaseViewModel<*, *, *, *>.onEvent( noinline listener: suspend (Event) -> Unit ) { - launch { - viewModel.uiEventFlow - .filterIsInstance() - .collect { - launch { - listener(it) - } + val coroutineScope = rememberCoroutineScope() + DisposableEffect(key1 = listener, key2 = this) { + with(coroutineScope) { + val job = launch { + uiEventFlow + .filterIsInstance() + .cancellable() + .collect { + launch { + listener(it) + } + } } + + onDispose { job.cancel() } + } } } @@ -73,10 +84,24 @@ inline fun > pageViewModel(): VM { return hiltViewModel().apply { val context = LocalContext.current if (context is BaseComposeActivity) { - uiEventFlow.filterIsInstance() - .collectIn(context) { - context.handleCommonEvent(it) + val coroutineScope = rememberCoroutineScope() + + DisposableEffect(key1 = this) { + with(coroutineScope) { + val job = + uiEventFlow + .filterIsInstance() + .cancellable() + .collectIn(context) { + context.handleCommonEvent(it) + } + + onDispose { + Log.i("pageViewModel", "onDispose") + job.cancel() + } } + } } } } @@ -85,14 +110,7 @@ inline fun > pageViewModel(): VM { inline fun > pageViewModel( initialIntent: List = emptyList(), ): VM { - return hiltViewModel().apply { - val context = LocalContext.current - if (context is BaseComposeActivity) { - uiEventFlow.filterIsInstance() - .collectIn(context) { - context.handleCommonEvent(it) - } - } + return pageViewModel().apply { if (initialIntent.isNotEmpty()) { LaunchedEffect(key1 = initialized) { if (!initialized) { diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/ForumPage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/ForumPage.kt index ebcdb0ab..285507d6 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/ForumPage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/ForumPage.kt @@ -367,55 +367,53 @@ fun ForumPage( val scaffoldState = rememberScaffoldState() val snackbarHostState = scaffoldState.snackbarHostState - LaunchedEffect(null) { - onEvent(viewModel) { - snackbarHostState.showSnackbar( - message = context.getString( - R.string.toast_sign_success, - "${it.signBonusPoint}", - "${it.userSignRank}" - ) + viewModel.onEvent { + snackbarHostState.showSnackbar( + message = context.getString( + R.string.toast_sign_success, + "${it.signBonusPoint}", + "${it.userSignRank}" ) - } - onEvent(viewModel) { - snackbarHostState.showSnackbar( - message = context.getString( - R.string.toast_sign_failed, - it.errorMsg - ) + ) + } + viewModel.onEvent { + snackbarHostState.showSnackbar( + message = context.getString( + R.string.toast_sign_failed, + it.errorMsg ) - } - onEvent(viewModel) { - snackbarHostState.showSnackbar( - message = context.getString( - R.string.toast_like_success, - it.memberSum, - ) + ) + } + viewModel.onEvent { + snackbarHostState.showSnackbar( + message = context.getString( + R.string.toast_like_success, + it.memberSum, ) - } - onEvent(viewModel) { - snackbarHostState.showSnackbar( - message = context.getString( - R.string.toast_like_failed, - it.errorMsg - ) + ) + } + viewModel.onEvent { + snackbarHostState.showSnackbar( + message = context.getString( + R.string.toast_like_failed, + it.errorMsg ) - } - onEvent(viewModel) { - snackbarHostState.showSnackbar( - message = context.getString( - R.string.toast_unlike_success - ) + ) + } + viewModel.onEvent { + snackbarHostState.showSnackbar( + message = context.getString( + R.string.toast_unlike_success ) - } - onEvent(viewModel) { - snackbarHostState.showSnackbar( - message = context.getString( - R.string.toast_unlike_failed, - it.errorMsg - ) + ) + } + viewModel.onEvent { + snackbarHostState.showSnackbar( + message = context.getString( + R.string.toast_unlike_failed, + it.errorMsg ) - } + ) } val isLoading by viewModel.uiState.collectPartialAsState( diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/threadlist/ForumThreadListPage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/threadlist/ForumThreadListPage.kt index 651bceb2..35579c39 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/threadlist/ForumThreadListPage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/forum/threadlist/ForumThreadListPage.kt @@ -132,26 +132,24 @@ fun ForumThreadListPage( } } } - LaunchedEffect(null) { - onEvent(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 { + 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( diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/history/list/HistoryListPage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/history/list/HistoryListPage.kt index d390dc50..3a64b476 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/history/list/HistoryListPage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/history/list/HistoryListPage.kt @@ -91,18 +91,16 @@ fun HistoryListPage( val context = LocalContext.current val navigator = LocalNavigator.current val snackbarHostState = LocalSnackbarHostState.current - LaunchedEffect(null) { - onEvent(viewModel) { - snackbarHostState.showSnackbar( - context.getString( - R.string.delete_history_failure, - it.errorMsg - ) + viewModel.onEvent { + snackbarHostState.showSnackbar( + context.getString( + R.string.delete_history_failure, + it.errorMsg ) - } - onEvent(viewModel) { - snackbarHostState.showSnackbar(context.getString(R.string.delete_history_success)) - } + ) + } + viewModel.onEvent { + snackbarHostState.showSnackbar(context.getString(R.string.delete_history_success)) } Box( modifier = Modifier diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/settings/block/BlockSettingsPage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/settings/block/BlockSettingsPage.kt index c35b3512..4709fa3a 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/settings/block/BlockSettingsPage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/settings/block/BlockSettingsPage.kt @@ -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(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 { + 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, diff --git a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/threadstore/ThreadStorePage.kt b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/threadstore/ThreadStorePage.kt index cc530356..bb1f740e 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/ui/page/threadstore/ThreadStorePage.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/ui/page/threadstore/ThreadStorePage.kt @@ -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(viewModel) { - scaffoldState.snackbarHostState.showSnackbar( - context.getString( - R.string.delete_store_failure, - it.errorMsg - ) + viewModel.onEvent { + scaffoldState.snackbarHostState.showSnackbar( + context.getString( + R.string.delete_store_failure, + it.errorMsg ) - } - onEvent(viewModel) { - scaffoldState.snackbarHostState.showSnackbar(context.getString(R.string.delete_store_success)) - } + ) + } + viewModel.onEvent { + scaffoldState.snackbarHostState.showSnackbar(context.getString(R.string.delete_store_success)) } MyScaffold( backgroundColor = Color.Transparent,