Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e7b8949
feat: 공통 유저 세션 관리 UseCase
soochan8 Oct 21, 2025
ab6d3d3
feat: 공통 세션 State 추가
soochan8 Oct 21, 2025
28a217c
feat: ViewModel 세션 확인 로직 추가
soochan8 Oct 21, 2025
2d82c43
feat: Cart 화면 진입 시 세션 확인 로직 추가
soochan8 Oct 21, 2025
869c4b4
chore: gradle kapt plugins 수정
soochan8 Oct 21, 2025
433dca8
feat: getUserIdUseCase 추가
soochan8 Oct 21, 2025
2bfd324
feat: 사용자별 장바구니 DataStore관리 기능 추가
soochan8 Oct 21, 2025
f298b8d
refactor: CartDataStore 접근을 User기반으로 개선
soochan8 Oct 21, 2025
1a0ab4b
feat: 로그인 성공 시, 장바구니 목록 불러오도록 수정
soochan8 Oct 21, 2025
3a0a19e
feat: 로그인 성공 시, 장바구니 목록 불러오도록 수정
soochan8 Oct 21, 2025
cc695cf
feat: 로그아웃 시, 사용자 ID도 함께 제거
soochan8 Oct 21, 2025
28c9f72
feat: Cart 리다이렉트 시 popUpTo 제거 및 복귀 흐름 개선
soochan8 Oct 22, 2025
83bab2d
feat: CartDataStoreManager storeCache를 ConcurrentHashMap으로 변경
soochan8 Oct 22, 2025
4243853
feat: 로그아웃 시 CartDataStoreManager clear
soochan8 Oct 22, 2025
90a818b
feat: flowUserIdUseCase를 통해 userId 변경 시, DataStore 전환되도록 수정
soochan8 Oct 22, 2025
a9f6f29
feat: dataStoreScope Module 설정
soochan8 Oct 22, 2025
95c9189
feat: cartDataStoreManager 내부에서 외부 코루틴 스코프 사용
soochan8 Oct 22, 2025
0b1eb90
feat: 로그아웃 시, CartDataStoreManager 리소스 정리
soochan8 Oct 22, 2025
a3f0444
feat: 미사용 코루틴 스코프 Module 제거
soochan8 Oct 22, 2025
056626d
feat: UseCase 미사용 context제거
soochan8 Oct 22, 2025
bfcc7ab
change: 경로 변경, 기존 cart -> core:database
soochan8 Oct 27, 2025
7468c77
change: 경로 변경 , core:auth
soochan8 Oct 27, 2025
3677e47
chore: build dependency 추가
soochan8 Oct 27, 2025
5239f72
feat: 기본 MyPage 구성
soochan8 Oct 27, 2025
e0fda36
feat: 로그아웃 시, CartDataStoreManager.clearAll 제거
soochan8 Oct 27, 2025
e84062c
feat: 마이페이지 NavGraph 설정
soochan8 Oct 27, 2025
39c3010
change: import 경로 수정
soochan8 Oct 27, 2025
9280e92
feat: 고유 cartStore 생성
soochan8 Oct 27, 2025
613b028
feat: 로그인 NavGraph 수정
soochan8 Oct 27, 2025
bf59022
Merge pull request #64 from f-lab-edu/feature/feature#52-cart-handle-…
soochan8 Oct 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
alias(libs.plugins.kotlin.android)
alias(libs.plugins.hilt)
alias(libs.plugins.google.services)
kotlin("kapt")
alias(libs.plugins.kotlin.kapt)
}

android {
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.kapt) apply false
// alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.hilt) apply false
alias(libs.plugins.jetbrains.kotlin.jvm) apply false
alias(libs.plugins.kotlin.kapt) apply false

}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.chan.android.state

interface SessionState {
val isSessionCheckCompleted: Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.chan.auth.domain

import kotlinx.coroutines.flow.firstOrNull
import javax.inject.Inject

class CheckSessionUseCase @Inject constructor(
private val authRepository: AuthRepository
) {
suspend operator fun invoke() : Boolean {
return authRepository.getSessionFlow().firstOrNull() != null
}
}
11 changes: 9 additions & 2 deletions feature/cart/src/main/java/com/chan/cart/CartContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.chan.android.LoadingState
import com.chan.android.ViewEffect
import com.chan.android.ViewEvent
import com.chan.android.ViewState
import com.chan.android.state.SessionState
import com.chan.cart.model.CartInProductsModel
import com.chan.cart.model.CartInTobBarModel
import com.chan.cart.model.PopupProductInfoModel
Expand All @@ -14,6 +15,7 @@ class CartContract {
data class SelectedTab(val index: Int) : Event()
data class LoadPopupProductInfo(val productId: String) : Event()
object LoadCartProducts : Event()
object CheckUserSession : Event()
data class AddToProduct(val productId: String) : Event()
data class UpdateProductSelected(val productId: String, val isSelected: Boolean) : Event()
data class UpdateProductQuantity(val productId: String, val isAdd: Boolean) : Event()
Expand All @@ -29,12 +31,17 @@ class CartContract {
val totalProductsCount : Int = 0,
val totalPrice : Int = 0,
val allSelected : Boolean = false,
val loadingState: LoadingState = LoadingState.Idle
) : ViewState
val loadingState: LoadingState = LoadingState.Idle,
override val isSessionCheckCompleted: Boolean = false
) : ViewState, SessionState

sealed class Effect : ViewEffect {
data class ShowError(val errorMsg: String) : Effect()
data class ShowToast(@StringRes val message: Int) : Effect()
object DismissCartPopup : Effect()

sealed class Navigation : Effect() {
object ToLogin : Navigation()
}
}
}
16 changes: 16 additions & 0 deletions feature/cart/src/main/java/com/chan/cart/CartViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.chan.cart
import androidx.lifecycle.viewModelScope
import com.chan.android.BaseViewModel
import com.chan.android.LoadingState
import com.chan.auth.domain.CheckSessionUseCase
import com.chan.cart.CartContract.Effect.Navigation.ToLogin
import com.chan.cart.domain.usecase.CartUseCases
import com.chan.cart.model.CartInTobBarModel
import com.chan.cart.ui.mapper.toDataStoreCartInProductsModel
Expand All @@ -14,6 +16,7 @@ import javax.inject.Inject

