diff --git a/data/src/main/java/com/dkin/chevit/data/di/usecase/PlanUseCaseModule.kt b/data/src/main/java/com/dkin/chevit/data/di/usecase/PlanUseCaseModule.kt index aa85ebf..a35ce8f 100644 --- a/data/src/main/java/com/dkin/chevit/data/di/usecase/PlanUseCaseModule.kt +++ b/data/src/main/java/com/dkin/chevit/data/di/usecase/PlanUseCaseModule.kt @@ -85,8 +85,7 @@ internal object PlanUseCaseModule { deviceIdProvider: DeviceIdProvider ) = GetMyChecklistUseCase( coroutineDispatcherProvider, - planRepository, - deviceIdProvider + planRepository ) @Provides @@ -96,8 +95,7 @@ internal object PlanUseCaseModule { deviceIdProvider: DeviceIdProvider ) = GetMyTemplateListUseCase( coroutineDispatcherProvider, - planRepository, - deviceIdProvider + planRepository ) @Provides @@ -116,8 +114,7 @@ internal object PlanUseCaseModule { deviceIdProvider: DeviceIdProvider ) = GetChecklistUseCase( coroutineDispatcherProvider, - planRepository, - deviceIdProvider + planRepository ) @Provides @@ -127,8 +124,7 @@ internal object PlanUseCaseModule { deviceIdProvider: DeviceIdProvider ) = GetTemplateUseCase( coroutineDispatcherProvider, - planRepository, - deviceIdProvider + planRepository ) @Provides diff --git a/data/src/main/java/com/dkin/chevit/data/model/request/CheckItemCheckedPayload.kt b/data/src/main/java/com/dkin/chevit/data/model/request/CheckItemCheckedPayload.kt new file mode 100644 index 0000000..4d951e3 --- /dev/null +++ b/data/src/main/java/com/dkin/chevit/data/model/request/CheckItemCheckedPayload.kt @@ -0,0 +1,10 @@ +package com.dkin.chevit.data.model.request + +import com.dkin.chevit.data.DataModel +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +internal data class CheckItemCheckedPayload( + @SerialName("isCheck") val checked: Boolean, +) : DataModel diff --git a/data/src/main/java/com/dkin/chevit/data/remote/PlanAPI.kt b/data/src/main/java/com/dkin/chevit/data/remote/PlanAPI.kt index 9b3829b..6dcf9e7 100644 --- a/data/src/main/java/com/dkin/chevit/data/remote/PlanAPI.kt +++ b/data/src/main/java/com/dkin/chevit/data/remote/PlanAPI.kt @@ -1,6 +1,7 @@ package com.dkin.chevit.data.remote import com.dkin.chevit.data.model.request.CategoryPayload +import com.dkin.chevit.data.model.request.CheckItemCheckedPayload import com.dkin.chevit.data.model.request.CheckItemPayload import com.dkin.chevit.data.model.request.NewSchedulePayload import com.dkin.chevit.data.model.request.NewTemplatePayload @@ -11,6 +12,7 @@ import com.dkin.chevit.data.model.response.LocaleResponse import com.dkin.chevit.data.model.response.NewsResponse import com.dkin.chevit.data.model.response.PlanResponse import com.dkin.chevit.data.model.response.WeatherListResponse +import com.dkin.chevit.domain.base.None import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.Field @@ -60,7 +62,6 @@ internal interface PlanAPI { */ @GET("fetchMyPlanList") suspend fun fetchMyPlanList( - @Query("Device-Id") deviceId: String, @Query("typ") typ: String ): List @@ -86,7 +87,6 @@ internal interface PlanAPI { @GET("plan/{planId}") suspend fun fetchPlan( @Path("planId") planId: String, - @Query("Device-Id") deviceId: String, @Query("typ") typ: String ): PlanResponse @@ -184,6 +184,6 @@ internal interface PlanAPI { suspend fun checkCheckItem( @Path("planId") planId: String, @Path("checkItemId") checkItemId: String, - @Field("isCheck") checked: Boolean + @Body body: CheckItemCheckedPayload ): CheckItemResponse } diff --git a/data/src/main/java/com/dkin/chevit/data/repository/PlanRepositoryImpl.kt b/data/src/main/java/com/dkin/chevit/data/repository/PlanRepositoryImpl.kt index 5dfb326..5926409 100644 --- a/data/src/main/java/com/dkin/chevit/data/repository/PlanRepositoryImpl.kt +++ b/data/src/main/java/com/dkin/chevit/data/repository/PlanRepositoryImpl.kt @@ -11,6 +11,7 @@ import com.dkin.chevit.data.model.mapper.TravelWithMapper import com.dkin.chevit.data.model.mapper.WeatherListMapper import com.dkin.chevit.data.model.mapper.mapDomainList import com.dkin.chevit.data.model.request.CategoryPayload +import com.dkin.chevit.data.model.request.CheckItemCheckedPayload import com.dkin.chevit.data.model.request.CheckItemPayload import com.dkin.chevit.data.model.request.NewSchedulePayload import com.dkin.chevit.data.model.request.NewTemplatePayload @@ -75,8 +76,8 @@ internal class PlanRepositoryImpl @Inject constructor( return planAPI.newTemplate(payload).let(PlanMapper::mapDomain) } - override suspend fun fetchMyPlanList(deviceId: String, typ: PlanType): DomainListModel { - return planAPI.fetchMyPlanList(deviceId, typ.name).mapDomainList(PlanMapper::mapDomain) + override suspend fun fetchMyPlanList(typ: PlanType): DomainListModel { + return planAPI.fetchMyPlanList(typ.name).mapDomainList(PlanMapper::mapDomain) } override suspend fun updateTemplate(planId: String, subject: String, color: ColorType): Plan { @@ -92,8 +93,8 @@ internal class PlanRepositoryImpl @Inject constructor( return None } - override suspend fun fetchPlan(planId: String, deviceId: String, typ: PlanType): Plan { - return planAPI.fetchPlan(planId, deviceId, typ.name).let(PlanMapper::mapDomain) + override suspend fun fetchPlan(planId: String, typ: PlanType): Plan { + return planAPI.fetchPlan(planId, typ.name).let(PlanMapper::mapDomain) } override suspend fun copyTemplate(planId: String, refPlanId: String?): Plan { @@ -182,6 +183,7 @@ internal class PlanRepositoryImpl @Inject constructor( checkItemId: String, checked: Boolean ): CheckItem { - return planAPI.checkCheckItem(planId, checkItemId, checked).let(CheckItemMapper::mapDomain) + val payload = CheckItemCheckedPayload(checked) + return planAPI.checkCheckItem(planId, checkItemId, payload).let(CheckItemMapper::mapDomain) } } diff --git a/domain/src/main/java/com/dkin/chevit/domain/repository/PlanRepository.kt b/domain/src/main/java/com/dkin/chevit/domain/repository/PlanRepository.kt index 59ae094..216e1fd 100644 --- a/domain/src/main/java/com/dkin/chevit/domain/repository/PlanRepository.kt +++ b/domain/src/main/java/com/dkin/chevit/domain/repository/PlanRepository.kt @@ -32,13 +32,13 @@ interface PlanRepository { suspend fun newTemplate(subject: String, color: ColorType, refPlanId: String?): Plan - suspend fun fetchMyPlanList(deviceId: String, typ: PlanType): DomainListModel + suspend fun fetchMyPlanList(typ: PlanType): DomainListModel suspend fun updateTemplate(planId: String, subject: String, color: ColorType): Plan suspend fun deletePlan(planId: String): None - suspend fun fetchPlan(planId: String, deviceId: String, typ: PlanType): Plan + suspend fun fetchPlan(planId: String, typ: PlanType): Plan suspend fun copyTemplate(planId: String, refPlanId: String?): Plan diff --git a/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetChecklistUseCase.kt b/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetChecklistUseCase.kt index c979e33..93adfd9 100644 --- a/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetChecklistUseCase.kt +++ b/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetChecklistUseCase.kt @@ -4,17 +4,15 @@ import com.dkin.chevit.domain.base.CoroutineDispatcherProvider import com.dkin.chevit.domain.base.IOUseCase import com.dkin.chevit.domain.model.Plan import com.dkin.chevit.domain.model.PlanType -import com.dkin.chevit.domain.provider.DeviceIdProvider import com.dkin.chevit.domain.repository.PlanRepository class GetChecklistUseCase( coroutineDispatcherProvider: CoroutineDispatcherProvider, private val planRepository: PlanRepository, - private val deviceIdProvider: DeviceIdProvider, ) : IOUseCase(coroutineDispatcherProvider) { override suspend fun execute(params: Param): Plan { return planRepository.fetchPlan( - params.planId, deviceId = deviceIdProvider.getDeviceId(), + params.planId, typ = PlanType.SCHEDULE, ) } diff --git a/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetMyChecklistUseCase.kt b/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetMyChecklistUseCase.kt index d604d9d..183a161 100644 --- a/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetMyChecklistUseCase.kt +++ b/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetMyChecklistUseCase.kt @@ -5,17 +5,14 @@ import com.dkin.chevit.domain.base.DomainListModel import com.dkin.chevit.domain.base.IOUseCase import com.dkin.chevit.domain.model.Plan import com.dkin.chevit.domain.model.PlanType -import com.dkin.chevit.domain.provider.DeviceIdProvider import com.dkin.chevit.domain.repository.PlanRepository class GetMyChecklistUseCase( coroutineDispatcherProvider: CoroutineDispatcherProvider, private val planRepository: PlanRepository, - private val deviceIdProvider: DeviceIdProvider, ) : IOUseCase>(coroutineDispatcherProvider) { override suspend fun execute(params: Unit): DomainListModel { return planRepository.fetchMyPlanList( - deviceId = deviceIdProvider.getDeviceId(), typ = PlanType.SCHEDULE, ) } diff --git a/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetMyTemplateListUseCase.kt b/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetMyTemplateListUseCase.kt index 6c8021c..bd77c16 100644 --- a/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetMyTemplateListUseCase.kt +++ b/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetMyTemplateListUseCase.kt @@ -5,17 +5,14 @@ import com.dkin.chevit.domain.base.DomainListModel import com.dkin.chevit.domain.base.IOUseCase import com.dkin.chevit.domain.model.Plan import com.dkin.chevit.domain.model.PlanType -import com.dkin.chevit.domain.provider.DeviceIdProvider import com.dkin.chevit.domain.repository.PlanRepository class GetMyTemplateListUseCase( coroutineDispatcherProvider: CoroutineDispatcherProvider, private val planRepository: PlanRepository, - private val deviceIdProvider: DeviceIdProvider, ) : IOUseCase>(coroutineDispatcherProvider) { override suspend fun execute(params: Unit): DomainListModel { return planRepository.fetchMyPlanList( - deviceId = deviceIdProvider.getDeviceId(), typ = PlanType.TEMPLATE, ) } diff --git a/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetTemplateUseCase.kt b/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetTemplateUseCase.kt index 4dcc03d..aa6c869 100644 --- a/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetTemplateUseCase.kt +++ b/domain/src/main/java/com/dkin/chevit/domain/usecase/plan/GetTemplateUseCase.kt @@ -4,17 +4,15 @@ import com.dkin.chevit.domain.base.CoroutineDispatcherProvider import com.dkin.chevit.domain.base.IOUseCase import com.dkin.chevit.domain.model.Plan import com.dkin.chevit.domain.model.PlanType -import com.dkin.chevit.domain.provider.DeviceIdProvider import com.dkin.chevit.domain.repository.PlanRepository class GetTemplateUseCase( coroutineDispatcherProvider: CoroutineDispatcherProvider, private val planRepository: PlanRepository, - private val deviceIdProvider: DeviceIdProvider, ) : IOUseCase(coroutineDispatcherProvider) { override suspend fun execute(params: Param): Plan { return planRepository.fetchPlan( - params.planId, deviceId = deviceIdProvider.getDeviceId(), + params.planId, typ = PlanType.SCHEDULE, ) } diff --git a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/AddItemScreen.kt b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/AddItemScreen.kt index 3731d64..5ee69f1 100644 --- a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/AddItemScreen.kt +++ b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/AddItemScreen.kt @@ -205,7 +205,10 @@ fun AddItemScreen( .fillMaxWidth() .height(54.dp), enabled = isValidInput, - onClick = { viewModel.addItem(title = title, memo = memo, count = count) } + onClick = { + viewModel.dispatch(ChecklistDetailIntent.AddCheckItem(content = title, memo = memo, quantity = count)) + onClickBack() + } ) { Text(text = "추가하기") } diff --git a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetail.kt b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetail.kt index 5ab84e4..3cc7d80 100644 --- a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetail.kt +++ b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetail.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.platform.ComposeView @@ -27,7 +28,29 @@ class ChecklistDetail : MVIComposeFragment() { override val viewModel: ChecklistDetailViewModel by viewModels() - override fun processEffect(effect: ChecklistDetailEffect) {} + override fun processEffect(effect: ChecklistDetailEffect) { + when (effect) { + ChecklistDetailEffect.GetChecklistFailed -> { + Toast.makeText(requireContext(), "체크리스트 가져오기에 실패하였습니다.", Toast.LENGTH_SHORT).show() + } + + ChecklistDetailEffect.AddItemFailed -> { + Toast.makeText(requireContext(), "아이템 추가에 실패하였습니다.", Toast.LENGTH_SHORT).show() + } + + ChecklistDetailEffect.CheckItemFailed -> { + Toast.makeText(requireContext(), "아이템 체크에 실패하였습니다.", Toast.LENGTH_SHORT).show() + } + + ChecklistDetailEffect.DeleteItemFailed -> { + Toast.makeText(requireContext(), "아이템 삭제에 실패하였습니다.", Toast.LENGTH_SHORT).show() + } + + ChecklistDetailEffect.EditItemFailed -> { + Toast.makeText(requireContext(), "아이템 업데이트에 실패하였습니다.", Toast.LENGTH_SHORT).show() + } + } + } override fun processState(state: ChecklistDetailState) {} @@ -74,7 +97,10 @@ class ChecklistDetail : EditItemScreen( viewModel = viewModel, onClickBack = { navController.popBackStack() }, - itemId = itemId, savedTitle = title, savedMemo = memo, savedCount = count + itemId = itemId, + savedTitle = title, + savedMemo = memo, + savedCount = count ) } dialog( @@ -84,7 +110,14 @@ class ChecklistDetail : val sortType by viewModel.sortType.collectAsState() ChecklistDetailSortContents( selectedType = sortType, - onClickType = { type -> viewModel.sortItem(type) }, + onClickType = { type -> + viewModel.dispatch( + ChecklistDetailIntent.SortItem( + type + ) + ) + navController.popBackStack() + }, onClose = { navController.popBackStack() } ) } @@ -107,7 +140,14 @@ class ChecklistDetail : navigateEditItem = { navController.navigate("editItem/${itemId}?title=${title}?memo=${memo}?count=${count}") }, - deleteItem = { viewModel.removeItem(itemId) }, + deleteItem = { + viewModel.dispatch( + ChecklistDetailIntent.DeleteCheckItem( + itemId + ) + ) + navController.popBackStack() + }, onClose = { navController.popBackStack() } ) } diff --git a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailContract.kt b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailContract.kt index a1dcae3..a73885a 100644 --- a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailContract.kt +++ b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailContract.kt @@ -4,13 +4,36 @@ import androidx.compose.runtime.Stable import com.dkin.chevit.core.mvi.ViewEffect import com.dkin.chevit.core.mvi.ViewIntent import com.dkin.chevit.core.mvi.ViewState +import com.dkin.chevit.presentation.common.model.SortType sealed interface ChecklistDetailIntent : ViewIntent { + data class SortItem(val sortType: SortType) : ChecklistDetailIntent + data class UpdateCheckItemChecked( + val checkItemId: String, + val checked: Boolean + ) : ChecklistDetailIntent + + data class DeleteCheckItem(val checkItemId: String) : ChecklistDetailIntent + + data class AddCheckItem( + val content: String, + val memo: String, + val quantity: Int + ) : ChecklistDetailIntent + + data class UpdateCheckItem( + val checkItemId: String, + val content: String, + val memo: String, + val quantity: Int + ) : ChecklistDetailIntent + } @Stable data class ChecklistDetailState( val planId: String, + val categoryId: String, val title: String, val detailItems: List = listOf() ) : ViewState { @@ -25,15 +48,50 @@ data class ChecklistDetailState( companion object { fun empty(): ChecklistDetailState = ChecklistDetailState( + planId = "", categoryId = "", title = "", detailItems = listOf() + ) + + fun dummy(): ChecklistDetailState = ChecklistDetailState( planId = "", + categoryId = "", title = "파리, 프랑스", detailItems = listOf( ChecklistDetailItem(id = "0", checked = true, title = "여권", memo = "", count = 1), - ChecklistDetailItem(id = "1", checked = true, title = "항공권", memo = "프린트하기", count = 1), - ChecklistDetailItem(id = "2", checked = false, title = "여행자보험", memo = "프린트하기", count = 1), - ChecklistDetailItem(id = "3", checked = false, title = "지갑 및 현금", memo = "공항에서 환전하기", count = 1), - ChecklistDetailItem(id = "4", checked = false, title = "볼펜", memo = "입국신고서 및 서류 작성용", count = 1), - ChecklistDetailItem(id = "5", checked = false, title = "기내용 겉옷", memo = "", count = 2), + ChecklistDetailItem( + id = "1", + checked = true, + title = "항공권", + memo = "프린트하기", + count = 1 + ), + ChecklistDetailItem( + id = "2", + checked = false, + title = "여행자보험", + memo = "프린트하기", + count = 1 + ), + ChecklistDetailItem( + id = "3", + checked = false, + title = "지갑 및 현금", + memo = "공항에서 환전하기", + count = 1 + ), + ChecklistDetailItem( + id = "4", + checked = false, + title = "볼펜", + memo = "입국신고서 및 서류 작성용", + count = 1 + ), + ChecklistDetailItem( + id = "5", + checked = false, + title = "기내용 겉옷", + memo = "", + count = 2 + ), ChecklistDetailItem(id = "6", checked = false, title = "목베개", memo = "", count = 1), ChecklistDetailItem(id = "7", checked = true, title = "티", memo = "", count = 2), ChecklistDetailItem(id = "8", checked = true, title = "바지", memo = "", count = 2), @@ -44,4 +102,9 @@ data class ChecklistDetailState( } sealed interface ChecklistDetailEffect : ViewEffect { + object GetChecklistFailed : ChecklistDetailEffect + object CheckItemFailed : ChecklistDetailEffect + object DeleteItemFailed : ChecklistDetailEffect + object AddItemFailed : ChecklistDetailEffect + object EditItemFailed : ChecklistDetailEffect } diff --git a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailScreen.kt b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailScreen.kt index cc13aab..8b9972a 100644 --- a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailScreen.kt +++ b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailScreen.kt @@ -50,13 +50,10 @@ fun ChecklistDetailScreen( openMoreSheet: (itemId: String, title: String, memo: String, count: Int) -> Unit, ) { val detailState by viewModel.state.collectAsState() + val sortType by viewModel.sortType.collectAsState() var input by remember { mutableStateOf("") } var checkUnCompleted by rememberSaveable { mutableStateOf(false) } - LaunchedEffect(input) { - viewModel.searchItem(input) - } - Box(modifier = Modifier.fillMaxSize()) { Column( modifier = Modifier.fillMaxSize() @@ -158,8 +155,14 @@ fun ChecklistDetailScreen( .fillMaxWidth() .weight(1f), detailItems = detailState.detailItems, + searchKeyword = input, + sortType = sortType, checkUnCompleted = checkUnCompleted, - onClickItem = { itemId -> viewModel.checkItem(itemId) }, + onClickItem = { itemId, checked -> + viewModel.dispatch( + ChecklistDetailIntent.UpdateCheckItemChecked(itemId, checked) + ) + }, navigateAddItem = navigateAddItem, openMoreSheet = openMoreSheet ) diff --git a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailViewModel.kt b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailViewModel.kt index 9008a8f..ea85414 100644 --- a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailViewModel.kt +++ b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/ChecklistDetailViewModel.kt @@ -1,14 +1,30 @@ package com.dkin.chevit.presentation.checklist.detail +import androidx.lifecycle.viewModelScope import com.dkin.chevit.core.mvi.MVIViewModel +import com.dkin.chevit.domain.base.get +import com.dkin.chevit.domain.base.onComplete +import com.dkin.chevit.domain.usecase.plan.DeleteCheckItemUseCase +import com.dkin.chevit.domain.usecase.plan.GetCategoryUseCase +import com.dkin.chevit.domain.usecase.plan.PostNewCheckItemUseCase +import com.dkin.chevit.domain.usecase.plan.UpdateCheckItemCheckedUseCase +import com.dkin.chevit.domain.usecase.plan.UpdateCheckItemUseCase import com.dkin.chevit.presentation.common.model.SortType import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class ChecklistDetailViewModel @Inject constructor() : +class ChecklistDetailViewModel @Inject constructor( + private val getCategoryUseCase: GetCategoryUseCase, + private val updateCheckItemCheckedUseCase: UpdateCheckItemCheckedUseCase, + private val deleteCheckItemUseCase: DeleteCheckItemUseCase, + private val postNewCheckItemUseCase: PostNewCheckItemUseCase, + private val updateCheckItemUseCase: UpdateCheckItemUseCase +) : MVIViewModel() { private val _sortType: MutableStateFlow = MutableStateFlow(SortType.NEW) @@ -17,36 +33,167 @@ class ChecklistDetailViewModel @Inject constructor() : override fun createInitialState(): ChecklistDetailState = ChecklistDetailState.empty() override suspend fun processIntent(intent: ChecklistDetailIntent) { - when(intent) { - else -> {} + when (intent) { + is ChecklistDetailIntent.SortItem -> sortItem(intent.sortType) + is ChecklistDetailIntent.AddCheckItem -> addItem( + intent.content, + intent.memo, + intent.quantity + ) + + is ChecklistDetailIntent.DeleteCheckItem -> removeItem(intent.checkItemId) + is ChecklistDetailIntent.UpdateCheckItem -> editItem( + intent.checkItemId, + intent.content, + intent.memo, + intent.quantity + ) + + is ChecklistDetailIntent.UpdateCheckItemChecked -> checkItem( + intent.checkItemId, + intent.checked + ) } } fun getChecklistDetail(planId: String, categoryId: String) { - //TODO - } + viewModelScope.launch { + val detailUseCase = getCategoryUseCase( + GetCategoryUseCase.Param(planId = planId, categoryId = categoryId) + ) + detailUseCase.onComplete( + doOnComplete = {}, + doOnError = { + setEffect { ChecklistDetailEffect.GetChecklistFailed } + }, + doOnSuccess = { + val detail = this + setState { + copy( + planId = planId, + categoryId = categoryId, + title = detail.subject, + detailItems = detail.checkList.map { + ChecklistDetailState.ChecklistDetailItem( + id = it.id, + checked = it.checked, + title = it.content, + memo = it.memo, + count = it.quantity + ) + } + ) + } + } + ) - fun searchItem(keyword: String) { - //TODO + } } - fun checkItem(itemId: String) { - //TODO + private suspend fun checkItem(itemId: String, checked: Boolean) { + val state = state.value + val checkItem = updateCheckItemCheckedUseCase( + UpdateCheckItemCheckedUseCase.Param( + planId = state.planId, + checkItemId = itemId, + checked = checked + ) + ) + checkItem.onComplete( + doOnComplete = {}, + doOnError = { + if (it is NullPointerException) { + //성공했을 때 응답값이 null로 내려옴 + getChecklistDetail(state.planId, state.categoryId) + } else { + setEffect { ChecklistDetailEffect.CheckItemFailed } + } + }, + doOnSuccess = { + getChecklistDetail(state.planId, state.categoryId) + } + ) } - fun removeItem(itemId: String) { - //TODO + private suspend fun removeItem(itemId: String) { + val state = state.value + val deleteItem = deleteCheckItemUseCase( + DeleteCheckItemUseCase.Param( + planId = state.planId, + checkItemId = itemId, + ) + ) + deleteItem.onComplete( + doOnComplete = {}, + doOnError = { + if (it is NullPointerException) { + //성공했을 때 응답값이 null로 내려옴 + getChecklistDetail(state.planId, state.categoryId) + } else { + setEffect { ChecklistDetailEffect.DeleteItemFailed } + } + }, + doOnSuccess = { + getChecklistDetail(state.planId, state.categoryId) + } + ) } - fun sortItem(type: SortType) { - //TODO + private suspend fun addItem(title: String, memo: String, count: Int) { + val state = state.value + val addItem = postNewCheckItemUseCase( + PostNewCheckItemUseCase.Param( + planId = state.planId, + categoryId = state.categoryId, + content = title, + memo = memo, + quantity = count, + ) + ) + addItem.onComplete( + doOnComplete = {}, + doOnError = { + if (it is NullPointerException) { + //성공했을 때 응답값이 null로 내려옴 + getChecklistDetail(state.planId, state.categoryId) + } else { + setEffect { ChecklistDetailEffect.AddItemFailed } + } + }, + doOnSuccess = { + getChecklistDetail(state.planId, state.categoryId) + } + ) } - fun addItem(title: String, memo: String, count: Int) { - //TODO + private suspend fun editItem(itemId: String, title: String, memo: String, count: Int) { + val state = state.value + val editItem = updateCheckItemUseCase( + UpdateCheckItemUseCase.Param( + planId = state.planId, + checkItemId = itemId, + content = title, + memo = memo, + quantity = count, + ) + ) + editItem.onComplete( + doOnComplete = {}, + doOnError = { + if (it is NullPointerException) { + //성공했을 때 응답값이 null로 내려옴 + getChecklistDetail(state.planId, state.categoryId) + } else { + setEffect { ChecklistDetailEffect.EditItemFailed } + } + }, + doOnSuccess = { + getChecklistDetail(state.planId, state.categoryId) + } + ) } - fun editItem(itemId: String, title: String, memo: String, count: Int) { - //TODO + private fun sortItem(type: SortType) { + _sortType.update { type } } } \ No newline at end of file diff --git a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/EditItemScreen.kt b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/EditItemScreen.kt index 0d993ce..fec9fdf 100644 --- a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/EditItemScreen.kt +++ b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/EditItemScreen.kt @@ -210,12 +210,12 @@ fun EditItemScreen( .height(54.dp), enabled = isValidInput, onClick = { - viewModel.editItem( - itemId = itemId, - title = title, - memo = memo, - count = count + viewModel.dispatch( + ChecklistDetailIntent.UpdateCheckItem( + checkItemId = itemId, content = title, memo = memo, quantity = count + ) ) + onClickBack() } ) { Text(text = "수정하기") diff --git a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/contents/ChecklistDetailListContents.kt b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/contents/ChecklistDetailListContents.kt index 7d1c589..471ff70 100644 --- a/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/contents/ChecklistDetailListContents.kt +++ b/presentation/checklist/src/main/java/com/dkin/chevit/presentation/checklist/detail/contents/ChecklistDetailListContents.kt @@ -3,6 +3,7 @@ package com.dkin.chevit.presentation.checklist.detail.contents import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -20,12 +21,15 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.dkin.chevit.presentation.checklist.detail.ChecklistDetailState +import com.dkin.chevit.presentation.common.model.SortType import com.dkin.chevit.presentation.resource.ChevitFloatingButton import com.dkin.chevit.presentation.resource.ChevitTheme import com.dkin.chevit.presentation.resource.icon.ChevitIcon @@ -38,15 +42,17 @@ import com.dkin.chevit.presentation.resource.util.clickableNoRipple fun ChecklistDetailListContents( detailItems: List, checkUnCompleted: Boolean, - onClickItem: (itemId: String) -> Unit, + searchKeyword: String, + sortType: SortType, + onClickItem: (itemId: String, checked: Boolean) -> Unit, navigateAddItem: () -> Unit, openMoreSheet: (itemId: String, title: String, memo: String, count: Int) -> Unit, modifier: Modifier = Modifier ) { val detailList = if (checkUnCompleted) { - detailItems.filter { !it.checked } + detailItems.filter { !it.checked && it.title.contains(searchKeyword) } } else { - detailItems + detailItems.filter { it.title.contains(searchKeyword) } } val listState = rememberLazyListState() Column(modifier = modifier.fillMaxWidth()) { @@ -64,6 +70,8 @@ fun ChecklistDetailListContents( LazyColumn( state = listState, contentPadding = PaddingValues(bottom = 10.dp), + reverseLayout = sortType == SortType.OLD, + verticalArrangement = Arrangement.Top ) { items( count = detailList.size @@ -90,7 +98,7 @@ fun ChecklistDetailListContents( @Composable private fun DetailItem( item: ChecklistDetailState.ChecklistDetailItem, - onClickItem: (itemId: String) -> Unit, + onClickItem: (itemId: String, checked: Boolean) -> Unit, openMoreSheet: (itemId: String, title: String, memo: String, count: Int) -> Unit ) { Row( @@ -103,7 +111,7 @@ private fun DetailItem( modifier = Modifier .size(24.dp) .clip(CircleShape) - .clickable { onClickItem(item.id) }, + .clickable { onClickItem(item.id, !item.checked) }, contentAlignment = Alignment.Center ) { Image(