diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/ReviewNav.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/ReviewNav.kt index 159f18319..2dd825fb1 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/ReviewNav.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/ReviewNav.kt @@ -1,6 +1,9 @@ package com.eatssu.android.presentation.cafeteria.review import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -17,6 +20,8 @@ object ReviewNav { const val Modify = "modify" } +private const val KEY_REVIEW_LIST_REFRESH_NONCE = "review_list_refresh_nonce" + @Composable fun ReviewNav( navHostController: NavHostController = rememberNavController(), @@ -31,11 +36,16 @@ fun ReviewNav( startDestination = ReviewNav.List ) { // 리뷰 보기 - composable(ReviewNav.List) { + composable(ReviewNav.List) { backStackEntry -> + val refreshNonce by backStackEntry.savedStateHandle + .getStateFlow(KEY_REVIEW_LIST_REFRESH_NONCE, 0L) + .collectAsState() + ReviewListScreen( menuName = menuName, menuType = menuType, id = id, + refreshNonce = refreshNonce, onBack = { onExit() }, onModifyClick = { review -> // 선택된 리뷰 데이터를 Modify 화면으로 전달 @@ -54,6 +64,12 @@ fun ReviewNav( } } ) + + LaunchedEffect(refreshNonce) { + if (refreshNonce != 0L) { + backStackEntry.savedStateHandle[KEY_REVIEW_LIST_REFRESH_NONCE] = 0L + } + } } // 리뷰 작성 @@ -62,7 +78,12 @@ fun ReviewNav( menuType = menuType, menuName = menuName, id = id, - onBack = { navHostController.popBackStack() }, + onBack = { + navHostController.previousBackStackEntry + ?.savedStateHandle + ?.set(KEY_REVIEW_LIST_REFRESH_NONCE, System.currentTimeMillis()) + navHostController.popBackStack() + }, ) } @@ -84,4 +105,4 @@ fun ReviewNav( ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt index 58122ca7e..39e4803ee 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListScreen.kt @@ -77,6 +77,7 @@ fun ReviewListScreen( menuType: MenuType, menuName: String, id: Long, + refreshNonce: Long = 0L, onBack: () -> Unit = {}, onWriteButtonClick: () -> Unit, onModifyClick: (Review) -> Unit, @@ -94,6 +95,13 @@ fun ReviewListScreen( val reviewPagingItems = viewModel.reviewPagingData.collectAsLazyPagingItems() val uiEvent by viewModel.uiEvent.collectAsStateWithLifecycle(initialValue = null) + LaunchedEffect(refreshNonce) { + if (refreshNonce == 0L) return@LaunchedEffect + // 리뷰 작성 화면에서 돌아온 경우, 리스트/정보를 강제로 갱신한다. + viewModel.getReview(menuType, id) + reviewPagingItems.refresh() + } + LaunchedEffect(uiEvent) { when (val event = uiEvent) { is UiEvent.ShowToast -> context.showToast(event) @@ -730,4 +738,4 @@ fun ReviewListErrorPreview() { reviewPagingItems = rememberPreviewPagingItems(pagingData), ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt index bf4781f57..7b2287ba5 100644 --- a/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt +++ b/app/src/main/java/com/eatssu/android/presentation/cafeteria/review/list/ReviewListViewModel.kt @@ -17,13 +17,13 @@ import com.eatssu.common.enums.MenuType import com.eatssu.common.enums.ToastType import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.launch import javax.inject.Inject @@ -41,21 +41,24 @@ class ReviewListViewModel @Inject constructor( private val _uiEvent: MutableSharedFlow = MutableSharedFlow() val uiEvent = _uiEvent.asSharedFlow() - private val _loadParams = MutableStateFlow?>(null) + private val _loadParams = MutableSharedFlow>( + replay = 1, + extraBufferCapacity = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST, + ) @OptIn(ExperimentalCoroutinesApi::class) val reviewPagingData: Flow> = _loadParams - .filterNotNull() .flatMapLatest { (menuType, itemId) -> getReviewListPagedUseCase(menuType, itemId) } .cachedIn(viewModelScope) fun getReview(menuType: MenuType, itemId: Long) { - // 파라미터 업데이트 시 페이징 흐름 트리거 - // StateFlow는 동일한 값으로 설정되어도 업데이트되지 않으므로 별도의 체크 불필요 - _loadParams.value = menuType to itemId - + // 동일 파라미터로 다시 진입(작성/수정 후 popBackStack)해도 + // 항상 페이징 소스를 새로 만들 수 있도록 SharedFlow로 트리거한다. + _loadParams.tryEmit(menuType to itemId) + viewModelScope.launch { loadReviewInfo(menuType, itemId) } @@ -94,10 +97,8 @@ class ReviewListViewModel @Inject constructor( _uiEvent.emit(ReviewListEvent.ReviewDeleted) // 정보 갱신 - val currentParams = _loadParams.value - if (currentParams != null) { - loadReviewInfo(currentParams.first, currentParams.second) - } + val currentParams = _loadParams.replayCache.lastOrNull() + if (currentParams != null) loadReviewInfo(currentParams.first, currentParams.second) } } }