From 4f45f82a1b2b1bf2119d522bfd3c865e6fb69128 Mon Sep 17 00:00:00 2001 From: soochan Date: Tue, 18 Nov 2025 16:01:22 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20like.proto=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - product_ids 저장 --- core/database/src/main/proto/like.proto | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 core/database/src/main/proto/like.proto diff --git a/core/database/src/main/proto/like.proto b/core/database/src/main/proto/like.proto new file mode 100644 index 0000000..dcb24ac --- /dev/null +++ b/core/database/src/main/proto/like.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +option java_package = "com.chan.like.proto"; +option java_multiple_files = true; + +message Like { + repeated string product_ids = 1; +} \ No newline at end of file From 3bdb9f6813543a8450235baee61393c65cee6e7a Mon Sep 17 00:00:00 2001 From: soochan Date: Tue, 18 Nov 2025 16:03:06 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat(core-domain):=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=20UseCase=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - GetLikedProductIdsUseCase: 좋아요 누른 상품의 id - GetLikedProductsUseCase: 좋아요 누른 상품 model - ToggleLikeUseCase: 좋아요 설정 및 해제 --- .../domain/usecase/GetLikedProductIdsUseCase.kt | 13 +++++++++++++ .../chan/domain/usecase/GetLikedProductsUseCase.kt | 14 ++++++++++++++ .../com/chan/domain/usecase/ToggleLikeUseCase.kt | 12 ++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 core/domain/src/main/java/com/chan/domain/usecase/GetLikedProductIdsUseCase.kt create mode 100644 core/domain/src/main/java/com/chan/domain/usecase/GetLikedProductsUseCase.kt create mode 100644 core/domain/src/main/java/com/chan/domain/usecase/ToggleLikeUseCase.kt diff --git a/core/domain/src/main/java/com/chan/domain/usecase/GetLikedProductIdsUseCase.kt b/core/domain/src/main/java/com/chan/domain/usecase/GetLikedProductIdsUseCase.kt new file mode 100644 index 0000000..a7aa5cd --- /dev/null +++ b/core/domain/src/main/java/com/chan/domain/usecase/GetLikedProductIdsUseCase.kt @@ -0,0 +1,13 @@ +package com.chan.domain.usecase + +import com.chan.domain.repository.LikeRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class GetLikedProductIdsUseCase @Inject constructor( + private val likeRepository: LikeRepository, +) { + operator fun invoke() : Flow> { + return likeRepository.getLikedProductIds() + } +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/chan/domain/usecase/GetLikedProductsUseCase.kt b/core/domain/src/main/java/com/chan/domain/usecase/GetLikedProductsUseCase.kt new file mode 100644 index 0000000..400a8e5 --- /dev/null +++ b/core/domain/src/main/java/com/chan/domain/usecase/GetLikedProductsUseCase.kt @@ -0,0 +1,14 @@ +package com.chan.domain.usecase + +import com.chan.domain.ProductsVO +import com.chan.domain.repository.LikeRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class GetLikedProductsUseCase @Inject constructor( + private val likeRepository: LikeRepository +) { + operator fun invoke() : Flow> { + return likeRepository.getLikedProducts() + } +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/chan/domain/usecase/ToggleLikeUseCase.kt b/core/domain/src/main/java/com/chan/domain/usecase/ToggleLikeUseCase.kt new file mode 100644 index 0000000..4974e1f --- /dev/null +++ b/core/domain/src/main/java/com/chan/domain/usecase/ToggleLikeUseCase.kt @@ -0,0 +1,12 @@ +package com.chan.domain.usecase + +import com.chan.domain.repository.LikeRepository +import javax.inject.Inject + +class ToggleLikeUseCase @Inject constructor( + private val likeRepository: LikeRepository +) { + suspend operator fun invoke(productId: String) { + likeRepository.toggleLike(productId) + } +} \ No newline at end of file From 1cec922cd56369041fdc62458c31630bd7fdaaee Mon Sep 17 00:00:00 2001 From: soochan Date: Tue, 18 Nov 2025 16:03:54 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20LikeRepository/LikeRepositoryImpl?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - getLikeStore()를 통해 유저별로 좋아요 항목 관리 --- .../database/mappers/EntityToDomainMappers.kt | 20 ++++++ .../database/repository/LikeRepositoryImpl.kt | 67 +++++++++++++++++++ .../chan/domain/repository/LikeRepository.kt | 11 +++ .../java/com/chan/like/mapper/LikeMappers.kt | 22 ++++++ 4 files changed, 120 insertions(+) create mode 100644 core/database/src/main/java/com/chan/database/mappers/EntityToDomainMappers.kt create mode 100644 core/database/src/main/java/com/chan/database/repository/LikeRepositoryImpl.kt create mode 100644 core/domain/src/main/java/com/chan/domain/repository/LikeRepository.kt create mode 100644 feature/like/src/main/java/com/chan/like/mapper/LikeMappers.kt diff --git a/core/database/src/main/java/com/chan/database/mappers/EntityToDomainMappers.kt b/core/database/src/main/java/com/chan/database/mappers/EntityToDomainMappers.kt new file mode 100644 index 0000000..db7c69b --- /dev/null +++ b/core/database/src/main/java/com/chan/database/mappers/EntityToDomainMappers.kt @@ -0,0 +1,20 @@ +package com.chan.database.mappers + +import com.chan.database.entity.CommonProductEntity +import com.chan.domain.ProductsVO + +fun CommonProductEntity.toProductsVO(): ProductsVO { + return ProductsVO( + productId = productId, + productName = productName, + brandName = brandName, + imageUrl = imageUrl, + originalPrice = originalPrice, + discountPercent = discountPercent, + discountPrice = discountPrice, + tags = tags, + reviewRating = reviewRating, + reviewCount = reviewCount, + categoryIds = categoryIds, + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/chan/database/repository/LikeRepositoryImpl.kt b/core/database/src/main/java/com/chan/database/repository/LikeRepositoryImpl.kt new file mode 100644 index 0000000..bf2fa05 --- /dev/null +++ b/core/database/src/main/java/com/chan/database/repository/LikeRepositoryImpl.kt @@ -0,0 +1,67 @@ +package com.chan.database.repository + +import android.content.Context +import androidx.datastore.core.DataStore +import com.chan.auth.domain.usecase.GetCurrentUserIdUseCase +import com.chan.database.dao.ProductsDao +import com.chan.database.datastore.LikeDataStoreManager +import com.chan.database.mappers.toProductsVO +import com.chan.domain.ProductsVO +import com.chan.domain.repository.LikeRepository +import com.chan.like.proto.Like +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.mapLatest +import javax.inject.Inject +import kotlin.collections.toMutableSet +import kotlin.collections.toSet + +class LikeRepositoryImpl @Inject constructor( + @ApplicationContext private val context: Context, + private val getCurrentUserIdUseCase: GetCurrentUserIdUseCase, + private val productsDao: ProductsDao +) : LikeRepository { + + private fun getLikeStore(): DataStore { + val userId = getCurrentUserIdUseCase() ?: "guest" + return LikeDataStoreManager.getDataStore(context, userId) + } + + + override fun getLikedProductIds(): Flow> { + return getLikeStore().data + .map { it.productIdsList.toSet() } + .distinctUntilChanged() + } + + override suspend fun toggleLike(productId: String) { + getLikeStore().updateData { current -> + val currentLikedSet = current.productIdsList.toMutableSet() + + if (currentLikedSet.contains(productId)) + currentLikedSet.remove(productId) + else + currentLikedSet.add(productId) + + current.toBuilder().clearProductIds().addAllProductIds(currentLikedSet).build() + } + } + + override fun getLikedProducts(): Flow> { + return getLikeStore().data.mapLatest { likeProto -> + val likedIds = likeProto.productIdsList.toSet() + if (likedIds.isEmpty()) + return@mapLatest emptyList() + + val allProducts = productsDao.getAllProducts() + + allProducts + .filter { it.productId in likedIds } + .map { it.toProductsVO() } + + } + } + +} \ No newline at end of file diff --git a/core/domain/src/main/java/com/chan/domain/repository/LikeRepository.kt b/core/domain/src/main/java/com/chan/domain/repository/LikeRepository.kt new file mode 100644 index 0000000..11c0c9a --- /dev/null +++ b/core/domain/src/main/java/com/chan/domain/repository/LikeRepository.kt @@ -0,0 +1,11 @@ +package com.chan.domain.repository + +import com.chan.domain.ProductsVO +import kotlinx.coroutines.flow.Flow + + +interface LikeRepository { + fun getLikedProductIds() : Flow> + suspend fun toggleLike(productId: String) + fun getLikedProducts() : Flow> +} \ No newline at end of file diff --git a/feature/like/src/main/java/com/chan/like/mapper/LikeMappers.kt b/feature/like/src/main/java/com/chan/like/mapper/LikeMappers.kt new file mode 100644 index 0000000..cb0244c --- /dev/null +++ b/feature/like/src/main/java/com/chan/like/mapper/LikeMappers.kt @@ -0,0 +1,22 @@ +package com.chan.like.mapper + +import com.chan.android.model.ProductsModel +import com.chan.domain.ProductsVO +import java.text.NumberFormat +import java.util.Locale + +fun ProductsVO.toProductsModel(): ProductsModel { + return ProductsModel( + productId = productId, + productName = productName, + brandName = brandName, + imageUrl = "https://image.oliveyoung.co.kr/cfimages/cf-goods/uploads/images/thumbnails/550/10/0000/0016/A00000016559829ko.jpg?l=ko", + originalPrice = NumberFormat.getNumberInstance(Locale.KOREA).format(originalPrice) + "원", + discountPrice = NumberFormat.getNumberInstance(Locale.KOREA).format(discountPrice) + "원", + discountPercent = "${discountPercent}%", + tags = tags, + reviewRating = reviewRating, + reviewCount = "(${NumberFormat.getNumberInstance(Locale.KOREA).format(reviewCount)})", + categoryIds = categoryIds, + ) +} \ No newline at end of file From 1490c42382728f4a3144ea5751831c31e3b71d33 Mon Sep 17 00:00:00 2001 From: soochan Date: Tue, 18 Nov 2025 16:05:42 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat(core-database):=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=EB=B3=84=20=EC=A2=8B=EC=95=84=EC=9A=94=20DataStore=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datastore/LikeDataStoreManager.kt | 38 +++++++++++++++++++ .../chan/database/datastore/LikeSerializer.kt | 21 ++++++++++ 2 files changed, 59 insertions(+) create mode 100644 core/database/src/main/java/com/chan/database/datastore/LikeDataStoreManager.kt create mode 100644 core/database/src/main/java/com/chan/database/datastore/LikeSerializer.kt diff --git a/core/database/src/main/java/com/chan/database/datastore/LikeDataStoreManager.kt b/core/database/src/main/java/com/chan/database/datastore/LikeDataStoreManager.kt new file mode 100644 index 0000000..f2856a1 --- /dev/null +++ b/core/database/src/main/java/com/chan/database/datastore/LikeDataStoreManager.kt @@ -0,0 +1,38 @@ +package com.chan.database.datastore + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.core.DataStoreFactory +import androidx.datastore.dataStoreFile +import com.chan.like.proto.Like +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import java.util.concurrent.ConcurrentHashMap + +object LikeDataStoreManager { + + private val storeCache = ConcurrentHashMap>() + private val scopeCache = ConcurrentHashMap() + + fun getDataStore(context: Context, userId: String): DataStore { + + return storeCache.getOrPut(userId) { + val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + scopeCache[userId] = scope + + DataStoreFactory.create( + serializer = LikeSerializer, + produceFile = { context.dataStoreFile("like_$userId.pb") }, + scope = scope + ) + } + } + + fun clearAll() { + scopeCache.values.forEach { it.cancel() } + scopeCache.clear() + storeCache.clear() + } +} \ No newline at end of file diff --git a/core/database/src/main/java/com/chan/database/datastore/LikeSerializer.kt b/core/database/src/main/java/com/chan/database/datastore/LikeSerializer.kt new file mode 100644 index 0000000..3492a63 --- /dev/null +++ b/core/database/src/main/java/com/chan/database/datastore/LikeSerializer.kt @@ -0,0 +1,21 @@ +package com.chan.database.datastore + +import androidx.datastore.core.CorruptionException +import androidx.datastore.core.Serializer +import com.chan.like.proto.Like +import java.io.InputStream +import java.io.OutputStream + +object LikeSerializer : Serializer { + override val defaultValue: Like = Like.getDefaultInstance() + + override suspend fun readFrom(input: InputStream): Like { + try { + return Like.parseFrom(input) + } catch (exception: com.google.protobuf.InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", exception) + } + } + + override suspend fun writeTo(t: Like, output: OutputStream) = t.writeTo(output) +} \ No newline at end of file From 24eb613f107d945358a77ff37a838194b902b0f7 Mon Sep 17 00:00:00 2001 From: soochan Date: Tue, 18 Nov 2025 16:06:06 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat(di):=20LikeRepository=20hilt=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/chan/database/di/RepositoryModule.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 core/database/src/main/java/com/chan/database/di/RepositoryModule.kt diff --git a/core/database/src/main/java/com/chan/database/di/RepositoryModule.kt b/core/database/src/main/java/com/chan/database/di/RepositoryModule.kt new file mode 100644 index 0000000..f3ff58c --- /dev/null +++ b/core/database/src/main/java/com/chan/database/di/RepositoryModule.kt @@ -0,0 +1,18 @@ +package com.chan.database.di + +import com.chan.database.repository.LikeRepositoryImpl +import com.chan.domain.repository.LikeRepository +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +abstract class RepositoryModule { + + @Binds + abstract fun bindLikeRepository( + likeRepositoryImpl: LikeRepositoryImpl + ) : LikeRepository +} \ No newline at end of file From 91b8166f6455d5caacb0d6d0867d92ad4859f8bf Mon Sep 17 00:00:00 2001 From: soochan Date: Tue, 18 Nov 2025 16:06:50 +0900 Subject: [PATCH 6/8] =?UTF-8?q?feat(feature-like):=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=20=ED=99=94=EB=A9=B4=20=EB=BC=88=EB=8C=80=20=EC=84=A4?= =?UTF-8?q?=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/chan/like/LikeScreen.kt | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 feature/like/src/main/java/com/chan/like/LikeScreen.kt diff --git a/feature/like/src/main/java/com/chan/like/LikeScreen.kt b/feature/like/src/main/java/com/chan/like/LikeScreen.kt new file mode 100644 index 0000000..8fb343d --- /dev/null +++ b/feature/like/src/main/java/com/chan/like/LikeScreen.kt @@ -0,0 +1,73 @@ +package com.chan.like + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.filled.ShoppingCart +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import com.chan.android.model.ProductCardOrientation +import com.chan.android.ui.CommonProductsCard +import com.chan.android.ui.composable.MainTopBar +import com.chan.android.ui.theme.White + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun LikeScreen( + state: LikeContract.State, + onEvent: (LikeContract.Event) -> Unit, +) { + val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() + + Scaffold( + topBar = { + Column { + MainTopBar( + navigationIcon = null, + titleContent = { + Text(text = stringResource(R.string.history)) + }, + actions = { + IconButton(onClick = { onEvent(LikeContract.Event.TopBar.NavigateToSearch) }) { + Icon(Icons.Default.Search, contentDescription = "검색") + } + IconButton(onClick = { onEvent(LikeContract.Event.TopBar.NavigateToCart) }) { + Icon(Icons.Default.ShoppingCart, contentDescription = "장바구니") + } + }, + scrollBehavior = scrollBehavior + ) + } + }, + modifier = Modifier.fillMaxSize() + ) { innerPadding -> + Column(modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + .background(White)) { + state.likeProducts.forEach { product -> + CommonProductsCard( + product = product, + isLiked = product.productId in state.likedProductIds, + modifier = Modifier.fillMaxWidth(), + orientation = ProductCardOrientation.HORIZONTAL, + onClick = { onEvent(LikeContract.Event.LikeAction.NavigateToProductDetail(it)) }, + onLikeClick = { onEvent(LikeContract.Event.LikeAction.ToggleFavorite(it)) }, + onCartClick = { onEvent(LikeContract.Event.LikeAction.ShowCartPopup(it)) } + ) + } + + } + } +} \ No newline at end of file From ed754f59cd9260af3b3738c68ecfb9f44f694ecd Mon Sep 17 00:00:00 2001 From: soochan Date: Tue, 18 Nov 2025 16:08:08 +0900 Subject: [PATCH 7/8] =?UTF-8?q?feat(feature-like):=20LikeContract/LikeView?= =?UTF-8?q?Model=20=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - observeLikes(): 좋아요 설정/해제 상품 id 구독 - loadFavorites(): 좋아요 목록 구성 - toggleFavorite(): 좋아요 설정/해제 이벤트 --- .../main/java/com/chan/like/LikeContract.kt | 42 ++++++++++++ .../main/java/com/chan/like/LikeViewModel.kt | 68 +++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 feature/like/src/main/java/com/chan/like/LikeContract.kt create mode 100644 feature/like/src/main/java/com/chan/like/LikeViewModel.kt diff --git a/feature/like/src/main/java/com/chan/like/LikeContract.kt b/feature/like/src/main/java/com/chan/like/LikeContract.kt new file mode 100644 index 0000000..ee38842 --- /dev/null +++ b/feature/like/src/main/java/com/chan/like/LikeContract.kt @@ -0,0 +1,42 @@ +package com.chan.like + +import com.chan.android.LoadingState +import com.chan.android.ViewEffect +import com.chan.android.ViewEvent +import com.chan.android.ViewState +import com.chan.android.model.ProductsModel + +class LikeContract { + + sealed class Event : ViewEvent { + object LoadFavorites: Event() + + sealed class TopBar : Event() { + object NavigateToSearch : TopBar() + object NavigateToCart : TopBar() + } + + sealed class LikeAction : Event() { + data class ToggleFavorite(val productId: String) : LikeAction() + data class NavigateToProductDetail(val productId: String) : LikeAction() + data class ShowCartPopup(val productId: String) : LikeAction() + } + } + + data class State( + //좋아요 상품 목록 + val likeProducts: List = emptyList(), + //좋아요 상품 Id + val likedProductIds: Set = emptySet(), + val loadingState: LoadingState = LoadingState.Idle + ) : ViewState + + sealed class Effect : ViewEffect { + sealed class Navigation : Effect() { + object ToCartRoute : Navigation() + object ToSearchRoute : Navigation() + data class ToCartPopupRoute(val productId: String) : Navigation() + data class ToProductDetail(val productId: String) : Navigation() + } + } +} \ No newline at end of file diff --git a/feature/like/src/main/java/com/chan/like/LikeViewModel.kt b/feature/like/src/main/java/com/chan/like/LikeViewModel.kt new file mode 100644 index 0000000..3665811 --- /dev/null +++ b/feature/like/src/main/java/com/chan/like/LikeViewModel.kt @@ -0,0 +1,68 @@ +package com.chan.like + +import androidx.lifecycle.viewModelScope +import com.chan.android.BaseViewModel +import com.chan.domain.usecase.GetLikedProductIdsUseCase +import com.chan.domain.usecase.GetLikedProductsUseCase +import com.chan.domain.usecase.ToggleLikeUseCase +import com.chan.like.LikeContract.Effect.Navigation.ToCartPopupRoute +import com.chan.like.LikeContract.Effect.Navigation.ToCartRoute +import com.chan.like.LikeContract.Effect.Navigation.ToProductDetail +import com.chan.like.LikeContract.Effect.Navigation.ToSearchRoute +import com.chan.like.mapper.toProductsModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class LikeViewModel @Inject constructor( + private val toggleLikeUseCase: ToggleLikeUseCase, + private val getLikedProductIdsUseCase: GetLikedProductIdsUseCase, + private val getLikedProductsUseCase: GetLikedProductsUseCase, +) : BaseViewModel() { + + init { + observeLikes() + } + + override fun setInitialState() = LikeContract.State() + + override fun handleEvent(event: LikeContract.Event) { + when (event) { + LikeContract.Event.TopBar.NavigateToCart -> setEffect { ToCartRoute } + LikeContract.Event.TopBar.NavigateToSearch -> setEffect { ToSearchRoute } + is LikeContract.Event.LikeAction.NavigateToProductDetail -> setEffect { + ToProductDetail( + event.productId + ) + } + + is LikeContract.Event.LikeAction.ShowCartPopup -> setEffect { ToCartPopupRoute(event.productId) } + is LikeContract.Event.LikeAction.ToggleFavorite -> toggleFavorite(event.productId) + LikeContract.Event.LoadFavorites -> loadFavorites() + } + } + + private fun observeLikes() { + viewModelScope.launch { + getLikedProductIdsUseCase.invoke() + .collect { ids -> + setState { copy(likedProductIds = ids) } + } + } + } + + private fun loadFavorites() { + viewModelScope.launch { + val products = getLikedProductsUseCase.invoke().first().map { it.toProductsModel() } + setState { copy(likeProducts = products) } + } + } + + private fun toggleFavorite(productId: String) { + viewModelScope.launch { + toggleLikeUseCase.invoke(productId) + } + } +} \ No newline at end of file From 457e7a4a4a9b3d566fb10375ec200f666ce9b55b Mon Sep 17 00:00:00 2001 From: soochan Date: Tue, 18 Nov 2025 16:08:48 +0900 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=8B=9C,=20=EA=B8=B0=EC=A1=B4=20dataStore=20?= =?UTF-8?q?=EC=BA=90=EC=8B=9C=20=EC=B4=88=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/chan/mypage/domain/usecase/LogoutUseCase.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/feature/mypage/src/main/java/com/chan/mypage/domain/usecase/LogoutUseCase.kt b/feature/mypage/src/main/java/com/chan/mypage/domain/usecase/LogoutUseCase.kt index 6ea3d71..39cd6e8 100644 --- a/feature/mypage/src/main/java/com/chan/mypage/domain/usecase/LogoutUseCase.kt +++ b/feature/mypage/src/main/java/com/chan/mypage/domain/usecase/LogoutUseCase.kt @@ -1,16 +1,16 @@ package com.chan.mypage.domain.usecase -import android.content.Context import com.chan.auth.domain.AuthRepository import com.chan.database.datastore.CartDataStoreManager -import dagger.hilt.android.qualifiers.ApplicationContext +import com.chan.database.datastore.LikeDataStoreManager import javax.inject.Inject class LogoutUseCase @Inject constructor( - private val authRepository: AuthRepository + private val authRepository: AuthRepository, ) { suspend operator fun invoke() { authRepository.logout() CartDataStoreManager.clearAll() + LikeDataStoreManager.clearAll() } } \ No newline at end of file