@HiltViewModel
class CartViewModel @Inject constructor(
private val checkSessionUseCase: CheckSessionUseCase,
private val cartUseCases: CartUseCases
) : BaseViewModel<CartContract.Event, CartContract.State, CartContract.Effect>() {

Expand Down Expand Up @@ -42,6 +45,19 @@ class CartViewModel @Inject constructor(

CartContract.Event.OnAllSelected -> updateAllSelected()
is CartContract.Event.DeleteProduct -> deleteProduct(event.productId)
CartContract.Event.CheckUserSession -> checkSessionStatus()
}
}

private fun checkSessionStatus() {
viewModelScope.launch {
val currentSession = checkSessionUseCase.invoke()

if (currentSession) {
setState { copy(isSessionCheckCompleted = true) }
} else {
setEffect { ToLogin }
}
}
}

Expand Down
72 changes: 54 additions & 18 deletions feature/cart/src/main/java/com/chan/cart/naivgation/CartNavGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import com.chan.cart.CartViewModel
import com.chan.cart.ui.CartScreen
import com.chan.cart.ui.popup.CartPopupScreen
import com.chan.navigation.NavGraphProvider
import com.chan.navigation.Routes
import com.chan.navigation.createLoginRoute
import kotlinx.coroutines.flow.filterIsInstance
import javax.inject.Inject

class CartNavGraph @Inject constructor() : NavGraphProvider {
Expand All @@ -41,16 +44,34 @@ class CartNavGraph @Inject constructor() : NavGraphProvider {
fun CartRoute(navController: NavHostController) {
val viewModel: CartViewModel = hiltViewModel()
val state by viewModel.viewState.collectAsState()
val context = LocalContext.current

LaunchedEffect(Unit) {
viewModel.effect
.filterIsInstance<CartContract.Effect.Navigation>()
.collect { navigate ->
when (navigate) {
CartContract.Effect.Navigation.ToLogin -> {
navController.navigate(createLoginRoute(Routes.CART.route)) {
popUpTo(Routes.CART.route) {
inclusive = true
}
}
}
}
}
}

LaunchedEffect(Unit) {
viewModel.setEvent(CartContract.Event.LoadCartProducts)
viewModel.setEvent(CartContract.Event.CheckUserSession)
}

CartScreen(
state = state,
onEvent = viewModel::setEvent
)
if (state.isSessionCheckCompleted) {
CartScreen(
state = state,
onEvent = viewModel::setEvent
)
}
}


Expand All @@ -60,24 +81,39 @@ fun CartPopupRoute(navController: NavHostController, productId: String) {
val state by viewModel.viewState.collectAsState()
val context = LocalContext.current

LaunchedEffect(Unit) {
viewModel.effect
.collect { effect ->
when (effect) {
is CartContract.Effect.ShowToast -> {
Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show()
}

is CartContract.Effect.ShowError -> {
Toast.makeText(context, effect.errorMsg, Toast.LENGTH_SHORT).show()
}

CartContract.Effect.DismissCartPopup -> {
navController.popBackStack()
}

CartContract.Effect.Navigation.ToLogin -> {
navController.navigate(createLoginRoute(Routes.HOME.route)) {
popUpTo(Routes.CART.route) {
inclusive = true
}
}
}
}
}
}

LaunchedEffect(key1 = productId) {
viewModel.handleEvent(CartContract.Event.LoadPopupProductInfo(productId))
}

LaunchedEffect(Unit) {
viewModel.effect.collect { effect ->
when (effect) {
is CartContract.Effect.ShowToast -> {
Toast.makeText(context, effect.message, Toast.LENGTH_SHORT).show()
}

is CartContract.Effect.ShowError -> {
Toast.makeText(context, effect.errorMsg, Toast.LENGTH_SHORT).show()
}

CartContract.Effect.DismissCartPopup -> navController.popBackStack()
}
}
viewModel.setEvent(CartContract.Event.CheckUserSession)
}

CartPopupScreen(
Expand Down