From 806130a4b78e6b170c07f22bda93cbfdb0e5f09a Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sat, 20 Jan 2024 23:21:10 +0900 Subject: [PATCH 01/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B4=EC=9A=94=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=84=9C=EB=B2=84=20=ED=86=B5?= =?UTF-8?q?=EC=8B=A0=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/susu/core/model/Envelope.kt | 8 ++++ .../main/java/com/susu/core/model/Friend.kt | 10 +++++ .../com/susu/data/data/di/RepositoryModule.kt | 7 ++++ .../repository/EnvelopesRepositoryImpl.kt | 27 +++++++++++++ .../susu/data/remote/api/EnvelopesService.kt | 18 +++++++++ .../susu/data/remote/di/ApiServiceModule.kt | 8 +++- .../model/response/EnvelopesListResponse.kt | 26 +++++++++++++ .../model/response/EnvelopesResponse.kt | 39 +++++++++++++++++++ .../domain/repository/EnvelopesRepository.kt | 14 +++++++ .../envelope/GetEnvelopesListUseCase.kt | 31 +++++++++++++++ .../com/susu/feature/sent/SentContract.kt | 19 +++++++++ .../com/susu/feature/sent/SentViewModel.kt | 21 ++++++++++ 12 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 core/model/src/main/java/com/susu/core/model/Envelope.kt create mode 100644 core/model/src/main/java/com/susu/core/model/Friend.kt create mode 100644 data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt create mode 100644 data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt create mode 100644 data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt create mode 100644 data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt create mode 100644 domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt create mode 100644 domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesListUseCase.kt create mode 100644 feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt create mode 100644 feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt diff --git a/core/model/src/main/java/com/susu/core/model/Envelope.kt b/core/model/src/main/java/com/susu/core/model/Envelope.kt new file mode 100644 index 00000000..8877563d --- /dev/null +++ b/core/model/src/main/java/com/susu/core/model/Envelope.kt @@ -0,0 +1,8 @@ +package com.susu.core.model + +data class Envelope( + val friend: Friend = Friend(), + val receivedAmounts: Int = 0, + val sentAmounts: Int = 0, + val totalAmounts: Int = 0, +) diff --git a/core/model/src/main/java/com/susu/core/model/Friend.kt b/core/model/src/main/java/com/susu/core/model/Friend.kt new file mode 100644 index 00000000..cd92c296 --- /dev/null +++ b/core/model/src/main/java/com/susu/core/model/Friend.kt @@ -0,0 +1,10 @@ +package com.susu.core.model + +data class Friend( + val id: Int = 0, + val uid: Int = 0, + val name: String = "", + val phoneNumber: String = "", + val createdAt: String = "", + val modifiedAt: String = "", +) diff --git a/data/src/main/java/com/susu/data/data/di/RepositoryModule.kt b/data/src/main/java/com/susu/data/data/di/RepositoryModule.kt index a9dc5263..f6d1133c 100644 --- a/data/src/main/java/com/susu/data/data/di/RepositoryModule.kt +++ b/data/src/main/java/com/susu/data/data/di/RepositoryModule.kt @@ -1,6 +1,7 @@ package com.susu.data.data.di import com.susu.data.data.repository.CategoryConfigRepositoryImpl +import com.susu.data.data.repository.EnvelopesRepositoryImpl import com.susu.data.data.repository.LedgerRecentSearchRepositoryImpl import com.susu.data.data.repository.LedgerRepositoryImpl import com.susu.data.data.repository.LoginRepositoryImpl @@ -8,6 +9,7 @@ import com.susu.data.data.repository.SignUpRepositoryImpl import com.susu.data.data.repository.TermRepositoryImpl import com.susu.data.data.repository.TokenRepositoryImpl import com.susu.domain.repository.CategoryConfigRepository +import com.susu.domain.repository.EnvelopesRepository import com.susu.domain.repository.LedgerRecentSearchRepository import com.susu.domain.repository.LedgerRepository import com.susu.domain.repository.LoginRepository @@ -57,4 +59,9 @@ abstract class RepositoryModule { abstract fun bindCategoryConfigRepository( categoryConfigRepositoryImpl: CategoryConfigRepositoryImpl, ): CategoryConfigRepository + + @Binds + abstract fun bindEnvelopesRepository( + envelopesRepositoryImpl: EnvelopesRepositoryImpl, + ): EnvelopesRepository } diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt new file mode 100644 index 00000000..c9078b98 --- /dev/null +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -0,0 +1,27 @@ +package com.susu.data.data.repository + +import com.susu.core.model.Envelope +import com.susu.data.remote.api.EnvelopesService +import com.susu.data.remote.model.response.toModel +import com.susu.domain.repository.EnvelopesRepository +import javax.inject.Inject + +class EnvelopesRepositoryImpl @Inject constructor( + private val envelopesService: EnvelopesService, +) : EnvelopesRepository { + override suspend fun getEnvelopesList( + friendIds: List?, + fromTotalAmounts: Int?, + toTotalAmounts: Int?, + page: Int?, + size: Int?, + sort: String?, + ): List = envelopesService.getEnvelopesList( + friendIds = friendIds, + fromTotalAmounts = fromTotalAmounts, + toTotalMounts = toTotalAmounts, + page = page, + size = size, + sort = sort, + ).getOrThrow().toModel() +} diff --git a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt new file mode 100644 index 00000000..010ea7dc --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt @@ -0,0 +1,18 @@ +package com.susu.data.remote.api + +import com.susu.data.remote.model.response.EnvelopesListResponse +import com.susu.data.remote.retrofit.ApiResult +import retrofit2.http.GET +import retrofit2.http.Query + +interface EnvelopesService { + @GET("envelopes/friend-statistics") + suspend fun getEnvelopesList( + @Query("friendIds") friendIds: List?, + @Query("fromTotalAmounts") fromTotalAmounts: Int?, + @Query("toTotalAmounts") toTotalMounts: Int?, + @Query("page") page: Int?, + @Query("size") size: Int?, + @Query("sort") sort: String?, + ): ApiResult +} diff --git a/data/src/main/java/com/susu/data/remote/di/ApiServiceModule.kt b/data/src/main/java/com/susu/data/remote/di/ApiServiceModule.kt index 945668fc..729e0a88 100644 --- a/data/src/main/java/com/susu/data/remote/di/ApiServiceModule.kt +++ b/data/src/main/java/com/susu/data/remote/di/ApiServiceModule.kt @@ -1,6 +1,7 @@ package com.susu.data.remote.di import com.susu.data.remote.api.CategoryService +import com.susu.data.remote.api.EnvelopesService import com.susu.data.remote.api.LedgerService import com.susu.data.remote.api.SignUpService import com.susu.data.remote.api.TermService @@ -11,7 +12,6 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit -import retrofit2.create import javax.inject.Singleton @Module @@ -53,4 +53,10 @@ object ApiServiceModule { fun providesCategoryService(retrofit: Retrofit): CategoryService { return retrofit.create(CategoryService::class.java) } + + @Singleton + @Provides + fun providesEnvelopeService(retrofit: Retrofit): EnvelopesService { + return retrofit.create(EnvelopesService::class.java) + } } diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt new file mode 100644 index 00000000..ac76868f --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt @@ -0,0 +1,26 @@ +package com.susu.data.remote.model.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class EnvelopesListResponse( + @SerialName("data") + val envelopesList: List, + val page: Int, + val size: Int, + val sort: Sort, + val totalCount: Int, + val totalPage: Int, +) + +@Serializable +data class Sort( + val empty: Boolean, + val sorted: Boolean, + val unsorted: Boolean, +) + +internal fun EnvelopesListResponse.toModel() = this.envelopesList.map { envelopes -> + envelopes.toModel() +} diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt new file mode 100644 index 00000000..83d1218d --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt @@ -0,0 +1,39 @@ +package com.susu.data.remote.model.response + +import com.susu.core.model.Envelope +import com.susu.core.model.Friend +import kotlinx.serialization.Serializable + +@Serializable +data class EnvelopesResponse( + val friend: FriendInfo, + val receivedAmounts: Int, + val sentAmounts: Int, + val totalAmounts: Int, +) + +@Serializable +data class FriendInfo( + val id: Int, + val uid: Int, + val createdAt: String, + val modifiedAt: String, + val name: String, + val phoneNumber: String, +) + +internal fun FriendInfo.toModel() = Friend( + id = id, + uid = uid, + name = name, + phoneNumber = phoneNumber, + createdAt = createdAt, + modifiedAt = modifiedAt, +) + +internal fun EnvelopesResponse.toModel() = Envelope( + friend = friend.toModel(), + receivedAmounts = receivedAmounts, + sentAmounts = sentAmounts, + totalAmounts = totalAmounts, +) diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt new file mode 100644 index 00000000..c9348c70 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -0,0 +1,14 @@ +package com.susu.domain.repository + +import com.susu.core.model.Envelope + +interface EnvelopesRepository { + suspend fun getEnvelopesList( + friendIds: List?, + fromTotalAmounts: Int?, + toTotalAmounts: Int?, + page: Int?, + size: Int?, + sort: String?, + ): List +} diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesListUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesListUseCase.kt new file mode 100644 index 00000000..ae47170b --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesListUseCase.kt @@ -0,0 +1,31 @@ +package com.susu.domain.usecase.envelope + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.EnvelopesRepository +import javax.inject.Inject + +class GetEnvelopesListUseCase @Inject constructor( + private val envelopesRepository: EnvelopesRepository, +) { + suspend operator fun invoke(param: Param) = runCatchingIgnoreCancelled { + with(param) { + envelopesRepository.getEnvelopesList( + friendIds = friendIds, + fromTotalAmounts = fromTotalAmounts, + toTotalAmounts = toTotalAmounts, + page = page, + size = size, + sort = sort, + ) + } + } + + data class Param( + val friendIds: List? = emptyList(), + val fromTotalAmounts: Int? = null, + val toTotalAmounts: Int? = null, + val page: Int? = null, + val size: Int? = null, + val sort: String? = null, + ) +} diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt new file mode 100644 index 00000000..8c897ddc --- /dev/null +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt @@ -0,0 +1,19 @@ +package com.susu.feature.sent + +import com.susu.core.model.Envelope +import com.susu.core.ui.base.SideEffect +import com.susu.core.ui.base.UiState +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf + +data class SentState( + val isLoading: Boolean = false, + val envelopesList: PersistentList = persistentListOf(), + val showEmptyEnvelopes: Boolean = false, +) : UiState + +sealed interface SentSideEffect : SideEffect { + data object NavigateEnvelopeDetail : SentSideEffect + data object NavigateEnvelopeAdd : SentSideEffect + data object NavigateEnvelopeSearch : SentSideEffect +} diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt new file mode 100644 index 00000000..2bbeba06 --- /dev/null +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt @@ -0,0 +1,21 @@ +package com.susu.feature.sent + +import androidx.lifecycle.viewModelScope +import com.susu.core.ui.base.BaseViewModel +import com.susu.domain.usecase.envelope.GetEnvelopesListUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SentViewModel @Inject constructor( + private val getEnvelopesListUseCase: GetEnvelopesListUseCase, +) : BaseViewModel( + SentState(), +) { + private var page = 0 + + fun getEnvelopesList() = viewModelScope.launch { + // TODO: 리스트 불러오기 + } +} From 87233c7722f89f0c42c90d4b399e813f6bff7164 Mon Sep 17 00:00:00 2001 From: syb8200 Date: Wed, 24 Jan 2024 21:44:41 +0900 Subject: [PATCH 02/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B4=EC=9A=94=20?= =?UTF-8?q?=EB=B4=89=ED=88=AC=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B6=88?= =?UTF-8?q?=EB=9F=AC=EC=98=A4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/feature/sent/SentContract.kt | 7 +- .../java/com/susu/feature/sent/SentScreen.kt | 87 +++++++++++++------ .../com/susu/feature/sent/SentViewModel.kt | 19 +++- .../susu/feature/sent/component/SentCard.kt | 37 +++++--- .../feature/sent/component/SentHistoryCard.kt | 13 ++- .../feature/sent/component/SentHistoryItem.kt | 46 +++++++--- 6 files changed, 153 insertions(+), 56 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt index 8c897ddc..14110840 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt @@ -12,8 +12,7 @@ data class SentState( val showEmptyEnvelopes: Boolean = false, ) : UiState -sealed interface SentSideEffect : SideEffect { - data object NavigateEnvelopeDetail : SentSideEffect - data object NavigateEnvelopeAdd : SentSideEffect - data object NavigateEnvelopeSearch : SentSideEffect +sealed interface SentEffect : SideEffect { + data object NavigateEnvelope : SentEffect + data object NavigateEnvelopeAdd : SentEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt index e824e0c2..c5af5eff 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt @@ -12,19 +12,20 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.LogoIcon import com.susu.core.designsystem.component.appbar.icon.NotificationIcon @@ -36,23 +37,44 @@ import com.susu.core.designsystem.component.button.SusuGhostButton import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.ui.extension.OnBottomReached +import com.susu.core.ui.extension.collectWithLifecycle import com.susu.feature.sent.component.SentCard @Composable fun SentRoute( + viewModel: SentViewModel = hiltViewModel(), padding: PaddingValues, navigateSentEnvelope: () -> Unit, navigateSentEnvelopeAdd: () -> Unit, ) { + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val envelopesListState = rememberLazyListState() + + viewModel.sideEffect.collectWithLifecycle { sideEffect -> + when (sideEffect) { + SentEffect.NavigateEnvelopeAdd -> navigateSentEnvelopeAdd() + is SentEffect.NavigateEnvelope -> navigateSentEnvelope() + } + } + + envelopesListState.OnBottomReached { + viewModel.getEnvelopesList() + } + SentScreen( + uiState = uiState, + envelopesListState = envelopesListState, padding = padding, - onClickHistoryShowAll = navigateSentEnvelope, - onClickAddEnvelope = navigateSentEnvelopeAdd, + onClickHistoryShowAll = viewModel::navigateSentEnvelope, + onClickAddEnvelope = viewModel::navigateSentAdd, ) } @Composable fun SentScreen( + uiState: SentState = SentState(), + envelopesListState: LazyListState = rememberLazyListState(), padding: PaddingValues, modifier: Modifier = Modifier, onClickSearchIcon: () -> Unit = {}, @@ -60,9 +82,6 @@ fun SentScreen( onClickHistoryShowAll: () -> Unit = {}, onClickAddEnvelope: () -> Unit = {}, ) { - // TODO: 수정 필요 (확인을 위해 false로 설정) - var isEmpty by remember { mutableStateOf(false) } - Box( modifier = Modifier .background(SusuTheme.colorScheme.background15) @@ -82,28 +101,42 @@ fun SentScreen( }, ) - if (!isEmpty) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), - contentPadding = PaddingValues(SusuTheme.spacing.spacing_m), + LazyColumn( + modifier = modifier.fillMaxSize(), + state = envelopesListState, + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + contentPadding = PaddingValues(SusuTheme.spacing.spacing_m), + ) { + item { + FilterSection( + padding = PaddingValues( + bottom = SusuTheme.spacing.spacing_xxs, + ), + ) + } + + items( + items = uiState.envelopesList, + key = { it.friend.id }, ) { - // TODO: 수정 필요 - item { - FilterSection( - padding = PaddingValues( - bottom = SusuTheme.spacing.spacing_xxs, - ), - ) - } - items(8) { - SentCard(onClick = onClickHistoryShowAll) - } + // 전체 보기 버튼 + SentCard( + friend = it.friend, + totalAmounts = it.totalAmounts, + sentAmounts = it.sentAmounts, + receivedAmounts = it.receivedAmounts, + onClick = onClickHistoryShowAll, + ) } - } else { + } + + if (uiState.showEmptyEnvelopes) { FilterSection( padding = PaddingValues(SusuTheme.spacing.spacing_m), ) - EmptyView() + EmptyView( + onClickAddEnvelope = onClickAddEnvelope + ) } } @@ -159,6 +192,7 @@ fun FilterSection( @Composable fun EmptyView( modifier: Modifier = Modifier, + onClickAddEnvelope: () -> Unit = {}, ) { Column( modifier = modifier.fillMaxSize(), @@ -175,6 +209,7 @@ fun EmptyView( color = GhostButtonColor.Black, style = SmallButtonStyle.height40, text = stringResource(R.string.sent_screen_empty_view_add_button), + onClick = onClickAddEnvelope, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt index 2bbeba06..d9d3d205 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt @@ -4,18 +4,33 @@ import androidx.lifecycle.viewModelScope import com.susu.core.ui.base.BaseViewModel import com.susu.domain.usecase.envelope.GetEnvelopesListUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class SentViewModel @Inject constructor( private val getEnvelopesListUseCase: GetEnvelopesListUseCase, -) : BaseViewModel( +) : BaseViewModel( SentState(), ) { private var page = 0 fun getEnvelopesList() = viewModelScope.launch { - // TODO: 리스트 불러오기 + getEnvelopesListUseCase( + GetEnvelopesListUseCase.Param(page = page), + ).onSuccess { envelopesList -> + page++ + val newEnvelopesList = currentState.envelopesList.plus(envelopesList).toPersistentList() + intent { + copy( + envelopesList = newEnvelopesList, + showEmptyEnvelopes = newEnvelopesList.isEmpty(), + ) + } + } } + + fun navigateSentEnvelope() = postSideEffect(SentEffect.NavigateEnvelope) + fun navigateSentAdd() = postSideEffect(SentEffect.NavigateEnvelopeAdd) } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt index a1a298ba..36b7b090 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.susu.core.designsystem.component.badge.BadgeColor import com.susu.core.designsystem.component.badge.BadgeStyle @@ -43,18 +44,23 @@ import com.susu.core.designsystem.theme.Gray60 import com.susu.core.designsystem.theme.Gray90 import com.susu.core.designsystem.theme.Orange20 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.Friend import com.susu.core.ui.extension.susuClickable +import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.sent.R @Composable fun SentCard( modifier: Modifier = Modifier, + friend: Friend = Friend(), + totalAmounts: Int, + sentAmounts: Int, + receivedAmounts: Int, onClick: () -> Unit = {}, ) { // TODO: 수정 필요 var expanded by remember { mutableStateOf(false) } val degrees by animateFloatAsState(if (expanded) 180f else 0f, label = "") - val historyCount = 3 Box( modifier = modifier @@ -76,14 +82,14 @@ fun SentCard( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = "김철수", + text = friend.name, style = SusuTheme.typography.title_xs, color = Gray100, ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_s)) SusuBadge( color = BadgeColor.Gray20, - text = "전체 100,000원", + text = "전체 ${totalAmounts.toMoneyFormat()}", padding = BadgeStyle.smallBadge, ) Spacer(modifier = modifier.weight(1f)) @@ -118,13 +124,13 @@ fun SentCard( ) } LinearProgressIndicator( - progress = { 0.7f }, - color = SusuTheme.colorScheme.primary, - trackColor = Orange20, - strokeCap = StrokeCap.Round, + progress = { sentAmounts.toFloat() / totalAmounts }, modifier = modifier .fillMaxWidth() .padding(vertical = SusuTheme.spacing.spacing_xxxxs), + color = SusuTheme.colorScheme.primary, + trackColor = Orange20, + strokeCap = StrokeCap.Round, ) Row( modifier = modifier @@ -133,12 +139,12 @@ fun SentCard( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = "70,000원", + text = "${sentAmounts.toMoneyFormat()}원", style = SusuTheme.typography.title_xxxs, color = Gray90, ) Text( - text = "30,000원", + text = "${receivedAmounts.toMoneyFormat()}원", style = SusuTheme.typography.title_xxxs, color = Gray60, ) @@ -151,8 +157,19 @@ fun SentCard( exit = fadeOut() + shrinkVertically(), ) { SentHistoryCard( - historyCount = historyCount, onClick = onClick, ) } } + +@Preview +@Composable +fun SentCardPreview() { + SusuTheme { + SentCard( + totalAmounts = 100000, + sentAmounts = 70000, + receivedAmounts = 30000, + ) + } +} diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt index 5c950b3a..47ec5798 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt @@ -12,6 +12,7 @@ import androidx.compose.material3.CardDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.SusuFilledButton @@ -24,7 +25,7 @@ import com.susu.feature.sent.R @Composable fun SentHistoryCard( modifier: Modifier = Modifier, - historyCount: Int, + historyCount: Int = 3, onClick: () -> Unit = {}, ) { Card( @@ -50,7 +51,7 @@ fun SentHistoryCard( ), ) { repeat(historyCount) { - SentHistoryItem(isSent = true) + SentHistoryItem() // TODO: id에 해당하는 envelopeInfo 넣어주기 Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_xxs)) } Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_xxs)) @@ -65,3 +66,11 @@ fun SentHistoryCard( } } } + +@Preview +@Composable +fun SentHistoryCardPreview() { + SusuTheme { + SentHistoryCard() + } +} diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt index e37dea12..14cbc92c 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt @@ -10,7 +10,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp +import androidx.compose.ui.tooling.preview.Preview import com.susu.core.designsystem.component.badge.BadgeColor import com.susu.core.designsystem.component.badge.BadgeStyle import com.susu.core.designsystem.component.badge.SusuBadge @@ -19,12 +19,22 @@ import com.susu.core.designsystem.theme.Gray40 import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.ui.extension.toMoneyFormat +import com.susu.core.ui.util.to_yyyy_dot_MM_dot_dd import com.susu.feature.sent.R +import java.time.LocalDateTime + +enum class EnvelopeType { + SENT, RECEIVED +} @Composable fun SentHistoryItem( modifier: Modifier = Modifier, - isSent: Boolean = true, + type: String = "", + event: String = "", + date: LocalDateTime = LocalDateTime.now(), + money: Int = 0, ) { Row( modifier = modifier.fillMaxWidth(), @@ -32,35 +42,47 @@ fun SentHistoryItem( ) { Icon( painter = painterResource( - id = if (isSent) { + id = if (type == EnvelopeType.SENT.name) { R.drawable.ic_round_arrow_sent } else { R.drawable.ic_round_arrow_received }, ), contentDescription = null, - tint = if (isSent) Orange60 else Gray40, - modifier = modifier.size(20.dp), + tint = if (type == EnvelopeType.SENT.name) Orange60 else Gray40, + modifier = modifier.size(SusuTheme.spacing.spacing_l), ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_s)) - // TODO: text 수정 필요 SusuBadge( - color = if (isSent) BadgeColor.Gray90 else BadgeColor.Gray40, - text = "돌잔치", + color = if (type == EnvelopeType.SENT.name) BadgeColor.Gray90 else BadgeColor.Gray40, + text = event, padding = BadgeStyle.extraSmallBadge, ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_s)) Text( - text = "23.07.18", + text = date.to_yyyy_dot_MM_dot_dd().substring(2), style = SusuTheme.typography.title_xxxs, - color = if (isSent) Gray100 else Gray50, + color = if (type == EnvelopeType.SENT.name) Gray100 else Gray50, ) Spacer(modifier = modifier.weight(1f)) Text( - text = "50,000원", + text = "${money.toMoneyFormat()}원", style = SusuTheme.typography.title_xxs, - color = if (isSent) Gray100 else Gray50, + color = if (type == EnvelopeType.SENT.name) Gray100 else Gray50, + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFFFFF) +@Composable +fun SentHistoryItemPreview() { + SusuTheme { + SentHistoryItem( + type = "SENT", + event = "돌잔치", + date = LocalDateTime.now(), + money = 50000, ) } } From 41d57730edf9e8d90e469117e3c933dfb49e8f1b Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sun, 28 Jan 2024 17:03:14 +0900 Subject: [PATCH 03/95] =?UTF-8?q?chore:=20=ED=95=84=EB=93=9C=EB=AA=85=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=95=84=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/model/src/main/java/com/susu/core/model/Friend.kt | 6 +++--- .../susu/data/data/repository/EnvelopesRepositoryImpl.kt | 2 +- .../main/java/com/susu/data/remote/api/EnvelopesService.kt | 2 +- .../data/remote/model/response/EnvelopesListResponse.kt | 4 ++-- .../susu/data/remote/model/response/EnvelopesResponse.kt | 6 ------ 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/core/model/src/main/java/com/susu/core/model/Friend.kt b/core/model/src/main/java/com/susu/core/model/Friend.kt index cd92c296..763033af 100644 --- a/core/model/src/main/java/com/susu/core/model/Friend.kt +++ b/core/model/src/main/java/com/susu/core/model/Friend.kt @@ -2,9 +2,9 @@ package com.susu.core.model data class Friend( val id: Int = 0, - val uid: Int = 0, +// val uid: Int = 0, val name: String = "", val phoneNumber: String = "", - val createdAt: String = "", - val modifiedAt: String = "", +// val createdAt: String = "", +// val modifiedAt: String = "", ) diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index c9078b98..c8b2608a 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -19,7 +19,7 @@ class EnvelopesRepositoryImpl @Inject constructor( ): List = envelopesService.getEnvelopesList( friendIds = friendIds, fromTotalAmounts = fromTotalAmounts, - toTotalMounts = toTotalAmounts, + toTotalAmounts = toTotalAmounts, page = page, size = size, sort = sort, diff --git a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt index 010ea7dc..2dfcff0f 100644 --- a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt +++ b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt @@ -10,7 +10,7 @@ interface EnvelopesService { suspend fun getEnvelopesList( @Query("friendIds") friendIds: List?, @Query("fromTotalAmounts") fromTotalAmounts: Int?, - @Query("toTotalAmounts") toTotalMounts: Int?, + @Query("toTotalAmounts") toTotalAmounts: Int?, @Query("page") page: Int?, @Query("size") size: Int?, @Query("sort") sort: String?, diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt index ac76868f..4455ebe8 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt @@ -9,9 +9,9 @@ data class EnvelopesListResponse( val envelopesList: List, val page: Int, val size: Int, - val sort: Sort, - val totalCount: Int, val totalPage: Int, + val totalCount: Int, + val sort: Sort, ) @Serializable diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt index 83d1218d..ed70e0bf 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt @@ -15,20 +15,14 @@ data class EnvelopesResponse( @Serializable data class FriendInfo( val id: Int, - val uid: Int, - val createdAt: String, - val modifiedAt: String, val name: String, val phoneNumber: String, ) internal fun FriendInfo.toModel() = Friend( id = id, - uid = uid, name = name, phoneNumber = phoneNumber, - createdAt = createdAt, - modifiedAt = modifiedAt, ) internal fun EnvelopesResponse.toModel() = Envelope( From 34a7fcbe7d17ee585c1220b873c8cecfd3cca006 Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sun, 28 Jan 2024 17:36:36 +0900 Subject: [PATCH 04/95] =?UTF-8?q?chore:=20Envelope=20=E2=86=92=20FriendSta?= =?UTF-8?q?tistics=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/core/model/{Envelope.kt => FriendStatistics.kt} | 2 +- .../com/susu/data/data/repository/EnvelopesRepositoryImpl.kt | 4 ++-- .../com/susu/data/remote/model/response/EnvelopesResponse.kt | 4 ++-- .../java/com/susu/domain/repository/EnvelopesRepository.kt | 4 ++-- .../sent/src/main/java/com/susu/feature/sent/SentContract.kt | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) rename core/model/src/main/java/com/susu/core/model/{Envelope.kt => FriendStatistics.kt} (84%) diff --git a/core/model/src/main/java/com/susu/core/model/Envelope.kt b/core/model/src/main/java/com/susu/core/model/FriendStatistics.kt similarity index 84% rename from core/model/src/main/java/com/susu/core/model/Envelope.kt rename to core/model/src/main/java/com/susu/core/model/FriendStatistics.kt index 8877563d..5b189b31 100644 --- a/core/model/src/main/java/com/susu/core/model/Envelope.kt +++ b/core/model/src/main/java/com/susu/core/model/FriendStatistics.kt @@ -1,6 +1,6 @@ package com.susu.core.model -data class Envelope( +data class FriendStatistics( val friend: Friend = Friend(), val receivedAmounts: Int = 0, val sentAmounts: Int = 0, diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index c8b2608a..23e85d50 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -1,6 +1,6 @@ package com.susu.data.data.repository -import com.susu.core.model.Envelope +import com.susu.core.model.FriendStatistics import com.susu.data.remote.api.EnvelopesService import com.susu.data.remote.model.response.toModel import com.susu.domain.repository.EnvelopesRepository @@ -16,7 +16,7 @@ class EnvelopesRepositoryImpl @Inject constructor( page: Int?, size: Int?, sort: String?, - ): List = envelopesService.getEnvelopesList( + ): List = envelopesService.getEnvelopesList( friendIds = friendIds, fromTotalAmounts = fromTotalAmounts, toTotalAmounts = toTotalAmounts, diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt index ed70e0bf..199c6a9f 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt @@ -1,6 +1,6 @@ package com.susu.data.remote.model.response -import com.susu.core.model.Envelope +import com.susu.core.model.FriendStatistics import com.susu.core.model.Friend import kotlinx.serialization.Serializable @@ -25,7 +25,7 @@ internal fun FriendInfo.toModel() = Friend( phoneNumber = phoneNumber, ) -internal fun EnvelopesResponse.toModel() = Envelope( +internal fun EnvelopesResponse.toModel() = FriendStatistics( friend = friend.toModel(), receivedAmounts = receivedAmounts, sentAmounts = sentAmounts, diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index c9348c70..ea2377b7 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -1,6 +1,6 @@ package com.susu.domain.repository -import com.susu.core.model.Envelope +import com.susu.core.model.FriendStatistics interface EnvelopesRepository { suspend fun getEnvelopesList( @@ -10,5 +10,5 @@ interface EnvelopesRepository { page: Int?, size: Int?, sort: String?, - ): List + ): List } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt index 14110840..5c945d97 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt @@ -1,6 +1,6 @@ package com.susu.feature.sent -import com.susu.core.model.Envelope +import com.susu.core.model.FriendStatistics import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState import kotlinx.collections.immutable.PersistentList @@ -8,7 +8,7 @@ import kotlinx.collections.immutable.persistentListOf data class SentState( val isLoading: Boolean = false, - val envelopesList: PersistentList = persistentListOf(), + val envelopesList: PersistentList = persistentListOf(), val showEmptyEnvelopes: Boolean = false, ) : UiState From 220c17cc0cc7c489aefa85a2e490ff9dd52d6ebc Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sun, 28 Jan 2024 19:25:04 +0900 Subject: [PATCH 05/95] =?UTF-8?q?chore:=20conflict=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/susu/core/model/EnvelopeStatics.kt | 8 -------- core/model/src/main/java/com/susu/core/model/Friend.kt | 8 ++++---- .../susu/data/data/repository/EnvelopesRepositoryImpl.kt | 1 - .../susu/data/remote/model/response/EnvelopesResponse.kt | 3 +-- .../com/susu/domain/repository/EnvelopesRepository.kt | 2 +- 5 files changed, 6 insertions(+), 16 deletions(-) delete mode 100644 core/model/src/main/java/com/susu/core/model/EnvelopeStatics.kt diff --git a/core/model/src/main/java/com/susu/core/model/EnvelopeStatics.kt b/core/model/src/main/java/com/susu/core/model/EnvelopeStatics.kt deleted file mode 100644 index f123fb2e..00000000 --- a/core/model/src/main/java/com/susu/core/model/EnvelopeStatics.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.susu.core.model - -data class EnvelopeStatics( - val friend: Friend = Friend(), - val receivedAmounts: Int = 0, - val sentAmounts: Int = 0, - val totalAmounts: Int = 0, -) diff --git a/core/model/src/main/java/com/susu/core/model/Friend.kt b/core/model/src/main/java/com/susu/core/model/Friend.kt index 763033af..9c924f02 100644 --- a/core/model/src/main/java/com/susu/core/model/Friend.kt +++ b/core/model/src/main/java/com/susu/core/model/Friend.kt @@ -1,10 +1,10 @@ package com.susu.core.model data class Friend( - val id: Int = 0, -// val uid: Int = 0, + val id: Long = 0, + val uid: Long = 0, val name: String = "", val phoneNumber: String = "", -// val createdAt: String = "", -// val modifiedAt: String = "", + val createdAt: String = "", + val modifiedAt: String = "", ) diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index b6cc11b1..b5c8866c 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -3,7 +3,6 @@ package com.susu.data.data.repository import com.susu.core.model.FriendStatistics import com.susu.data.remote.api.EnvelopesService import com.susu.core.model.Envelope -import com.susu.core.model.EnvelopeStatics import com.susu.core.model.Relationship import com.susu.data.remote.model.request.CategoryRequest import com.susu.data.remote.model.request.EnvelopeRequest diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt index 371022ad..2881fbdd 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt @@ -1,7 +1,6 @@ package com.susu.data.remote.model.response import com.susu.core.model.FriendStatistics -import com.susu.core.model.EnvelopeStatics import com.susu.core.model.Friend import kotlinx.serialization.Serializable @@ -15,7 +14,7 @@ data class EnvelopesResponse( @Serializable data class FriendInfo( - val id: Int, + val id: Long, val name: String, val phoneNumber: String, ) diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index 41ca776e..75d57ddc 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -3,7 +3,7 @@ package com.susu.domain.repository import com.susu.core.model.FriendStatistics import com.susu.core.model.Envelope import com.susu.core.model.Relationship -import java.time.LocalDateTime +import kotlinx.datetime.LocalDateTime interface EnvelopesRepository { suspend fun getEnvelopesList( From a2e69dbfafd50bc2c1a78ff0ab0f34ef13e22ef6 Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sun, 28 Jan 2024 22:56:48 +0900 Subject: [PATCH 06/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B4=EC=9A=94=20?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=20=ED=86=B5=EC=8B=A0=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/susu/core/model/Category.kt | 1 + .../main/java/com/susu/core/model/Envelope.kt | 2 +- .../com/susu/core/model/EnvelopeSearch.kt | 8 +++ .../java/com/susu/core/model/Relationship.kt | 1 + .../repository/EnvelopesRepositoryImpl.kt | 23 ++++++++ .../susu/data/remote/api/EnvelopesService.kt | 14 +++++ .../response/EnvelopesHistoryListResponse.kt | 19 ++++++ .../model/response/EnvelopesListResponse.kt | 4 +- .../data/remote/model/response/FriendInfo.kt | 17 ++++++ ...esponse.kt => FriendStatisticsResponse.kt} | 18 +----- .../model/response/SearchEnvelopeResponse.kt | 59 +++++++++++++++++++ .../domain/repository/EnvelopesRepository.kt | 13 ++++ .../GetEnvelopesHistoryListUseCase.kt | 37 ++++++++++++ .../com/susu/feature/sent/SentViewModel.kt | 10 ++++ 14 files changed, 207 insertions(+), 19 deletions(-) create mode 100644 core/model/src/main/java/com/susu/core/model/EnvelopeSearch.kt create mode 100644 data/src/main/java/com/susu/data/remote/model/response/EnvelopesHistoryListResponse.kt create mode 100644 data/src/main/java/com/susu/data/remote/model/response/FriendInfo.kt rename data/src/main/java/com/susu/data/remote/model/response/{EnvelopesResponse.kt => FriendStatisticsResponse.kt} (53%) create mode 100644 data/src/main/java/com/susu/data/remote/model/response/SearchEnvelopeResponse.kt create mode 100644 domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesHistoryListUseCase.kt diff --git a/core/model/src/main/java/com/susu/core/model/Category.kt b/core/model/src/main/java/com/susu/core/model/Category.kt index 8919864d..c45ff258 100644 --- a/core/model/src/main/java/com/susu/core/model/Category.kt +++ b/core/model/src/main/java/com/susu/core/model/Category.kt @@ -9,6 +9,7 @@ data class Category( val id: Int = 0, val seq: Int = 0, val name: String = "", + val category: String = "", val customCategory: String? = null, val style: String = "", ) diff --git a/core/model/src/main/java/com/susu/core/model/Envelope.kt b/core/model/src/main/java/com/susu/core/model/Envelope.kt index a84cb3bc..faa3b5e7 100644 --- a/core/model/src/main/java/com/susu/core/model/Envelope.kt +++ b/core/model/src/main/java/com/susu/core/model/Envelope.kt @@ -6,7 +6,7 @@ data class Envelope( val id: Long, val uid: Long, val type: String, - val friend: Friend, + val friend: Friend? = null, val amount: Long, val gift: String? = null, val memo: String? = null, diff --git a/core/model/src/main/java/com/susu/core/model/EnvelopeSearch.kt b/core/model/src/main/java/com/susu/core/model/EnvelopeSearch.kt new file mode 100644 index 00000000..22392430 --- /dev/null +++ b/core/model/src/main/java/com/susu/core/model/EnvelopeSearch.kt @@ -0,0 +1,8 @@ +package com.susu.core.model + +data class EnvelopeSearch( + val envelope: Envelope, + val category: Category? = null, + val friend: Friend? = null, + val relationship: Relationship? = null, +) diff --git a/core/model/src/main/java/com/susu/core/model/Relationship.kt b/core/model/src/main/java/com/susu/core/model/Relationship.kt index b4d8589b..847ad8f8 100644 --- a/core/model/src/main/java/com/susu/core/model/Relationship.kt +++ b/core/model/src/main/java/com/susu/core/model/Relationship.kt @@ -7,4 +7,5 @@ data class Relationship( val id: Int = -1, val relation: String = "", val customRelation: String? = null, + val description: String? = null, ) diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index b5c8866c..f87cdf6d 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -3,6 +3,7 @@ package com.susu.data.data.repository import com.susu.core.model.FriendStatistics import com.susu.data.remote.api.EnvelopesService import com.susu.core.model.Envelope +import com.susu.core.model.EnvelopeSearch import com.susu.core.model.Relationship import com.susu.data.remote.model.request.CategoryRequest import com.susu.data.remote.model.request.EnvelopeRequest @@ -65,4 +66,26 @@ class EnvelopesRepositoryImpl @Inject constructor( }, ), ).getOrThrow().toModel() + + override suspend fun getEnvelopesHistoryList( + friendIds: List?, + ledgerId: Int?, + type: List?, + include: List?, + fromAmount: Int?, + toAmount: Int?, + page: Int?, + size: Int?, + sort: String?, + ): List = envelopesService.getEnvelopesHistoryList( + friendIds = friendIds, + ledgerId = ledgerId, + types = type, + include = include, + fromAmount = fromAmount, + toAmount = toAmount, + page = page, + size = size, + sort = sort, + ).getOrThrow().toModel() } diff --git a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt index 5706c4e4..e547ba84 100644 --- a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt +++ b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt @@ -4,6 +4,7 @@ import com.susu.data.remote.model.response.EnvelopesListResponse import com.susu.data.remote.retrofit.ApiResult import com.susu.data.remote.model.request.EnvelopeRequest import com.susu.data.remote.model.response.EnvelopeResponse +import com.susu.data.remote.model.response.EnvelopesHistoryListResponse import com.susu.data.remote.model.response.RelationShipListResponse import retrofit2.http.Body import retrofit2.http.GET @@ -28,4 +29,17 @@ interface EnvelopesService { suspend fun createEnvelope( @Body envelopeRequest: EnvelopeRequest, ): ApiResult + + @GET("envelopes") + suspend fun getEnvelopesHistoryList( + @Query("friendIds") friendIds: List?, + @Query("ledgerId") ledgerId: Int?, + @Query("types") types: List?, + @Query("include") include: List?, + @Query("fromAmount") fromAmount: Int?, + @Query("toAmount") toAmount: Int?, + @Query("page") page: Int?, + @Query("size") size: Int?, + @Query("sort") sort: String?, + ): ApiResult } diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesHistoryListResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesHistoryListResponse.kt new file mode 100644 index 00000000..3c83b8aa --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesHistoryListResponse.kt @@ -0,0 +1,19 @@ +package com.susu.data.remote.model.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class EnvelopesHistoryListResponse( + @SerialName("data") + val envelopesHistoryList: List, + val page: Int = 0, + val size: Int, + val sort: Sort, + val totalCount: Int, + val totalPage: Int +) + +internal fun EnvelopesHistoryListResponse.toModel() = this.envelopesHistoryList.map { envelopesHistory -> + envelopesHistory.toModel() +} diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt index 4455ebe8..8d49e1f8 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable @Serializable data class EnvelopesListResponse( @SerialName("data") - val envelopesList: List, + val envelopesList: List, val page: Int, val size: Int, val totalPage: Int, @@ -18,7 +18,7 @@ data class EnvelopesListResponse( data class Sort( val empty: Boolean, val sorted: Boolean, - val unsorted: Boolean, + val unsorted: Boolean ) internal fun EnvelopesListResponse.toModel() = this.envelopesList.map { envelopes -> diff --git a/data/src/main/java/com/susu/data/remote/model/response/FriendInfo.kt b/data/src/main/java/com/susu/data/remote/model/response/FriendInfo.kt new file mode 100644 index 00000000..f75f465e --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/FriendInfo.kt @@ -0,0 +1,17 @@ +package com.susu.data.remote.model.response + +import com.susu.core.model.Friend +import kotlinx.serialization.Serializable + +@Serializable +data class FriendInfo( + val id: Long, + val name: String, + val phoneNumber: String = "", +) + +internal fun FriendInfo.toModel() = Friend( + id = id, + name = name, + phoneNumber = phoneNumber, +) diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/FriendStatisticsResponse.kt similarity index 53% rename from data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt rename to data/src/main/java/com/susu/data/remote/model/response/FriendStatisticsResponse.kt index 2881fbdd..c757c548 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/FriendStatisticsResponse.kt @@ -1,31 +1,17 @@ package com.susu.data.remote.model.response import com.susu.core.model.FriendStatistics -import com.susu.core.model.Friend import kotlinx.serialization.Serializable @Serializable -data class EnvelopesResponse( +data class FriendStatisticsResponse( val friend: FriendInfo, val receivedAmounts: Int, val sentAmounts: Int, val totalAmounts: Int, ) -@Serializable -data class FriendInfo( - val id: Long, - val name: String, - val phoneNumber: String, -) - -internal fun FriendInfo.toModel() = Friend( - id = id, - name = name, - phoneNumber = phoneNumber, -) - -internal fun EnvelopesResponse.toModel() = FriendStatistics( +internal fun FriendStatisticsResponse.toModel() = FriendStatistics( friend = friend.toModel(), receivedAmounts = receivedAmounts, sentAmounts = sentAmounts, diff --git a/data/src/main/java/com/susu/data/remote/model/response/SearchEnvelopeResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/SearchEnvelopeResponse.kt new file mode 100644 index 00000000..0d79d3bf --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/SearchEnvelopeResponse.kt @@ -0,0 +1,59 @@ +package com.susu.data.remote.model.response + +import com.susu.core.model.Envelope +import com.susu.core.model.EnvelopeSearch +import com.susu.core.model.Relationship +import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.toJavaLocalDateTime +import kotlinx.serialization.Serializable + +@Serializable +data class SearchEnvelopeResponse( + val envelope: EnvelopeInfo, + val category: CategoryInfo? = null, + val friend: FriendInfo? = null, + val relation: RelationshipInfo? = null, +) + +@Serializable +data class EnvelopeInfo( + val id: Long, + val uid: Long, + val type: String, + val amount: Long, + val gift: String? = null, + val memo: String? = null, + val hasVisited: Boolean? = null, + val handedOverAt: LocalDateTime? = null, +) + +@Serializable +data class RelationshipInfo( + val id: Int, + val relation: String, + val description: String = "", +) + +internal fun EnvelopeInfo.toModel() = Envelope( + id = id, + uid = uid, + type = type, + amount = amount, + gift = gift, + memo = memo, + hasVisited = hasVisited, + handedOverAt = handedOverAt?.toJavaLocalDateTime(), +) + +internal fun RelationshipInfo.toModel() = Relationship( + id = id, + relation = relation, + description = description, +) + +internal fun SearchEnvelopeResponse.toModel() = EnvelopeSearch( + envelope = envelope.toModel(), + category = category?.toModel(), + friend = friend?.toModel(), + relationship = relation?.toModel(), +) diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index 75d57ddc..9345f20f 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -2,6 +2,7 @@ package com.susu.domain.repository import com.susu.core.model.FriendStatistics import com.susu.core.model.Envelope +import com.susu.core.model.EnvelopeSearch import com.susu.core.model.Relationship import kotlinx.datetime.LocalDateTime @@ -29,4 +30,16 @@ interface EnvelopesRepository { categoryId: Long? = null, customCategory: String? = null, ): Envelope + + suspend fun getEnvelopesHistoryList( + friendIds: List?, + ledgerId: Int?, + type: List?, + include: List?, + fromAmount: Int?, + toAmount: Int?, + page: Int?, + size: Int?, + sort: String?, + ): List } diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesHistoryListUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesHistoryListUseCase.kt new file mode 100644 index 00000000..525a4cd6 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesHistoryListUseCase.kt @@ -0,0 +1,37 @@ +package com.susu.domain.usecase.envelope + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.EnvelopesRepository +import javax.inject.Inject + +class GetEnvelopesHistoryListUseCase @Inject constructor( + private val envelopesRepository: EnvelopesRepository, +) { + suspend operator fun invoke(param: Param) = runCatchingIgnoreCancelled { + with(param) { + envelopesRepository.getEnvelopesHistoryList( + friendIds = friendIds, + ledgerId = ledgerId, + type = type, + include = include, + fromAmount = fromAmount, + toAmount = toAmount, + page = page, + size = size, + sort = sort, + ) + } + } + + data class Param( + val friendIds: List? = emptyList(), + val ledgerId: Int? = null, + val type: List? = emptyList(), + val include: List? = emptyList(), + val fromAmount: Int? = null, + val toAmount: Int? = null, + val page: Int? = null, + val size: Int? = null, + val sort: String? = null, + ) +} diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt index d9d3d205..1be88569 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt @@ -2,6 +2,7 @@ package com.susu.feature.sent import androidx.lifecycle.viewModelScope import com.susu.core.ui.base.BaseViewModel +import com.susu.domain.usecase.envelope.GetEnvelopesHistoryListUseCase import com.susu.domain.usecase.envelope.GetEnvelopesListUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList @@ -11,6 +12,7 @@ import javax.inject.Inject @HiltViewModel class SentViewModel @Inject constructor( private val getEnvelopesListUseCase: GetEnvelopesListUseCase, + private val getEnvelopesHistoryListUseCase: GetEnvelopesHistoryListUseCase, ) : BaseViewModel( SentState(), ) { @@ -31,6 +33,14 @@ class SentViewModel @Inject constructor( } } + fun getEnvelopesHistoryList() = viewModelScope.launch { + getEnvelopesHistoryListUseCase( + GetEnvelopesHistoryListUseCase.Param(), + ).onSuccess { + // TODO: EnvelopesHistoryList 불러오기 + } + } + fun navigateSentEnvelope() = postSideEffect(SentEffect.NavigateEnvelope) fun navigateSentAdd() = postSideEffect(SentEffect.NavigateEnvelopeAdd) } From df6e5fccf08005e788feb3d7255d6d0f29f2c36c Mon Sep 17 00:00:00 2001 From: syb8200 Date: Wed, 31 Jan 2024 01:06:20 +0900 Subject: [PATCH 07/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B4=EC=9A=94=20?= =?UTF-8?q?=EB=B4=89=ED=88=AC=20=EC=84=B8=EB=B6=80=20=EB=82=B4=EC=97=AD=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/EnvelopesRepositoryImpl.kt | 6 ++- .../susu/data/remote/api/EnvelopesService.kt | 2 +- .../remote/model/response/LedgerResponse.kt | 1 + .../domain/repository/EnvelopesRepository.kt | 4 +- .../GetEnvelopesHistoryListUseCase.kt | 2 +- .../com/susu/feature/sent/SentContract.kt | 2 + .../java/com/susu/feature/sent/SentScreen.kt | 9 +++- .../com/susu/feature/sent/SentViewModel.kt | 17 ++++++-- .../susu/feature/sent/component/SentCard.kt | 41 +++++++++---------- .../feature/sent/component/SentHistoryCard.kt | 26 ++++++------ .../feature/sent/component/SentHistoryItem.kt | 4 +- 11 files changed, 66 insertions(+), 48 deletions(-) diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index f87cdf6d..a195cd98 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -1,9 +1,11 @@ package com.susu.data.data.repository +import com.susu.core.model.Category import com.susu.core.model.FriendStatistics import com.susu.data.remote.api.EnvelopesService import com.susu.core.model.Envelope import com.susu.core.model.EnvelopeSearch +import com.susu.core.model.Friend import com.susu.core.model.Relationship import com.susu.data.remote.model.request.CategoryRequest import com.susu.data.remote.model.request.EnvelopeRequest @@ -68,7 +70,7 @@ class EnvelopesRepositoryImpl @Inject constructor( ).getOrThrow().toModel() override suspend fun getEnvelopesHistoryList( - friendIds: List?, + friendIds: List?, ledgerId: Int?, type: List?, include: List?, @@ -76,7 +78,7 @@ class EnvelopesRepositoryImpl @Inject constructor( toAmount: Int?, page: Int?, size: Int?, - sort: String?, + sort: String? ): List = envelopesService.getEnvelopesHistoryList( friendIds = friendIds, ledgerId = ledgerId, diff --git a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt index e547ba84..70bb7c5f 100644 --- a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt +++ b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt @@ -32,7 +32,7 @@ interface EnvelopesService { @GET("envelopes") suspend fun getEnvelopesHistoryList( - @Query("friendIds") friendIds: List?, + @Query("friendIds") friendIds: List?, @Query("ledgerId") ledgerId: Int?, @Query("types") types: List?, @Query("include") include: List?, diff --git a/data/src/main/java/com/susu/data/remote/model/response/LedgerResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/LedgerResponse.kt index e3a372fb..855a2dee 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/LedgerResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/LedgerResponse.kt @@ -48,6 +48,7 @@ internal fun CategoryInfo.toModel() = Category( id = id, seq = seq, name = category, + category = category, customCategory = customCategory, style = style, ) diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index 9345f20f..5ecb946e 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -1,8 +1,10 @@ package com.susu.domain.repository +import com.susu.core.model.Category import com.susu.core.model.FriendStatistics import com.susu.core.model.Envelope import com.susu.core.model.EnvelopeSearch +import com.susu.core.model.Friend import com.susu.core.model.Relationship import kotlinx.datetime.LocalDateTime @@ -32,7 +34,7 @@ interface EnvelopesRepository { ): Envelope suspend fun getEnvelopesHistoryList( - friendIds: List?, + friendIds: List?, ledgerId: Int?, type: List?, include: List?, diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesHistoryListUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesHistoryListUseCase.kt index 525a4cd6..783891bf 100644 --- a/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesHistoryListUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesHistoryListUseCase.kt @@ -24,7 +24,7 @@ class GetEnvelopesHistoryListUseCase @Inject constructor( } data class Param( - val friendIds: List? = emptyList(), + val friendIds: List? = emptyList(), val ledgerId: Int? = null, val type: List? = emptyList(), val include: List? = emptyList(), diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt index 5c945d97..fa729c1e 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt @@ -1,5 +1,6 @@ package com.susu.feature.sent +import com.susu.core.model.EnvelopeSearch import com.susu.core.model.FriendStatistics import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState @@ -9,6 +10,7 @@ import kotlinx.collections.immutable.persistentListOf data class SentState( val isLoading: Boolean = false, val envelopesList: PersistentList = persistentListOf(), + val envelopesHistoryList: PersistentList = persistentListOf(), val showEmptyEnvelopes: Boolean = false, ) : UiState diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt index c5af5eff..d7e4bfb3 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt @@ -66,6 +66,9 @@ fun SentRoute( uiState = uiState, envelopesListState = envelopesListState, padding = padding, + onClickHistory = { friendId -> + viewModel.getEnvelopesHistoryList(friendId) + }, onClickHistoryShowAll = viewModel::navigateSentEnvelope, onClickAddEnvelope = viewModel::navigateSentAdd, ) @@ -79,6 +82,7 @@ fun SentScreen( modifier: Modifier = Modifier, onClickSearchIcon: () -> Unit = {}, onClickNotificationIcon: () -> Unit = {}, + onClickHistory: (Long) -> Unit = {}, onClickHistoryShowAll: () -> Unit = {}, onClickAddEnvelope: () -> Unit = {}, ) { @@ -119,13 +123,14 @@ fun SentScreen( items = uiState.envelopesList, key = { it.friend.id }, ) { - // 전체 보기 버튼 SentCard( + uiState = uiState, friend = it.friend, totalAmounts = it.totalAmounts, sentAmounts = it.sentAmounts, receivedAmounts = it.receivedAmounts, - onClick = onClickHistoryShowAll, + onClickHistory = onClickHistory, + onClickHistoryShowAll = onClickHistoryShowAll, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt index 1be88569..09bdd9cc 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt @@ -33,11 +33,20 @@ class SentViewModel @Inject constructor( } } - fun getEnvelopesHistoryList() = viewModelScope.launch { + fun getEnvelopesHistoryList(id: Long) = viewModelScope.launch { + val friendsList: List = listOf(id) + val includeList = listOf("CATEGORY", "FRIEND") + getEnvelopesHistoryListUseCase( - GetEnvelopesHistoryListUseCase.Param(), - ).onSuccess { - // TODO: EnvelopesHistoryList 불러오기 + GetEnvelopesHistoryListUseCase.Param(friendIds = friendsList, include = includeList), + ).onSuccess { history -> + val envelopesHistorySubList = if (history.size < 3) history else history.take(3) + val newEnvelopesHistoryList = envelopesHistorySubList.toPersistentList() + intent { + copy( + envelopesHistoryList = newEnvelopesHistoryList, + ) + } } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt index 36b7b090..e8b642c5 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import com.susu.core.designsystem.component.badge.BadgeColor import com.susu.core.designsystem.component.badge.BadgeStyle import com.susu.core.designsystem.component.badge.SusuBadge @@ -44,21 +45,25 @@ import com.susu.core.designsystem.theme.Gray60 import com.susu.core.designsystem.theme.Gray90 import com.susu.core.designsystem.theme.Orange20 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.EnvelopeSearch import com.susu.core.model.Friend import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.sent.R +import com.susu.feature.sent.SentState +import com.susu.feature.sent.SentViewModel @Composable fun SentCard( + uiState: SentState = SentState(), modifier: Modifier = Modifier, - friend: Friend = Friend(), - totalAmounts: Int, - sentAmounts: Int, - receivedAmounts: Int, - onClick: () -> Unit = {}, + friend: Friend, + totalAmounts: Int = 0, + sentAmounts: Int = 0, + receivedAmounts: Int = 0, + onClickHistory: (Long) -> Unit = {}, + onClickHistoryShowAll: () -> Unit = {}, ) { - // TODO: 수정 필요 var expanded by remember { mutableStateOf(false) } val degrees by animateFloatAsState(if (expanded) 180f else 0f, label = "") @@ -99,9 +104,12 @@ fun SentCard( tint = Gray100, modifier = modifier .clip(CircleShape) - .susuClickable { - expanded = !expanded - } + .susuClickable( + onClick = { + expanded = !expanded + onClickHistory(friend.id) + } + ) .rotate(degrees = degrees), ) } @@ -157,19 +165,8 @@ fun SentCard( exit = fadeOut() + shrinkVertically(), ) { SentHistoryCard( - onClick = onClick, - ) - } -} - -@Preview -@Composable -fun SentCardPreview() { - SusuTheme { - SentCard( - totalAmounts = 100000, - sentAmounts = 70000, - receivedAmounts = 30000, + envelopeHistoryList = uiState.envelopesHistoryList, + onClickHistoryShowAll = onClickHistoryShowAll, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt index 47ec5798..3653ddf8 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt @@ -20,13 +20,15 @@ import com.susu.core.designsystem.component.button.XSmallButtonStyle import com.susu.core.designsystem.theme.Gray10 import com.susu.core.designsystem.theme.Gray20 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.EnvelopeSearch import com.susu.feature.sent.R +import kotlinx.collections.immutable.PersistentList @Composable fun SentHistoryCard( modifier: Modifier = Modifier, - historyCount: Int = 3, - onClick: () -> Unit = {}, + envelopeHistoryList: PersistentList, + onClickHistoryShowAll: () -> Unit = {}, ) { Card( modifier = modifier @@ -50,10 +52,16 @@ fun SentHistoryCard( end = SusuTheme.spacing.spacing_m, ), ) { - repeat(historyCount) { - SentHistoryItem() // TODO: id에 해당하는 envelopeInfo 넣어주기 + envelopeHistoryList.forEach { + SentHistoryItem( + type = it.envelope.type, + event = it.category!!.category, + date = it.envelope.handedOverAt!!, + money = it.envelope.amount, + ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_xxs)) } + Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_xxs)) SusuFilledButton( color = FilledButtonColor.Black, @@ -61,16 +69,8 @@ fun SentHistoryCard( text = stringResource(R.string.sent_screen_envelope_history_show_all), modifier = modifier .fillMaxWidth(), - onClick = onClick, + onClick = onClickHistoryShowAll, ) } } } - -@Preview -@Composable -fun SentHistoryCardPreview() { - SusuTheme { - SentHistoryCard() - } -} diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt index 14cbc92c..ccabe186 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt @@ -34,7 +34,7 @@ fun SentHistoryItem( type: String = "", event: String = "", date: LocalDateTime = LocalDateTime.now(), - money: Int = 0, + money: Long = 0, ) { Row( modifier = modifier.fillMaxWidth(), @@ -67,7 +67,7 @@ fun SentHistoryItem( ) Spacer(modifier = modifier.weight(1f)) Text( - text = "${money.toMoneyFormat()}원", + text = "${money.toInt().toMoneyFormat()}원", style = SusuTheme.typography.title_xxs, color = if (type == EnvelopeType.SENT.name) Gray100 else Gray50, ) From 642297922cfc22b692cd3513f1e231c35db080a8 Mon Sep 17 00:00:00 2001 From: syb8200 Date: Wed, 31 Jan 2024 01:11:56 +0900 Subject: [PATCH 08/95] =?UTF-8?q?chore:=20string=20=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/susu/feature/sent/component/SentCard.kt | 10 +++------- .../com/susu/feature/sent/component/SentHistoryCard.kt | 1 - feature/sent/src/main/res/values/strings.xml | 2 ++ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt index e8b642c5..90d8894e 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt @@ -34,9 +34,7 @@ import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import com.susu.core.designsystem.component.badge.BadgeColor import com.susu.core.designsystem.component.badge.BadgeStyle import com.susu.core.designsystem.component.badge.SusuBadge @@ -45,13 +43,11 @@ import com.susu.core.designsystem.theme.Gray60 import com.susu.core.designsystem.theme.Gray90 import com.susu.core.designsystem.theme.Orange20 import com.susu.core.designsystem.theme.SusuTheme -import com.susu.core.model.EnvelopeSearch import com.susu.core.model.Friend import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.sent.R import com.susu.feature.sent.SentState -import com.susu.feature.sent.SentViewModel @Composable fun SentCard( @@ -94,7 +90,7 @@ fun SentCard( Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_s)) SusuBadge( color = BadgeColor.Gray20, - text = "전체 ${totalAmounts.toMoneyFormat()}", + text = stringResource(R.string.sent_envelope_card_monee_total) + totalAmounts.toMoneyFormat(), padding = BadgeStyle.smallBadge, ) Spacer(modifier = modifier.weight(1f)) @@ -147,12 +143,12 @@ fun SentCard( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = "${sentAmounts.toMoneyFormat()}원", + text = sentAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxxs, color = Gray90, ) Text( - text = "${receivedAmounts.toMoneyFormat()}원", + text = receivedAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxxs, color = Gray60, ) diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt index 3653ddf8..2bf09123 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt @@ -12,7 +12,6 @@ import androidx.compose.material3.CardDefaults import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.SusuFilledButton diff --git a/feature/sent/src/main/res/values/strings.xml b/feature/sent/src/main/res/values/strings.xml index a1ce7b4d..2e7bbdcf 100644 --- a/feature/sent/src/main/res/values/strings.xml +++ b/feature/sent/src/main/res/values/strings.xml @@ -53,4 +53,6 @@ 님에게 "님의 " "을 " + "전체 " + From 36f7ccf24ca2b3fecc638abe14a6eb7c6657b574 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 10:09:35 +0900 Subject: [PATCH 09/95] =?UTF-8?q?feat:=20=EB=B4=89=ED=88=AC=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/susu/core/model/Envelope.kt | 2 + .../main/java/com/susu/core/model/Friend.kt | 2 + .../java/com/susu/core/ui/extension/Long.kt | 8 +++ core/ui/src/main/res/values/strings.xml | 3 ++ .../ReceivedEnvelopeDetailContract.kt | 2 +- .../ReceivedEnvelopeDetailScreen.kt | 53 ++++++++++++------- .../ReceivedEnvelopeDetailViewModel.kt | 29 ++++------ .../ledgerdetail/LedgerDetailScreen.kt | 1 + .../ledgerdetail/LedgerDetailViewModel.kt | 20 +++++++ .../received/navigation/ReceivedNavigation.kt | 1 + 10 files changed, 82 insertions(+), 39 deletions(-) create mode 100644 core/ui/src/main/java/com/susu/core/ui/extension/Long.kt diff --git a/core/model/src/main/java/com/susu/core/model/Envelope.kt b/core/model/src/main/java/com/susu/core/model/Envelope.kt index 4715c06c..37c71fed 100644 --- a/core/model/src/main/java/com/susu/core/model/Envelope.kt +++ b/core/model/src/main/java/com/susu/core/model/Envelope.kt @@ -1,8 +1,10 @@ package com.susu.core.model +import androidx.compose.runtime.Stable import kotlinx.datetime.LocalDateTime import kotlinx.serialization.Serializable +@Stable @Serializable data class Envelope( val id: Long = 0, diff --git a/core/model/src/main/java/com/susu/core/model/Friend.kt b/core/model/src/main/java/com/susu/core/model/Friend.kt index b2676e4b..909e2f03 100644 --- a/core/model/src/main/java/com/susu/core/model/Friend.kt +++ b/core/model/src/main/java/com/susu/core/model/Friend.kt @@ -1,7 +1,9 @@ package com.susu.core.model +import androidx.compose.runtime.Stable import kotlinx.serialization.Serializable +@Stable @Serializable data class Friend( val id: Long = 0, diff --git a/core/ui/src/main/java/com/susu/core/ui/extension/Long.kt b/core/ui/src/main/java/com/susu/core/ui/extension/Long.kt new file mode 100644 index 00000000..a3cb638d --- /dev/null +++ b/core/ui/src/main/java/com/susu/core/ui/extension/Long.kt @@ -0,0 +1,8 @@ +package com.susu.core.ui.extension + +import java.text.DecimalFormat + +fun Long.toMoneyFormat(): String { + // DecimalFormat은 Thread Safe하지 않으므로 지역 변수로 사용함. + return DecimalFormat("#,###").format(this) +} diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index be364da8..9b183d82 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -49,4 +49,7 @@ 전체 방문 미방문 + 나와의 관계 + 방문 여부 + 선물 diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt index d8491bd6..14f49e23 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt @@ -5,7 +5,7 @@ import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState data class ReceivedEnvelopeDetailState( - val money: Long = 0, + val envelope: Envelope = Envelope(), ) : UiState sealed interface ReceivedEnvelopeDetailSideEffect : SideEffect { diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt index e68f1dd2..ec4d41ac 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt @@ -1,5 +1,6 @@ package com.susu.feature.received.envelopedetail +import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -14,6 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -26,6 +28,7 @@ import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.DialogToken import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle +import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.received.R import com.susu.feature.received.envelopedetail.component.DetailItem @@ -33,6 +36,7 @@ import com.susu.feature.received.envelopedetail.component.DetailItem fun ReceivedEnvelopeDetailRoute( viewModel: ReceivedEnvelopeDetailViewModel = hiltViewModel(), popBackStackWithDeleteReceivedEnvelopeId: (Long) -> Unit, + popBackStackWithReceivedEnvelope: (String) -> Unit, navigateReceivedEnvelopeEdit: () -> Unit, handleException: (Throwable, () -> Unit) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, @@ -47,7 +51,8 @@ fun ReceivedEnvelopeDetailRoute( is ReceivedEnvelopeDetailSideEffect.PopBackStackWithDeleteReceivedEnvelopeId -> popBackStackWithDeleteReceivedEnvelopeId( sideEffect.envelopeId, ) - is ReceivedEnvelopeDetailSideEffect.PopBackStackWithReceivedEnvelope -> TODO() + + is ReceivedEnvelopeDetailSideEffect.PopBackStackWithReceivedEnvelope -> popBackStackWithReceivedEnvelope(sideEffect.envelope) is ReceivedEnvelopeDetailSideEffect.ShowDeleteDialog -> onShowDialog( DialogToken( title = context.getString(R.string.dialog_delete_envelope_title), @@ -57,13 +62,19 @@ fun ReceivedEnvelopeDetailRoute( onConfirmRequest = sideEffect.onConfirmRequest, ), ) + ReceivedEnvelopeDetailSideEffect.ShowDeleteSuccessSnackbar -> onShowSnackbar( SnackbarToken(message = context.getString(R.string.toast_delete_envelope_success)), ) + is ReceivedEnvelopeDetailSideEffect.ShowSnackbar -> TODO() } } + BackHandler { + viewModel.popBackStackWithEnvelope() + } + LaunchedEffect(key1 = Unit) { viewModel.getEnvelope() } @@ -72,12 +83,12 @@ fun ReceivedEnvelopeDetailRoute( uiState = uiState, onClickEdit = navigateReceivedEnvelopeEdit, onClickDelete = viewModel::showDeleteDialog, + onClickBackIcon = viewModel::popBackStackWithEnvelope, ) } @Composable fun ReceivedEnvelopeDetailScreen( - @Suppress("detekt:UnusedParameter") uiState: ReceivedEnvelopeDetailState = ReceivedEnvelopeDetailState(), onClickBackIcon: () -> Unit = {}, onClickEdit: () -> Unit = {}, @@ -107,7 +118,7 @@ fun ReceivedEnvelopeDetailScreen( Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) }, ) - // TODO: text 수정 + Column( modifier = Modifier .fillMaxSize() @@ -119,41 +130,43 @@ fun ReceivedEnvelopeDetailScreen( .verticalScroll(scrollState), ) { Text( - text = "150,000원", + text = stringResource(id = com.susu.core.ui.R.string.money_unit_format, uiState.envelope.amount.toMoneyFormat()), style = SusuTheme.typography.title_xxl, color = Gray100, ) Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) Column { DetailItem( - categoryText = "이름", - contentText = "김철수", + categoryText = stringResource(id = com.susu.core.ui.R.string.word_name), + contentText = uiState.envelope.friend.name, isEmptyContent = false, ) DetailItem( - categoryText = "나와의 관계", - contentText = "친구", + categoryText = stringResource(com.susu.core.ui.R.string.word_relationship), + contentText = uiState.envelope.relationship.customRelation ?: uiState.envelope.relationship.relation, isEmptyContent = false, ) DetailItem( - categoryText = "방문 여부", - contentText = "예", - isEmptyContent = false, + categoryText = stringResource(com.susu.core.ui.R.string.word_is_visited), + contentText = if (uiState.envelope.hasVisited == true) stringResource(id = com.susu.core.ui.R.string.word_yes) else stringResource( + id = com.susu.core.ui.R.string.word_no, + ), + isEmptyContent = uiState.envelope.hasVisited == null, ) DetailItem( - categoryText = "선물", - contentText = "한끼 식사", - isEmptyContent = false, + categoryText = stringResource(com.susu.core.ui.R.string.word_gift), + contentText = uiState.envelope.gift ?: "", + isEmptyContent = uiState.envelope.gift == null, ) DetailItem( - categoryText = "연락처", - contentText = "01012345678", - isEmptyContent = false, + categoryText = stringResource(id = com.susu.core.ui.R.string.word_phone_number), + contentText = uiState.envelope.friend.phoneNumber, + isEmptyContent = uiState.envelope.friend.phoneNumber.isEmpty(), ) DetailItem( - categoryText = "메모", - contentText = "가나다라마바사아자차카타파하가나다라마바사아자차카타파하가나다라마바사아자차카타파하가나다라마바사아자차카타파하가나다라마바사아자차카타파하", - isEmptyContent = false, + categoryText = stringResource(id = com.susu.core.ui.R.string.word_memo), + contentText = uiState.envelope.memo ?: "", + isEmptyContent = uiState.envelope.memo == null, ) } } diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt index 5fe92118..50367c6a 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt @@ -7,6 +7,7 @@ import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri import com.susu.core.ui.extension.encodeToUri import com.susu.domain.usecase.envelope.DeleteEnvelopeUseCase +import com.susu.domain.usecase.envelope.GetEnvelopeUseCase import com.susu.feature.received.navigation.ReceivedRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch @@ -16,6 +17,7 @@ import javax.inject.Inject @HiltViewModel class ReceivedEnvelopeDetailViewModel @Inject constructor( + private val getEnvelopeUseCase: GetEnvelopeUseCase, private val deleteEnvelopeUseCase: DeleteEnvelopeUseCase, savedStateHandle: SavedStateHandle, ) : BaseViewModel( @@ -26,24 +28,15 @@ class ReceivedEnvelopeDetailViewModel @Inject constructor( fun getEnvelope() = viewModelScope.launch { envelope = Json.decodeFromUri(argument) - Timber.tag("테스트").d("$envelope") -// getLedgerUseCase(id = envelope.id) -// .onSuccess { ledger -> -// this@ReceivedReceivedEnvelopeDetailViewModel.envelope = ledger -// intent { -// with(ledger) { -// val category = ledger.category -// copy( -// name = ledger.title, -// money = ledger.totalAmounts, -// count = ledger.totalCounts, -// category = if (category.customCategory.isNullOrEmpty()) category.name else category.customCategory!!, -// startDate = ledger.startAt.toJavaLocalDateTime().to_yyyy_dot_MM_dot_dd(), -// endDate = ledger.endAt.toJavaLocalDateTime().to_yyyy_dot_MM_dot_dd(), -// ) -// } -// } -// } + getEnvelopeUseCase(id = envelope.id) + .onSuccess { envelope -> + this@ReceivedEnvelopeDetailViewModel.envelope = envelope + intent { + copy( + envelope = envelope + ) + } + } } fun navigateEnvelopeEdit() = postSideEffect(ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit(envelope)) diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt index cd3a2343..ab760394 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt @@ -106,6 +106,7 @@ fun LedgerDetailRoute( viewModel.initReceivedEnvelopeList() viewModel.addEnvelopeIfNeed(envelope) viewModel.deleteEnvelopeIfNeed(toDeleteEnvelopeId) + viewModel.updateEnvelopeIfNeed(envelope) } listState.OnBottomReached(minItemsCount = 4) { diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt index a28ee6d5..35a1ea41 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt @@ -56,6 +56,25 @@ class LedgerDetailViewModel @Inject constructor( ) } + fun updateEnvelopeIfNeed(envelopeUri: String?) = intent { + val envelope = envelopeUri?.let { + Json.decodeFromUri(it) + } ?: return@intent this + + val searchEnvelope = SearchEnvelope( + envelope = envelope, + friend = envelope.friend, + relation = envelope.relationship, + ) + + copy( + envelopeList = envelopeList.map { + if (it.envelope.id == searchEnvelope.envelope.id) searchEnvelope + else it + }.toPersistentList(), + ) + } + fun deleteEnvelopeIfNeed(toDeleteEnvelopeId: Long?) { if (toDeleteEnvelopeId == null) return @@ -155,5 +174,6 @@ class LedgerDetailViewModel @Inject constructor( fun navigateEnvelopeAdd() = postSideEffect( LedgerDetailSideEffect.NavigateEnvelopeAdd(ledger.category.customCategory ?: ledger.category.name, ledger.id), ) + fun navigateEnvelopeDetail(envelope: Envelope) = postSideEffect(LedgerDetailSideEffect.NavigateEnvelopeDetail(envelope)) } diff --git a/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt b/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt index 9804c948..fe87b0de 100644 --- a/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt +++ b/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt @@ -180,6 +180,7 @@ fun NavGraphBuilder.receivedNavGraph( ) { ReceivedEnvelopeDetailRoute( popBackStackWithDeleteReceivedEnvelopeId = popBackStackWithDeleteReceivedEnvelopeId, + popBackStackWithReceivedEnvelope = popBackStackWithEnvelope, navigateReceivedEnvelopeEdit = navigateEnvelopeEdit, onShowSnackbar = onShowSnackbar, onShowDialog = onShowDialog, From 301fb2bef050a85e953675f1e0955e2b75c37b1c Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 10:42:28 +0900 Subject: [PATCH 10/95] =?UTF-8?q?fix:=20=EB=B3=B4=EB=82=B8=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=ED=8E=BC=EC=B9=A8=20=EC=83=81=ED=83=9C=EA=B0=80=20?= =?UTF-8?q?=EC=9C=A0=EC=A7=80=20=EC=95=88=EB=90=98=EB=8A=94=20=ED=98=84?= =?UTF-8?q?=EC=83=81,=20=EB=B3=B4=EB=82=B8=20=EB=B4=89=ED=88=AC=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EB=82=B4=EC=97=AD=EC=9D=B4=20=EC=A0=9C?= =?UTF-8?q?=EB=8C=80=EB=A1=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/feature/sent/SentContract.kt | 20 +++++++++++++++++-- .../java/com/susu/feature/sent/SentScreen.kt | 4 ++-- .../com/susu/feature/sent/SentViewModel.kt | 10 ++++++++-- .../susu/feature/sent/component/SentCard.kt | 9 ++++----- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt index fa729c1e..9ef3232a 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt @@ -1,6 +1,7 @@ package com.susu.feature.sent import com.susu.core.model.EnvelopeSearch +import com.susu.core.model.Friend import com.susu.core.model.FriendStatistics import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState @@ -9,11 +10,26 @@ import kotlinx.collections.immutable.persistentListOf data class SentState( val isLoading: Boolean = false, - val envelopesList: PersistentList = persistentListOf(), - val envelopesHistoryList: PersistentList = persistentListOf(), + val envelopesList: PersistentList = persistentListOf(), val showEmptyEnvelopes: Boolean = false, ) : UiState +data class FriendStatisticsState( + val friend: Friend = Friend(), + val receivedAmounts: Int = 0, + val sentAmounts: Int = 0, + val totalAmounts: Int = 0, + val envelopesHistoryList: PersistentList = persistentListOf(), + val expand: Boolean = false, +) + +internal fun FriendStatistics.toState() = FriendStatisticsState( + friend = friend, + receivedAmounts = receivedAmounts, + sentAmounts = sentAmounts, + totalAmounts = totalAmounts, +) + sealed interface SentEffect : SideEffect { data object NavigateEnvelope : SentEffect data object NavigateEnvelopeAdd : SentEffect diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt index d7e4bfb3..592456ac 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt @@ -76,10 +76,10 @@ fun SentRoute( @Composable fun SentScreen( + modifier: Modifier = Modifier, uiState: SentState = SentState(), envelopesListState: LazyListState = rememberLazyListState(), padding: PaddingValues, - modifier: Modifier = Modifier, onClickSearchIcon: () -> Unit = {}, onClickNotificationIcon: () -> Unit = {}, onClickHistory: (Long) -> Unit = {}, @@ -124,7 +124,7 @@ fun SentScreen( key = { it.friend.id }, ) { SentCard( - uiState = uiState, + uiState = it, friend = it.friend, totalAmounts = it.totalAmounts, sentAmounts = it.sentAmounts, diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt index 09bdd9cc..1408fcaa 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt @@ -8,6 +8,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import javax.inject.Inject +import kotlin.system.exitProcess @HiltViewModel class SentViewModel @Inject constructor( @@ -23,7 +24,7 @@ class SentViewModel @Inject constructor( GetEnvelopesListUseCase.Param(page = page), ).onSuccess { envelopesList -> page++ - val newEnvelopesList = currentState.envelopesList.plus(envelopesList).toPersistentList() + val newEnvelopesList = currentState.envelopesList.plus(envelopesList.map { it.toState() }).toPersistentList() intent { copy( envelopesList = newEnvelopesList, @@ -44,7 +45,12 @@ class SentViewModel @Inject constructor( val newEnvelopesHistoryList = envelopesHistorySubList.toPersistentList() intent { copy( - envelopesHistoryList = newEnvelopesHistoryList, + envelopesList = envelopesList.map { + if (it.friend.id == id) it.copy( + envelopesHistoryList = newEnvelopesHistoryList, + expand = !it.expand + ) else it + }.toPersistentList(), ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt index e8b642c5..f455b70c 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt @@ -49,14 +49,15 @@ import com.susu.core.model.EnvelopeSearch import com.susu.core.model.Friend import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.extension.toMoneyFormat +import com.susu.feature.sent.FriendStatisticsState import com.susu.feature.sent.R import com.susu.feature.sent.SentState import com.susu.feature.sent.SentViewModel @Composable fun SentCard( - uiState: SentState = SentState(), modifier: Modifier = Modifier, + uiState: FriendStatisticsState = FriendStatisticsState(), friend: Friend, totalAmounts: Int = 0, sentAmounts: Int = 0, @@ -64,8 +65,7 @@ fun SentCard( onClickHistory: (Long) -> Unit = {}, onClickHistoryShowAll: () -> Unit = {}, ) { - var expanded by remember { mutableStateOf(false) } - val degrees by animateFloatAsState(if (expanded) 180f else 0f, label = "") + val degrees by animateFloatAsState(if (uiState.expand) 180f else 0f, label = "") Box( modifier = modifier @@ -106,7 +106,6 @@ fun SentCard( .clip(CircleShape) .susuClickable( onClick = { - expanded = !expanded onClickHistory(friend.id) } ) @@ -160,7 +159,7 @@ fun SentCard( } } AnimatedVisibility( - visible = expanded, + visible = uiState.expand, enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically(), ) { From a4af27c7cd036ceedc339f7397e4fd66bb6ac2db Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 10:55:35 +0900 Subject: [PATCH 11/95] =?UTF-8?q?feat:=20=EB=B0=9B=EC=9D=80=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20-=20=EB=82=A0=EC=A7=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/model/src/main/java/com/susu/core/model/Envelope.kt | 3 ++- core/ui/src/main/java/com/susu/core/ui/util/Date.kt | 9 +++++++++ .../susu/data/remote/model/response/EnvelopeResponse.kt | 2 +- .../envelopedetail/ReceivedEnvelopeDetailScreen.kt | 8 ++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/core/model/src/main/java/com/susu/core/model/Envelope.kt b/core/model/src/main/java/com/susu/core/model/Envelope.kt index 37c71fed..501fcf10 100644 --- a/core/model/src/main/java/com/susu/core/model/Envelope.kt +++ b/core/model/src/main/java/com/susu/core/model/Envelope.kt @@ -2,6 +2,7 @@ package com.susu.core.model import androidx.compose.runtime.Stable import kotlinx.datetime.LocalDateTime +import kotlinx.datetime.toKotlinLocalDateTime import kotlinx.serialization.Serializable @Stable @@ -14,7 +15,7 @@ data class Envelope( val gift: String? = null, val memo: String? = null, val hasVisited: Boolean? = null, - val handedOverAt: LocalDateTime? = null, + val handedOverAt: LocalDateTime = java.time.LocalDateTime.now().toKotlinLocalDateTime(), val friend: Friend = Friend(), val relationship: Relationship = Relationship(), ) diff --git a/core/ui/src/main/java/com/susu/core/ui/util/Date.kt b/core/ui/src/main/java/com/susu/core/ui/util/Date.kt index d0e7fa04..a9973a8e 100644 --- a/core/ui/src/main/java/com/susu/core/ui/util/Date.kt +++ b/core/ui/src/main/java/com/susu/core/ui/util/Date.kt @@ -14,6 +14,15 @@ fun LocalDateTime.to_yyyy_dot_MM_dot_dd(): String { return this.format(formatter) } +/** + * 2023년 11월 25일 + */ +@Suppress("detekt:FunctionNaming") +fun LocalDateTime.to_yyyy_korYear_MM_korMonth_dd_korDay(): String { + val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") + return this.format(formatter) +} + fun getSafeLocalDateTime(year: Int, month: Int, day: Int): LocalDateTime = try { LocalDateTime.of(year, month, day, 0, 0) } catch (e: DateTimeException) { diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeResponse.kt index bfd4c319..7cf0366e 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeResponse.kt @@ -22,7 +22,7 @@ data class EnvelopeInfo( val gift: String? = null, val memo: String? = null, val hasVisited: Boolean? = null, - val handedOverAt: LocalDateTime? = null, + val handedOverAt: LocalDateTime, ) @Serializable diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt index ec4d41ac..7d3c47d1 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt @@ -29,8 +29,11 @@ import com.susu.core.ui.DialogToken import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.toMoneyFormat +import com.susu.core.ui.util.to_yyyy_dot_MM_dot_dd +import com.susu.core.ui.util.to_yyyy_korYear_MM_korMonth_dd_korDay import com.susu.feature.received.R import com.susu.feature.received.envelopedetail.component.DetailItem +import kotlinx.datetime.toJavaLocalDateTime @Composable fun ReceivedEnvelopeDetailRoute( @@ -146,6 +149,11 @@ fun ReceivedEnvelopeDetailScreen( contentText = uiState.envelope.relationship.customRelation ?: uiState.envelope.relationship.relation, isEmptyContent = false, ) + DetailItem( + categoryText = stringResource(com.susu.core.ui.R.string.word_date), + contentText = uiState.envelope.handedOverAt.toJavaLocalDateTime().to_yyyy_korYear_MM_korMonth_dd_korDay(), + isEmptyContent = false, + ) DetailItem( categoryText = stringResource(com.susu.core.ui.R.string.word_is_visited), contentText = if (uiState.envelope.hasVisited == true) stringResource(id = com.susu.core.ui.R.string.word_yes) else stringResource( From 6517b267d3a05c1fc9e08c3662fec97b509a14de Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 12:27:38 +0900 Subject: [PATCH 12/95] =?UTF-8?q?feat:=20=EB=B0=9B=EC=9D=80=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=ED=8E=B8=EC=A7=91=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/susu/core/ui/util/Date.kt | 4 +- .../susu/feature/navigator/MainNavigator.kt | 6 +- .../ReceivedEnvelopeDetailScreen.kt | 11 +- .../ReceivedEnvelopeEditContract.kt | 23 ++ .../ReceivedEnvelopeEditScreen.kt | 210 ++++++++++++------ .../ReceivedEnvelopeEditViewModel.kt | 53 +++++ .../received/navigation/ReceivedNavigation.kt | 15 +- 7 files changed, 236 insertions(+), 86 deletions(-) create mode 100644 feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditContract.kt create mode 100644 feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt diff --git a/core/ui/src/main/java/com/susu/core/ui/util/Date.kt b/core/ui/src/main/java/com/susu/core/ui/util/Date.kt index a9973a8e..024825cb 100644 --- a/core/ui/src/main/java/com/susu/core/ui/util/Date.kt +++ b/core/ui/src/main/java/com/susu/core/ui/util/Date.kt @@ -18,8 +18,8 @@ fun LocalDateTime.to_yyyy_dot_MM_dot_dd(): String { * 2023년 11월 25일 */ @Suppress("detekt:FunctionNaming") -fun LocalDateTime.to_yyyy_korYear_MM_korMonth_dd_korDay(): String { - val formatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") +fun LocalDateTime.to_yyyy_korYear_M_korMonth_d_korDay(): String { + val formatter = DateTimeFormatter.ofPattern("yyyy년 M월 d일") return this.format(formatter) } diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt index b2cb8055..3573806f 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt @@ -60,7 +60,7 @@ internal class MainNavigator( ReceivedRoute.ledgerSearchRoute, ReceivedRoute.ledgerFilterRoute("{${ReceivedRoute.FILTER_ARGUMENT_NAME}}"), ReceivedRoute.envelopeDetailRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}"), - ReceivedRoute.envelopeEditRoute, + ReceivedRoute.envelopeEditRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}"), SentRoute.sentEnvelopeRoute, SentRoute.sentEnvelopeDetailRoute, SentRoute.sentEnvelopeEditRoute, @@ -172,8 +172,8 @@ internal class MainNavigator( navController.navigateReceivedEnvelopeDetail(envelope) } - fun navigateReceivedEnvelopeEdit() { - navController.navigateReceivedEnvelopeEdit() + fun navigateReceivedEnvelopeEdit(envelope: Envelope) { + navController.navigateReceivedEnvelopeEdit(envelope) } fun navigateVoteAdd() { diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt index 7d3c47d1..84bbaa1f 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt @@ -25,12 +25,13 @@ import com.susu.core.designsystem.component.appbar.icon.DeleteText import com.susu.core.designsystem.component.appbar.icon.EditText import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.Envelope import com.susu.core.ui.DialogToken import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.toMoneyFormat import com.susu.core.ui.util.to_yyyy_dot_MM_dot_dd -import com.susu.core.ui.util.to_yyyy_korYear_MM_korMonth_dd_korDay +import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay import com.susu.feature.received.R import com.susu.feature.received.envelopedetail.component.DetailItem import kotlinx.datetime.toJavaLocalDateTime @@ -40,7 +41,7 @@ fun ReceivedEnvelopeDetailRoute( viewModel: ReceivedEnvelopeDetailViewModel = hiltViewModel(), popBackStackWithDeleteReceivedEnvelopeId: (Long) -> Unit, popBackStackWithReceivedEnvelope: (String) -> Unit, - navigateReceivedEnvelopeEdit: () -> Unit, + navigateReceivedEnvelopeEdit: (Envelope) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, onShowDialog: (DialogToken) -> Unit, @@ -50,7 +51,7 @@ fun ReceivedEnvelopeDetailRoute( viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { is ReceivedEnvelopeDetailSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) - is ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit -> TODO() + is ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit -> navigateReceivedEnvelopeEdit(sideEffect.envelope) is ReceivedEnvelopeDetailSideEffect.PopBackStackWithDeleteReceivedEnvelopeId -> popBackStackWithDeleteReceivedEnvelopeId( sideEffect.envelopeId, ) @@ -84,7 +85,7 @@ fun ReceivedEnvelopeDetailRoute( ReceivedEnvelopeDetailScreen( uiState = uiState, - onClickEdit = navigateReceivedEnvelopeEdit, + onClickEdit = viewModel::navigateEnvelopeEdit, onClickDelete = viewModel::showDeleteDialog, onClickBackIcon = viewModel::popBackStackWithEnvelope, ) @@ -151,7 +152,7 @@ fun ReceivedEnvelopeDetailScreen( ) DetailItem( categoryText = stringResource(com.susu.core.ui.R.string.word_date), - contentText = uiState.envelope.handedOverAt.toJavaLocalDateTime().to_yyyy_korYear_MM_korMonth_dd_korDay(), + contentText = uiState.envelope.handedOverAt.toJavaLocalDateTime().to_yyyy_korYear_M_korMonth_d_korDay(), isEmptyContent = false, ) DetailItem( diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditContract.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditContract.kt new file mode 100644 index 00000000..3785dd52 --- /dev/null +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditContract.kt @@ -0,0 +1,23 @@ +package com.susu.feature.received.envelopeedit + +import com.susu.core.model.Envelope +import com.susu.core.model.Relationship +import com.susu.core.ui.base.SideEffect +import com.susu.core.ui.base.UiState +import com.susu.feature.received.envelopeadd.content.relationship.RelationShipSideEffect +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf + +data class ReceivedEnvelopeEditState( + val envelope: Envelope = Envelope(), + val relationshipConfig: PersistentList = persistentListOf(Relationship()), + val showCustomRelationButton: Boolean = false, + val showDateBottomSheet: Boolean = false, + val isRelationSaved: Boolean = false, +) : UiState { +} + +sealed interface ReceivedEnvelopeEditSideEffect : SideEffect { + data object PopBackStack : ReceivedEnvelopeEditSideEffect + data class HandleException(val throwable: Throwable, val retry: () -> Unit) : ReceivedEnvelopeEditSideEffect +} diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt index fef75fea..cf78aad1 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt @@ -12,20 +12,29 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text 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 +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon +import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuDatePickerBottomSheet +import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuLimitDatePickerBottomSheet import com.susu.core.designsystem.component.button.AddConditionButton import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.MediumButtonStyle @@ -33,36 +42,79 @@ import com.susu.core.designsystem.component.button.SmallButtonStyle import com.susu.core.designsystem.component.button.SusuFilledButton import com.susu.core.designsystem.component.textfield.SusuBasicTextField import com.susu.core.designsystem.component.textfield.SusuPriceTextField +import com.susu.core.designsystem.component.textfieldbutton.SusuTextFieldWrapContentButton +import com.susu.core.designsystem.component.textfieldbutton.TextFieldButtonColor +import com.susu.core.designsystem.component.textfieldbutton.style.SmallTextFieldButtonStyle import com.susu.core.designsystem.theme.Gray30 import com.susu.core.designsystem.theme.Gray40 import com.susu.core.designsystem.theme.Gray70 +import com.susu.core.designsystem.theme.Gray80 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.Relationship +import com.susu.core.ui.extension.collectWithLifecycle +import com.susu.core.ui.extension.susuClickable +import com.susu.core.ui.util.AnnotatedText import com.susu.feature.received.R import com.susu.feature.received.envelopeedit.component.EditDetailItem @Composable fun ReceivedEnvelopeEditRoute( - @Suppress("detekt:UnusedParameter") + viewModel: ReceivedEnvelopeEditViewModel = hiltViewModel(), popBackStack: () -> Unit, + handleException: (Throwable, () -> Unit) -> Unit, ) { - ReceivedEnvelopeEditScreen() + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + + val focusRequester = remember { FocusRequester() } + val scope = rememberCoroutineScope() + + viewModel.sideEffect.collectWithLifecycle { sideEffect -> + when (sideEffect) { + is ReceivedEnvelopeEditSideEffect.HandleException -> handleException( + sideEffect.throwable, + sideEffect.retry, + ) + + ReceivedEnvelopeEditSideEffect.PopBackStack -> popBackStack() + } + } + + LaunchedEffect(key1 = Unit) { + viewModel.initData() + } + + + ReceivedEnvelopeEditScreen( + uiState = uiState, + focusRequester = focusRequester, + onClickBackIcon = viewModel::popBackStack, + ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun ReceivedEnvelopeEditScreen( - modifier: Modifier = Modifier, + uiState: ReceivedEnvelopeEditState = ReceivedEnvelopeEditState(), + focusRequester: FocusRequester = remember { FocusRequester() }, onClickBackIcon: () -> Unit = {}, onClickSave: () -> Unit = {}, + onTextChangeMoney: (String) -> Unit = {}, + onTextChangeName: (String) -> Unit = {}, + onClickRelation: (Relationship) -> Unit = {}, + onClickCustomRelationClear: () -> Unit = {}, + onClickCustomRelationClose: () -> Unit = {}, + onClickRelationInnerButton: () -> Unit = {}, + onClickAddConditionButton: () -> Unit = {}, + onClickDate: () -> Unit = {}, + onClickHasVisited: (Boolean) -> Unit = {}, + onTextChangeGift: (String) -> Unit = {}, + onTextChangePhoneNumber: (String) -> Unit = {}, + onTextChangeMemo: (String) -> Unit = {}, + onDismissDateBottomSheet: (Int, Int, Int) -> Unit = { _, _, _ -> }, + onItemSelectedDateBottomSheet: (Int, Int, Int) -> Unit = { _, _, _ -> }, ) { - // TODO: 수정 필요 - var money by remember { mutableStateOf(150000) } - var name by remember { mutableStateOf("김철수") } - var present by remember { mutableStateOf("") } - var phone by remember { mutableStateOf("") } - var memo by remember { mutableStateOf("") } - Box( - modifier = modifier + modifier = Modifier .fillMaxSize() .background(SusuTheme.colorScheme.background10), ) { @@ -75,7 +127,7 @@ fun ReceivedEnvelopeEditScreen( }, ) Column( - modifier = modifier + modifier = Modifier .verticalScroll(rememberScrollState()) .weight(1f) .padding( @@ -85,59 +137,67 @@ fun ReceivedEnvelopeEditScreen( ), ) { SusuPriceTextField( - text = money.toString(), - onTextChange = { money = it.toInt() }, + text = uiState.envelope.amount.toString(), + onTextChange = onTextChangeMoney, textStyle = SusuTheme.typography.title_xxl, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) - Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_m)) + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) EditDetailItem( categoryText = stringResource(R.string.received_envelope_edit_screen_name), categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = name, - onTextChange = { name = it }, + text = uiState.envelope.friend.name, + onTextChange = onTextChangeName, placeholder = stringResource(R.string.received_envelope_edit_screen_name_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(R.string.received_envelope_edit_screen_relationship), categoryTextAlign = Alignment.Top, ) { - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = "친구", - isActive = true, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = "가족", - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = "친척", - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = "동료", - isActive = false, - onClick = {}, - ) - AddConditionButton( - onClick = {}, + uiState.relationshipConfig.dropLast(1).forEach { + SusuFilledButton( + color = FilledButtonColor.Orange, + style = SmallButtonStyle.height32, + text = it.relation, + isActive = it == uiState.envelope.relationship, + onClick = { onClickRelation(it) }, + ) + } + + if (uiState.showCustomRelationButton) { + SusuTextFieldWrapContentButton( + focusRequester = focusRequester, + onTextChange = {}, + color = TextFieldButtonColor.Orange, + style = SmallTextFieldButtonStyle.height32, + text = uiState.envelope.relationship.customRelation ?: "", + isFocused = uiState.relationshipConfig.last().id == uiState.envelope.relationship.id, + isSaved = uiState.isRelationSaved, + onClickClearIcon = onClickCustomRelationClear, + onClickCloseIcon = onClickCustomRelationClose, + onClickFilledButton = onClickRelationInnerButton, + onClickButton = { onClickRelation(uiState.relationshipConfig.last()) }, + ) + } else { + AddConditionButton(onClick = onClickAddConditionButton) + } + } + EditDetailItem(categoryText = stringResource(id = com.susu.core.ui.R.string.word_date), categoryTextAlign = Alignment.Top) { + Text( + modifier = Modifier.susuClickable(rippleEnabled = false, onClick = onClickDate), + text = stringResource( + R.string.ledger_add_screen_date, + uiState.envelope.handedOverAt.year, + uiState.envelope.handedOverAt.month.value, + uiState.envelope.handedOverAt.dayOfMonth, + ), + style = SusuTheme.typography.title_m, ) } EditDetailItem( @@ -148,68 +208,78 @@ fun ReceivedEnvelopeEditScreen( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, text = stringResource(com.susu.core.ui.R.string.word_yes), - isActive = true, - onClick = {}, - modifier = modifier.weight(1f), + isActive = uiState.envelope.hasVisited == true, + onClick = { onClickHasVisited(true) }, + modifier = Modifier.weight(1f), ) SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, text = stringResource(com.susu.core.ui.R.string.word_no), - isActive = false, - onClick = {}, - modifier = modifier.weight(1f), + isActive = uiState.envelope.hasVisited == false, + onClick = { onClickHasVisited(false) }, + modifier = Modifier.weight(1f), ) } EditDetailItem( categoryText = stringResource(R.string.received_envelope_edit_screen_present), - categoryTextColor = if (present.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (!uiState.envelope.gift.isNullOrEmpty()) Gray70 else Gray40, categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = present, - onTextChange = { present = it }, + text = uiState.envelope.gift ?: "", + onTextChange = onTextChangeGift, placeholder = stringResource(R.string.received_envelope_edit_screen_present_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(com.susu.core.ui.R.string.word_phone_number), - categoryTextColor = if (phone.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (uiState.envelope.friend.phoneNumber.isNotEmpty()) Gray70 else Gray40, categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = phone, - onTextChange = { phone = it }, + text = uiState.envelope.friend.phoneNumber, + onTextChange = onTextChangePhoneNumber, placeholder = stringResource(R.string.received_envelope_edit_screen_phone_number_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(com.susu.core.ui.R.string.word_memo), - categoryTextColor = if (memo.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (!uiState.envelope.memo.isNullOrEmpty()) Gray70 else Gray40, categoryTextAlign = Alignment.Top, ) { SusuBasicTextField( - text = memo, - onTextChange = { memo = it }, + text = uiState.envelope.memo ?: "", + onTextChange = onTextChangeMemo, placeholder = stringResource(R.string.received_envelope_edit_screen_memo_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, - maxLines = 2, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } - Spacer(modifier = modifier.size(240.dp)) + Spacer(modifier = Modifier.size(240.dp)) + } + + if (uiState.showDateBottomSheet) { + SusuDatePickerBottomSheet( + initialYear = uiState.envelope.handedOverAt.year, + initialMonth = uiState.envelope.handedOverAt.month.value, + initialDay = uiState.envelope.handedOverAt.dayOfMonth, + maximumContainerHeight = 346.dp, + onDismissRequest = onDismissDateBottomSheet, + onItemSelected = onItemSelectedDateBottomSheet, + ) } SusuFilledButton( - modifier = modifier + modifier = Modifier .fillMaxWidth() .imePadding(), color = FilledButtonColor.Black, diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt new file mode 100644 index 00000000..dae702d6 --- /dev/null +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt @@ -0,0 +1,53 @@ +package com.susu.feature.received.envelopeedit + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import com.susu.core.model.Envelope +import com.susu.core.ui.base.BaseViewModel +import com.susu.core.ui.extension.decodeFromUri +import com.susu.core.ui.extension.encodeToUri +import com.susu.domain.usecase.envelope.DeleteEnvelopeUseCase +import com.susu.domain.usecase.envelope.GetEnvelopeUseCase +import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase +import com.susu.feature.received.navigation.ReceivedRoute +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.launch +import kotlinx.serialization.json.Json +import timber.log.Timber +import javax.inject.Inject + +@HiltViewModel +class ReceivedEnvelopeEditViewModel @Inject constructor( + private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, + savedStateHandle: SavedStateHandle, +) : BaseViewModel( + ReceivedEnvelopeEditState(), +) { + private val argument = savedStateHandle.get(ReceivedRoute.ENVELOPE_ARGUMENT_NAME)!! + + private var isFirstVisited: Boolean = true + + fun initData() = viewModelScope.launch { + if (isFirstVisited.not()) return@launch + isFirstVisited = false + + val envelope = Json.decodeFromUri(argument) + + getRelationShipConfigListUseCase() + .onSuccess { + intent { + copy( + envelope = envelope, + relationshipConfig = it.toPersistentList(), + showCustomRelationButton = it.last().id == envelope.relationship.id, + isRelationSaved = it.last().id == envelope.relationship.id, + ) + } + } + } + + + fun popBackStack() = postSideEffect(ReceivedEnvelopeEditSideEffect.PopBackStack) + +} diff --git a/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt b/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt index fe87b0de..b9117123 100644 --- a/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt +++ b/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt @@ -56,8 +56,8 @@ fun NavController.navigateReceivedEnvelopeDetail(envelope: Envelope) { navigate(ReceivedRoute.envelopeDetailRoute(Json.encodeToUri(envelope))) } -fun NavController.navigateReceivedEnvelopeEdit() { - navigate(ReceivedRoute.envelopeEditRoute) +fun NavController.navigateReceivedEnvelopeEdit(envelope: Envelope) { + navigate(ReceivedRoute.envelopeEditRoute(Json.encodeToUri(envelope))) } @Suppress("detekt:LongMethod") @@ -74,7 +74,7 @@ fun NavGraphBuilder.receivedNavGraph( navigateLedgerAdd: () -> Unit, navigateEnvelopAdd: (String, Long) -> Unit, navigateEnvelopeDetail: (Envelope) -> Unit, - navigateEnvelopeEdit: () -> Unit, + navigateEnvelopeEdit: (Envelope) -> Unit, popBackStackWithEnvelope: (String) -> Unit, popBackStackWithDeleteReceivedEnvelopeId: (Long) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, @@ -189,9 +189,12 @@ fun NavGraphBuilder.receivedNavGraph( } composable( - route = ReceivedRoute.envelopeEditRoute, + route = ReceivedRoute.envelopeEditRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}"), ) { - ReceivedEnvelopeEditRoute(popBackStack = popBackStack) + ReceivedEnvelopeEditRoute( + popBackStack = popBackStack, + handleException = handleException, + ) } } @@ -213,5 +216,5 @@ object ReceivedRoute { fun envelopeAddRoute(categoryName: String, ledgerId: String) = "envelope-add/$categoryName/$ledgerId" fun envelopeDetailRoute(envelope: String) = "envelope-detail/$envelope" - const val envelopeEditRoute = "envelope-edit" // TODO 파라미터 넘기는 방식으로 수정해야함. + fun envelopeEditRoute(envelope: String) = "envelope-edit/$envelope" } From 8fe39d42cc6d74444e30f7f6d6945de6b75cf34f Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 14:44:28 +0900 Subject: [PATCH 13/95] =?UTF-8?q?feat:=20=EB=B0=9B=EC=9D=80=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=ED=8E=B8=EC=A7=91=20=EB=8F=99=EC=9E=91=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReceivedEnvelopeEditContract.kt | 7 +- .../ReceivedEnvelopeEditScreen.kt | 35 +++-- .../ReceivedEnvelopeEditViewModel.kt | 127 +++++++++++++++++- 3 files changed, 159 insertions(+), 10 deletions(-) diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditContract.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditContract.kt index 3785dd52..a5a2ea0f 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditContract.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditContract.kt @@ -4,7 +4,6 @@ import com.susu.core.model.Envelope import com.susu.core.model.Relationship import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState -import com.susu.feature.received.envelopeadd.content.relationship.RelationShipSideEffect import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf @@ -15,9 +14,15 @@ data class ReceivedEnvelopeEditState( val showDateBottomSheet: Boolean = false, val isRelationSaved: Boolean = false, ) : UiState { + val buttonEnabled = when { + envelope.friend.name.isEmpty() -> false + envelope.relationship.id == relationshipConfig.last().id && isRelationSaved.not() -> false + else -> true + } } sealed interface ReceivedEnvelopeEditSideEffect : SideEffect { + data object FocusCustomRelation : ReceivedEnvelopeEditSideEffect data object PopBackStack : ReceivedEnvelopeEditSideEffect data class HandleException(val throwable: Throwable, val retry: () -> Unit) : ReceivedEnvelopeEditSideEffect } diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt index cf78aad1..f0bf7710 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt @@ -16,11 +16,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text 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 import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester @@ -34,7 +31,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuDatePickerBottomSheet -import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuLimitDatePickerBottomSheet import com.susu.core.designsystem.component.button.AddConditionButton import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.MediumButtonStyle @@ -48,14 +44,14 @@ import com.susu.core.designsystem.component.textfieldbutton.style.SmallTextField import com.susu.core.designsystem.theme.Gray30 import com.susu.core.designsystem.theme.Gray40 import com.susu.core.designsystem.theme.Gray70 -import com.susu.core.designsystem.theme.Gray80 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.Relationship import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable -import com.susu.core.ui.util.AnnotatedText import com.susu.feature.received.R import com.susu.feature.received.envelopeedit.component.EditDetailItem +import kotlinx.coroutines.android.awaitFrame +import kotlinx.coroutines.launch @Composable fun ReceivedEnvelopeEditRoute( @@ -76,6 +72,10 @@ fun ReceivedEnvelopeEditRoute( ) ReceivedEnvelopeEditSideEffect.PopBackStack -> popBackStack() + ReceivedEnvelopeEditSideEffect.FocusCustomRelation -> scope.launch { + awaitFrame() + focusRequester.requestFocus() + } } } @@ -88,6 +88,22 @@ fun ReceivedEnvelopeEditRoute( uiState = uiState, focusRequester = focusRequester, onClickBackIcon = viewModel::popBackStack, + onClickSave = {}, + onTextChangeMoney = viewModel::updateMoney, + onTextChangeName = viewModel::updateName, + onTextChangeRelation = viewModel::updateCustomRelation, + onClickRelation = viewModel::updateRelation, + onClickCustomRelationClear = { viewModel.updateCustomRelation("") }, + onClickCustomRelationClose = viewModel::closeCustomRelation, + onClickRelationInnerButton = viewModel::toggleRelationSaved, + onClickAddConditionButton = viewModel::showCustomRelation, + onClickDate = viewModel::showDateBottomSheet, + onClickHasVisited = viewModel::updateHasVisited, + onTextChangeGift = viewModel::updateGift, + onTextChangePhoneNumber = viewModel::updatePhoneNumber, + onTextChangeMemo = viewModel::updateMemo, + onDismissDateBottomSheet = viewModel::hideDateBottomSheet, + onItemSelectedDateBottomSheet = viewModel::updateDate, ) } @@ -100,6 +116,7 @@ fun ReceivedEnvelopeEditScreen( onClickSave: () -> Unit = {}, onTextChangeMoney: (String) -> Unit = {}, onTextChangeName: (String) -> Unit = {}, + onTextChangeRelation: (String) -> Unit = {}, onClickRelation: (Relationship) -> Unit = {}, onClickCustomRelationClear: () -> Unit = {}, onClickCustomRelationClose: () -> Unit = {}, @@ -173,10 +190,10 @@ fun ReceivedEnvelopeEditScreen( if (uiState.showCustomRelationButton) { SusuTextFieldWrapContentButton( focusRequester = focusRequester, - onTextChange = {}, + onTextChange = onTextChangeRelation, color = TextFieldButtonColor.Orange, style = SmallTextFieldButtonStyle.height32, - text = uiState.envelope.relationship.customRelation ?: "", + text = uiState.relationshipConfig.last().customRelation ?: "", isFocused = uiState.relationshipConfig.last().id == uiState.envelope.relationship.id, isSaved = uiState.isRelationSaved, onClickClearIcon = onClickCustomRelationClear, @@ -285,6 +302,8 @@ fun ReceivedEnvelopeEditScreen( color = FilledButtonColor.Black, style = MediumButtonStyle.height60, shape = RectangleShape, + isActive = uiState.buttonEnabled, + isClickable = uiState.buttonEnabled, text = stringResource(com.susu.core.ui.R.string.word_save), onClick = onClickSave, ) diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt index dae702d6..c2772641 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt @@ -3,7 +3,10 @@ package com.susu.feature.received.envelopeedit import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.model.Envelope +import com.susu.core.model.Friend +import com.susu.core.model.Relationship import com.susu.core.ui.base.BaseViewModel +import com.susu.core.ui.base.SideEffect import com.susu.core.ui.extension.decodeFromUri import com.susu.core.ui.extension.encodeToUri import com.susu.domain.usecase.envelope.DeleteEnvelopeUseCase @@ -13,8 +16,10 @@ import com.susu.feature.received.navigation.ReceivedRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch +import kotlinx.datetime.toKotlinLocalDateTime import kotlinx.serialization.json.Json import timber.log.Timber +import java.time.LocalDateTime import javax.inject.Inject @HiltViewModel @@ -39,7 +44,10 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( intent { copy( envelope = envelope, - relationshipConfig = it.toPersistentList(), + relationshipConfig = it.map { + if (it.id == envelope.relationship.id) it.copy(customRelation = envelope.relationship.customRelation) + else it + }.toPersistentList(), showCustomRelationButton = it.last().id == envelope.relationship.id, isRelationSaved = it.last().id == envelope.relationship.id, ) @@ -50,4 +58,121 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( fun popBackStack() = postSideEffect(ReceivedEnvelopeEditSideEffect.PopBackStack) + fun updateMoney(money: String) = intent { + copy( + envelope = envelope.copy(amount = money.toLongOrNull() ?: 0L), + ) + } + + fun updateName(name: String) = intent { + copy( + envelope = envelope.copy(friend = Friend(name = name)), + ) + } + + fun updateRelation(relationship: Relationship) = intent { + copy( + envelope = envelope.copy(relationship = relationship), + ) + } + + fun updateCustomRelation(customRelation: String?) = intent { + copy( + envelope = envelope.copy(relationship = envelope.relationship.copy(customRelation = customRelation)), + relationshipConfig = relationshipConfig.map { + if (it.id == envelope.relationship.id) { + it.copy(customRelation = customRelation) + } else it + }.toPersistentList(), + ) + } + + fun closeCustomRelation() = intent { + copy( + envelope = if (envelope.relationship.id == relationshipConfig.last().id) { + envelope.copy(relationship = relationshipConfig.first()) + } else { + envelope + }, + relationshipConfig = relationshipConfig.map { + it.copy(customRelation = null) + }.toPersistentList(), + isRelationSaved = false, + showCustomRelationButton = false, + ) + } + + fun toggleRelationSaved() = intent { + copy( + isRelationSaved = !isRelationSaved, + ) + } + + fun showCustomRelation() = intent { + postSideEffect(ReceivedEnvelopeEditSideEffect.FocusCustomRelation) + copy( + isRelationSaved = false, + showCustomRelationButton = true, + envelope = envelope.copy( + relationship = relationshipConfig.last(), + ), + ) + } + + fun showDateBottomSheet() = intent { + copy( + showDateBottomSheet = true, + ) + } + + fun updateHasVisited(visited: Boolean) = intent { + copy( + envelope = envelope.copy( + hasVisited = if (visited == envelope.hasVisited) null else visited, + ), + ) + } + + fun updateGift(gift: String) = intent { + copy( + envelope = envelope.copy( + gift = gift.ifEmpty { null }, + ), + ) + } + + fun updatePhoneNumber(phoneNumber: String) = intent { + copy( + envelope = envelope.copy( + friend = envelope.friend.copy( + phoneNumber = phoneNumber, + ), + ), + ) + } + + fun updateMemo(memo: String) = intent { + copy( + envelope = envelope.copy( + memo = memo.ifEmpty { null }, + ), + ) + } + + fun hideDateBottomSheet(year: Int, month: Int, day: Int) = intent { + copy( + envelope = envelope.copy( + handedOverAt = LocalDateTime.of(year, month, day, 0, 0).toKotlinLocalDateTime(), + ), + showDateBottomSheet = false, + ) + } + + fun updateDate(year: Int, month: Int, day: Int) = intent { + copy( + envelope = envelope.copy( + handedOverAt = LocalDateTime.of(year, month, day, 0, 0).toKotlinLocalDateTime(), + ), + ) + } } From 03db3b95589106625e1a40a15424cdfb3b90e54b Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 14:58:58 +0900 Subject: [PATCH 14/95] feat: EditReceivedEnvelopeUseCase --- .../repository/EnvelopesRepositoryImpl.kt | 35 +++++++++++++ .../data/repository/FriendRepositoryImpl.kt | 16 ++++++ .../com/susu/data/remote/api/FriendService.kt | 8 +++ .../domain/repository/EnvelopesRepository.kt | 14 ++++++ .../domain/repository/FriendRepository.kt | 8 +++ .../envelope/EditReceivedEnvelopeUseCase.kt | 49 +++++++++++++++++++ 6 files changed, 130 insertions(+) create mode 100644 domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index 3c322612..55f80128 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -9,6 +9,7 @@ import com.susu.data.remote.model.request.CategoryRequest import com.susu.data.remote.model.request.EnvelopeRequest import com.susu.data.remote.model.response.toModel import com.susu.domain.repository.EnvelopesRepository +import kotlinx.datetime.LocalDateTime import javax.inject.Inject class EnvelopesRepositoryImpl @Inject constructor( @@ -90,4 +91,38 @@ class EnvelopesRepositoryImpl @Inject constructor( override suspend fun getEnvelope(id: Long): Envelope = envelopesService.getEnvelope(id).getOrThrow().toModel() override suspend fun deleteEnvelope(id: Long) = envelopesService.deleteEnvelope(id).getOrThrow() + + override suspend fun editEnvelope( + id: Long, + type: String, + friendId: Long, + ledgerId: Long?, + amount: Long, + gift: String?, + memo: String?, + hasVisited: Boolean?, + handedOverAt: LocalDateTime, + categoryId: Long?, + customCategory: String?, + ): Envelope = envelopesService.editEnvelope( + id = id, + envelopeRequest = EnvelopeRequest( + type = type, + friendId = friendId, + ledgerId = ledgerId, + amount = amount, + gift = gift, + memo = memo, + hasVisited = hasVisited, + handedOverAt = handedOverAt, + category = if (categoryId != null) { + CategoryRequest( + id = categoryId, + customCategory = customCategory, + ) + } else { + null + }, + ) + ).getOrThrow().toModel() } diff --git a/data/src/main/java/com/susu/data/data/repository/FriendRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/FriendRepositoryImpl.kt index 4812ca9c..6126cba7 100644 --- a/data/src/main/java/com/susu/data/data/repository/FriendRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/FriendRepositoryImpl.kt @@ -27,4 +27,20 @@ class FriendRepositoryImpl @Inject constructor( override suspend fun searchFriend(name: String): List = friendService.searchFriend( name = name, ).getOrThrow().data.map { it.toModel() } + + override suspend fun editFriend( + id: Long, + name: String, + phoneNumber: String?, + relationshipId: Long, + customRelation: String?, + ) = friendService.editFriend( + id = id, + friendRequest = FriendRequest( + name = name, + phoneNumber = phoneNumber, + relationshipId = relationshipId, + customRelation = customRelation, + ) + ).getOrThrow() } diff --git a/data/src/main/java/com/susu/data/remote/api/FriendService.kt b/data/src/main/java/com/susu/data/remote/api/FriendService.kt index 9274a7b4..b7fd40aa 100644 --- a/data/src/main/java/com/susu/data/remote/api/FriendService.kt +++ b/data/src/main/java/com/susu/data/remote/api/FriendService.kt @@ -7,6 +7,8 @@ import com.susu.data.remote.retrofit.ApiResult import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST +import retrofit2.http.PUT +import retrofit2.http.Path import retrofit2.http.Query interface FriendService { @@ -20,4 +22,10 @@ interface FriendService { suspend fun searchFriend( @Query("name") name: String, ): ApiResult + + @PUT("friends/{id}") + suspend fun editFriend( + @Path("id") id: Long, + @Body friendRequest: FriendRequest, + ): ApiResult } diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index 33fc7104..274b9149 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -49,4 +49,18 @@ interface EnvelopesRepository { suspend fun deleteEnvelope( id: Long, ) + + suspend fun editEnvelope( + id: Long, + type: String, + friendId: Long, + ledgerId: Long? = null, + amount: Long, + gift: String? = null, + memo: String? = null, + hasVisited: Boolean? = null, + handedOverAt: LocalDateTime, + categoryId: Long? = null, + customCategory: String? = null, + ): Envelope } diff --git a/domain/src/main/java/com/susu/domain/repository/FriendRepository.kt b/domain/src/main/java/com/susu/domain/repository/FriendRepository.kt index 2a1fb65a..e7c7e31a 100644 --- a/domain/src/main/java/com/susu/domain/repository/FriendRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/FriendRepository.kt @@ -13,4 +13,12 @@ interface FriendRepository { suspend fun searchFriend( name: String, ): List + + suspend fun editFriend( + id: Long, + name: String, + phoneNumber: String? = null, + relationshipId: Long, + customRelation: String? = null, + ) } diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt new file mode 100644 index 00000000..83b22a25 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt @@ -0,0 +1,49 @@ +package com.susu.domain.usecase.envelope + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.EnvelopesRepository +import com.susu.domain.repository.FriendRepository +import kotlinx.datetime.LocalDateTime +import javax.inject.Inject + +class EditReceivedEnvelopeUseCase @Inject constructor( + private val friendRepository: FriendRepository, + private val envelopesRepository: EnvelopesRepository, +) { + suspend operator fun invoke(param: Param) = runCatchingIgnoreCancelled { + with(param) { + friendRepository.editFriend( + id = friendId, + name = friendName, + phoneNumber = phoneNumber, + relationshipId = relationshipId, + customRelation = customRelation, + ) + + envelopesRepository.createEnvelope( + type = "RECEIVED", + friendId = friendId, + ledgerId = ledgerId, + amount = amount, + gift = gift, + memo = memo, + hasVisited = hasVisited, + handedOverAt = handedOverAt, + ) + } + } + + data class Param( + val friendId: Long, + val friendName: String, + val phoneNumber: String? = null, + val relationshipId: Long, + val customRelation: String? = null, + val ledgerId: Long, + val amount: Long, + val gift: String? = null, + val memo: String? = null, + val handedOverAt: LocalDateTime, + val hasVisited: Boolean? = null, + ) +} From d6386266238021747ce773f70904b4a936cbdd88 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 15:28:44 +0900 Subject: [PATCH 15/95] =?UTF-8?q?feat:=20=EB=B4=89=ED=88=AC=20=ED=8E=B8?= =?UTF-8?q?=EC=A7=91=20=EC=84=9C=EB=B2=84=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelope/EditReceivedEnvelopeUseCase.kt | 4 ++- .../susu/feature/navigator/MainNavigator.kt | 12 ++++---- .../ReceivedEnvelopeDetailContract.kt | 2 +- .../ReceivedEnvelopeDetailScreen.kt | 4 +-- .../ReceivedEnvelopeDetailViewModel.kt | 4 ++- .../ReceivedEnvelopeEditScreen.kt | 2 +- .../ReceivedEnvelopeEditViewModel.kt | 30 ++++++++++++++++++- .../ledgerdetail/LedgerDetailContract.kt | 2 +- .../ledgerdetail/LedgerDetailScreen.kt | 4 +-- .../ledgerdetail/LedgerDetailViewModel.kt | 2 +- .../received/navigation/ReceivedNavigation.kt | 26 +++++++++------- 11 files changed, 65 insertions(+), 27 deletions(-) diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt index 83b22a25..ff6a743d 100644 --- a/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt @@ -20,7 +20,8 @@ class EditReceivedEnvelopeUseCase @Inject constructor( customRelation = customRelation, ) - envelopesRepository.createEnvelope( + envelopesRepository.editEnvelope( + id = envelopeId, type = "RECEIVED", friendId = friendId, ledgerId = ledgerId, @@ -34,6 +35,7 @@ class EditReceivedEnvelopeUseCase @Inject constructor( } data class Param( + val envelopeId: Long, val friendId: Long, val friendName: String, val phoneNumber: String? = null, diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt index 3573806f..42e9adf7 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt @@ -59,8 +59,8 @@ internal class MainNavigator( in listOf( ReceivedRoute.ledgerSearchRoute, ReceivedRoute.ledgerFilterRoute("{${ReceivedRoute.FILTER_ARGUMENT_NAME}}"), - ReceivedRoute.envelopeDetailRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}"), - ReceivedRoute.envelopeEditRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}"), + ReceivedRoute.envelopeDetailRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}", "{${ReceivedRoute.LEDGER_ID_ARGUMENT_NAME}}"), + ReceivedRoute.envelopeEditRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}", "{${ReceivedRoute.LEDGER_ID_ARGUMENT_NAME}}"), SentRoute.sentEnvelopeRoute, SentRoute.sentEnvelopeDetailRoute, SentRoute.sentEnvelopeEditRoute, @@ -168,12 +168,12 @@ internal class MainNavigator( navController.navigateMyPagePrivacyPolicy() } - fun navigateReceivedEnvelopeDetail(envelope: Envelope) { - navController.navigateReceivedEnvelopeDetail(envelope) + fun navigateReceivedEnvelopeDetail(envelope: Envelope, ledgerId: Long) { + navController.navigateReceivedEnvelopeDetail(envelope, ledgerId) } - fun navigateReceivedEnvelopeEdit(envelope: Envelope) { - navController.navigateReceivedEnvelopeEdit(envelope) + fun navigateReceivedEnvelopeEdit(envelope: Envelope, ledgerId: Long) { + navController.navigateReceivedEnvelopeEdit(envelope, ledgerId) } fun navigateVoteAdd() { diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt index 14f49e23..bda1f1cf 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt @@ -9,7 +9,7 @@ data class ReceivedEnvelopeDetailState( ) : UiState sealed interface ReceivedEnvelopeDetailSideEffect : SideEffect { - data class NavigateReceivedEnvelopeEdit(val envelope: Envelope) : ReceivedEnvelopeDetailSideEffect + data class NavigateReceivedEnvelopeEdit(val envelope: Envelope, val ledgerId: Long) : ReceivedEnvelopeDetailSideEffect data class PopBackStackWithReceivedEnvelope(val envelope: String) : ReceivedEnvelopeDetailSideEffect data class PopBackStackWithDeleteReceivedEnvelopeId(val envelopeId: Long) : ReceivedEnvelopeDetailSideEffect data class ShowDeleteDialog(val onConfirmRequest: () -> Unit) : ReceivedEnvelopeDetailSideEffect diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt index 84bbaa1f..4949e5d5 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt @@ -41,7 +41,7 @@ fun ReceivedEnvelopeDetailRoute( viewModel: ReceivedEnvelopeDetailViewModel = hiltViewModel(), popBackStackWithDeleteReceivedEnvelopeId: (Long) -> Unit, popBackStackWithReceivedEnvelope: (String) -> Unit, - navigateReceivedEnvelopeEdit: (Envelope) -> Unit, + navigateReceivedEnvelopeEdit: (Envelope, Long) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, onShowDialog: (DialogToken) -> Unit, @@ -51,7 +51,7 @@ fun ReceivedEnvelopeDetailRoute( viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { is ReceivedEnvelopeDetailSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) - is ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit -> navigateReceivedEnvelopeEdit(sideEffect.envelope) + is ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit -> navigateReceivedEnvelopeEdit(sideEffect.envelope, sideEffect.ledgerId) is ReceivedEnvelopeDetailSideEffect.PopBackStackWithDeleteReceivedEnvelopeId -> popBackStackWithDeleteReceivedEnvelopeId( sideEffect.envelopeId, ) diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt index 50367c6a..d189c751 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt @@ -24,6 +24,8 @@ class ReceivedEnvelopeDetailViewModel @Inject constructor( ReceivedEnvelopeDetailState(), ) { private val argument = savedStateHandle.get(ReceivedRoute.ENVELOPE_ARGUMENT_NAME)!! + private val ledgerId = savedStateHandle.get(ReceivedRoute.LEDGER_ID_ARGUMENT_NAME)!!.toLong() + private var envelope = Envelope() fun getEnvelope() = viewModelScope.launch { @@ -39,7 +41,7 @@ class ReceivedEnvelopeDetailViewModel @Inject constructor( } } - fun navigateEnvelopeEdit() = postSideEffect(ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit(envelope)) + fun navigateEnvelopeEdit() = postSideEffect(ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit(envelope, ledgerId)) fun popBackStackWithEnvelope() = postSideEffect(ReceivedEnvelopeDetailSideEffect.PopBackStackWithReceivedEnvelope(Json.encodeToUri(envelope))) fun showDeleteDialog() = postSideEffect( ReceivedEnvelopeDetailSideEffect.ShowDeleteDialog( diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt index f0bf7710..a7c8e38b 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt @@ -88,7 +88,7 @@ fun ReceivedEnvelopeEditRoute( uiState = uiState, focusRequester = focusRequester, onClickBackIcon = viewModel::popBackStack, - onClickSave = {}, + onClickSave = viewModel::editReceivedEnvelope, onTextChangeMoney = viewModel::updateMoney, onTextChangeName = viewModel::updateName, onTextChangeRelation = viewModel::updateCustomRelation, diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt index c2772641..cbb5189c 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt @@ -10,6 +10,7 @@ import com.susu.core.ui.base.SideEffect import com.susu.core.ui.extension.decodeFromUri import com.susu.core.ui.extension.encodeToUri import com.susu.domain.usecase.envelope.DeleteEnvelopeUseCase +import com.susu.domain.usecase.envelope.EditReceivedEnvelopeUseCase import com.susu.domain.usecase.envelope.GetEnvelopeUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import com.susu.feature.received.navigation.ReceivedRoute @@ -25,11 +26,13 @@ import javax.inject.Inject @HiltViewModel class ReceivedEnvelopeEditViewModel @Inject constructor( private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, + private val editReceivedEnvelopeUseCase: EditReceivedEnvelopeUseCase, savedStateHandle: SavedStateHandle, ) : BaseViewModel( ReceivedEnvelopeEditState(), ) { private val argument = savedStateHandle.get(ReceivedRoute.ENVELOPE_ARGUMENT_NAME)!! + private val ledgerId = savedStateHandle.get(ReceivedRoute.LEDGER_ID_ARGUMENT_NAME)!!.toLong() private var isFirstVisited: Boolean = true @@ -55,6 +58,31 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( } } + fun editReceivedEnvelope() = viewModelScope.launch { + editReceivedEnvelopeUseCase( + param = with(currentState) { + EditReceivedEnvelopeUseCase.Param( + envelopeId = envelope.id, + friendId = envelope.friend.id, + friendName = envelope.friend.name, + phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, + relationshipId = envelope.relationship.id, + customRelation = envelope.relationship.customRelation, + ledgerId = ledgerId, + amount = envelope.amount, + gift = envelope.gift, + memo = envelope.memo, + handedOverAt = envelope.handedOverAt, + hasVisited = envelope.hasVisited, + ) + } + ).onSuccess { + popBackStack() + }.onFailure { + postSideEffect(ReceivedEnvelopeEditSideEffect.HandleException(it, ::editReceivedEnvelope)) + } + } + fun popBackStack() = postSideEffect(ReceivedEnvelopeEditSideEffect.PopBackStack) @@ -66,7 +94,7 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( fun updateName(name: String) = intent { copy( - envelope = envelope.copy(friend = Friend(name = name)), + envelope = envelope.copy(friend = envelope.friend.copy(name = name)), ) } diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailContract.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailContract.kt index 3b555cbc..3ddaae39 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailContract.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailContract.kt @@ -20,7 +20,7 @@ data class LedgerDetailState( sealed interface LedgerDetailSideEffect : SideEffect { data class NavigateEnvelopeAdd(val categoryName: String, val ledgerId: Long) : LedgerDetailSideEffect - data class NavigateEnvelopeDetail(val envelope: Envelope) : LedgerDetailSideEffect + data class NavigateEnvelopeDetail(val envelope: Envelope, val ledgerId: Long) : LedgerDetailSideEffect data class NavigateLedgerEdit(val ledger: Ledger) : LedgerDetailSideEffect data class PopBackStackWithLedger(val ledger: String) : LedgerDetailSideEffect data class PopBackStackWithDeleteLedgerId(val ledgerId: Long) : LedgerDetailSideEffect diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt index ab760394..2da3f53c 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt @@ -59,7 +59,7 @@ fun LedgerDetailRoute( toDeleteEnvelopeId: Long?, navigateLedgerEdit: (Ledger) -> Unit, navigateEnvelopAdd: (String, Long) -> Unit, - navigateEnvelopeDetail: (Envelope) -> Unit, + navigateEnvelopeDetail: (Envelope, Long) -> Unit, popBackStackWithLedger: (String) -> Unit, popBackStackWithDeleteLedgerId: (Long) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, @@ -97,7 +97,7 @@ fun LedgerDetailRoute( is LedgerDetailSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) is LedgerDetailSideEffect.ShowSnackbar -> onShowSnackbar(SnackbarToken(message = sideEffect.msg)) is LedgerDetailSideEffect.NavigateEnvelopeAdd -> navigateEnvelopAdd(sideEffect.categoryName, sideEffect.ledgerId) - is LedgerDetailSideEffect.NavigateEnvelopeDetail -> navigateEnvelopeDetail(sideEffect.envelope) + is LedgerDetailSideEffect.NavigateEnvelopeDetail -> navigateEnvelopeDetail(sideEffect.envelope, sideEffect.ledgerId) } } diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt index 35a1ea41..1457d05b 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt @@ -175,5 +175,5 @@ class LedgerDetailViewModel @Inject constructor( LedgerDetailSideEffect.NavigateEnvelopeAdd(ledger.category.customCategory ?: ledger.category.name, ledger.id), ) - fun navigateEnvelopeDetail(envelope: Envelope) = postSideEffect(LedgerDetailSideEffect.NavigateEnvelopeDetail(envelope)) + fun navigateEnvelopeDetail(envelope: Envelope) = postSideEffect(LedgerDetailSideEffect.NavigateEnvelopeDetail(envelope, ledger.id)) } diff --git a/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt b/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt index b9117123..a464b0d9 100644 --- a/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt +++ b/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt @@ -52,12 +52,12 @@ fun NavController.navigateReceivedEnvelopeAdd(categoryName: String, ledgerId: Lo navigate(ReceivedRoute.envelopeAddRoute(categoryName, ledgerId.toString())) } -fun NavController.navigateReceivedEnvelopeDetail(envelope: Envelope) { - navigate(ReceivedRoute.envelopeDetailRoute(Json.encodeToUri(envelope))) +fun NavController.navigateReceivedEnvelopeDetail(envelope: Envelope, ledgerId: Long) { + navigate(ReceivedRoute.envelopeDetailRoute(Json.encodeToUri(envelope), ledgerId.toString())) } -fun NavController.navigateReceivedEnvelopeEdit(envelope: Envelope) { - navigate(ReceivedRoute.envelopeEditRoute(Json.encodeToUri(envelope))) +fun NavController.navigateReceivedEnvelopeEdit(envelope: Envelope, ledgerId: Long) { + navigate(ReceivedRoute.envelopeEditRoute(Json.encodeToUri(envelope), ledgerId.toString())) } @Suppress("detekt:LongMethod") @@ -73,8 +73,8 @@ fun NavGraphBuilder.receivedNavGraph( navigateLedgerFilter: (FilterArgument) -> Unit, navigateLedgerAdd: () -> Unit, navigateEnvelopAdd: (String, Long) -> Unit, - navigateEnvelopeDetail: (Envelope) -> Unit, - navigateEnvelopeEdit: (Envelope) -> Unit, + navigateEnvelopeDetail: (Envelope, Long) -> Unit, + navigateEnvelopeEdit: (Envelope, Long) -> Unit, popBackStackWithEnvelope: (String) -> Unit, popBackStackWithDeleteReceivedEnvelopeId: (Long) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, @@ -176,7 +176,10 @@ fun NavGraphBuilder.receivedNavGraph( } composable( - route = ReceivedRoute.envelopeDetailRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}"), + route = ReceivedRoute.envelopeDetailRoute( + envelope = "{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}", + ledgerId = "{${ReceivedRoute.LEDGER_ID_ARGUMENT_NAME}}", + ), ) { ReceivedEnvelopeDetailRoute( popBackStackWithDeleteReceivedEnvelopeId = popBackStackWithDeleteReceivedEnvelopeId, @@ -189,7 +192,10 @@ fun NavGraphBuilder.receivedNavGraph( } composable( - route = ReceivedRoute.envelopeEditRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}"), + route = ReceivedRoute.envelopeEditRoute( + envelope = "{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}", + ledgerId = "{${ReceivedRoute.LEDGER_ID_ARGUMENT_NAME}}", + ), ) { ReceivedEnvelopeEditRoute( popBackStack = popBackStack, @@ -215,6 +221,6 @@ object ReceivedRoute { const val ledgerAddRoute = "ledger-add" fun envelopeAddRoute(categoryName: String, ledgerId: String) = "envelope-add/$categoryName/$ledgerId" - fun envelopeDetailRoute(envelope: String) = "envelope-detail/$envelope" - fun envelopeEditRoute(envelope: String) = "envelope-edit/$envelope" + fun envelopeDetailRoute(envelope: String, ledgerId: String) = "envelope-detail/$envelope/$ledgerId" + fun envelopeEditRoute(envelope: String, ledgerId: String) = "envelope-edit/$envelope/$ledgerId" } From e759b4c2a4a164d4ed713fa2523bdf3aa82fee20 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 15:31:58 +0900 Subject: [PATCH 16/95] =?UTF-8?q?feat:=20=EC=9E=A5=EB=B6=80=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EA=B7=B8=EB=A3=B9=ED=95=91=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../received/received/ReceivedScreen.kt | 60 +++++----------- .../received/component/LedgerCategoryCard.kt | 69 ------------------- 2 files changed, 17 insertions(+), 112 deletions(-) delete mode 100644 feature/received/src/main/java/com/susu/feature/received/received/component/LedgerCategoryCard.kt diff --git a/feature/received/src/main/java/com/susu/feature/received/received/ReceivedScreen.kt b/feature/received/src/main/java/com/susu/feature/received/received/ReceivedScreen.kt index 1a627a29..81cc293a 100644 --- a/feature/received/src/main/java/com/susu/feature/received/received/ReceivedScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/received/ReceivedScreen.kt @@ -57,7 +57,6 @@ import com.susu.feature.received.R import com.susu.feature.received.navigation.argument.FilterArgument import com.susu.feature.received.received.component.LedgerAddCard import com.susu.feature.received.received.component.LedgerCard -import com.susu.feature.received.received.component.LedgerCategoryCard import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.android.awaitFrame import kotlinx.coroutines.launch @@ -236,49 +235,24 @@ fun ReceiveScreen( verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { - if (uiState.isFiltered) { - uiState.ledgerList.groupBy { it.category }.forEach { (category, ledgerList) -> - item( - span = { GridItemSpan(2) }, - contentType = "LedgerCategoryCard", - ) { - LedgerCategoryCard(name = category.name) - } - - items( - items = ledgerList, - key = { it.id }, - ) { ledger -> - LedgerCard( - ledgerType = ledger.category.name, - title = ledger.title, - money = ledger.totalAmounts, - count = ledger.totalCounts, - style = ledger.category.style, - onClick = { onClickLedgerCard(ledger) }, - ) - } - } - } else { - items( - items = uiState.ledgerList, - key = { it.id }, - ) { ledger -> - LedgerCard( - ledgerType = ledger.category.name, - title = ledger.title, - money = ledger.totalAmounts, - count = ledger.totalCounts, - style = ledger.category.style, - onClick = { onClickLedgerCard(ledger) }, - ) - } + items( + items = uiState.ledgerList, + key = { it.id }, + ) { ledger -> + LedgerCard( + ledgerType = ledger.category.name, + title = ledger.title, + money = ledger.totalAmounts, + count = ledger.totalCounts, + style = ledger.category.style, + onClick = { onClickLedgerCard(ledger) }, + ) + } - item { - LedgerAddCard( - onClick = onClickLedgerAddCard, - ) - } + item { + LedgerAddCard( + onClick = onClickLedgerAddCard, + ) } } } diff --git a/feature/received/src/main/java/com/susu/feature/received/received/component/LedgerCategoryCard.kt b/feature/received/src/main/java/com/susu/feature/received/received/component/LedgerCategoryCard.kt deleted file mode 100644 index 268248ab..00000000 --- a/feature/received/src/main/java/com/susu/feature/received/received/component/LedgerCategoryCard.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.susu.feature.received.received.component - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.susu.core.designsystem.theme.Gray40 -import com.susu.core.designsystem.theme.SusuTheme -import com.susu.feature.received.R - -@Composable -fun LedgerCategoryCard( - name: String, -) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), - ) { - HorizontalDivider( - modifier = Modifier.weight(1f), - color = Gray40, - ) - - Text( - text = stringResource(R.string.ledger_category_card_event_category), - style = SusuTheme.typography.title_xxxs, - color = Gray40, - ) - - Box( - modifier = Modifier - .clip(CircleShape) - .size(4.dp) - .background(Gray40), - ) - - Text( - text = name, - style = SusuTheme.typography.title_xxxs, - color = Gray40, - ) - - HorizontalDivider( - modifier = Modifier.weight(1f), - color = Gray40, - ) - } -} - -@Preview(showBackground = true) -@Composable -fun LedgerCategoryCardPreview() { - SusuTheme { - LedgerCategoryCard( - name = "결혼식", - ) - } -} From 77fa521e49ba8fcc6847833bee0121ba742f98c1 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 15:32:35 +0900 Subject: [PATCH 17/95] chore: ktlint --- .../repository/EnvelopesRepositoryImpl.kt | 2 +- .../data/repository/FriendRepositoryImpl.kt | 2 +- .../ReceivedEnvelopeDetailScreen.kt | 11 ++++++---- .../ReceivedEnvelopeDetailViewModel.kt | 3 +-- .../ReceivedEnvelopeEditScreen.kt | 1 - .../ReceivedEnvelopeEditViewModel.kt | 22 +++++++++---------- .../ledgerdetail/LedgerDetailViewModel.kt | 7 ++++-- .../received/received/ReceivedScreen.kt | 1 - 8 files changed, 25 insertions(+), 24 deletions(-) diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index 55f80128..1df24d34 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -123,6 +123,6 @@ class EnvelopesRepositoryImpl @Inject constructor( } else { null }, - ) + ), ).getOrThrow().toModel() } diff --git a/data/src/main/java/com/susu/data/data/repository/FriendRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/FriendRepositoryImpl.kt index 6126cba7..83c80ac2 100644 --- a/data/src/main/java/com/susu/data/data/repository/FriendRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/FriendRepositoryImpl.kt @@ -41,6 +41,6 @@ class FriendRepositoryImpl @Inject constructor( phoneNumber = phoneNumber, relationshipId = relationshipId, customRelation = customRelation, - ) + ), ).getOrThrow() } diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt index 4949e5d5..f1515785 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt @@ -30,7 +30,6 @@ import com.susu.core.ui.DialogToken import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.toMoneyFormat -import com.susu.core.ui.util.to_yyyy_dot_MM_dot_dd import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay import com.susu.feature.received.R import com.susu.feature.received.envelopedetail.component.DetailItem @@ -157,9 +156,13 @@ fun ReceivedEnvelopeDetailScreen( ) DetailItem( categoryText = stringResource(com.susu.core.ui.R.string.word_is_visited), - contentText = if (uiState.envelope.hasVisited == true) stringResource(id = com.susu.core.ui.R.string.word_yes) else stringResource( - id = com.susu.core.ui.R.string.word_no, - ), + contentText = if (uiState.envelope.hasVisited == true) { + stringResource(id = com.susu.core.ui.R.string.word_yes) + } else { + stringResource( + id = com.susu.core.ui.R.string.word_no, + ) + }, isEmptyContent = uiState.envelope.hasVisited == null, ) DetailItem( diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt index d189c751..3c100068 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt @@ -12,7 +12,6 @@ import com.susu.feature.received.navigation.ReceivedRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import kotlinx.serialization.json.Json -import timber.log.Timber import javax.inject.Inject @HiltViewModel @@ -35,7 +34,7 @@ class ReceivedEnvelopeDetailViewModel @Inject constructor( this@ReceivedEnvelopeDetailViewModel.envelope = envelope intent { copy( - envelope = envelope + envelope = envelope, ) } } diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt index a7c8e38b..d22a0466 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditScreen.kt @@ -83,7 +83,6 @@ fun ReceivedEnvelopeEditRoute( viewModel.initData() } - ReceivedEnvelopeEditScreen( uiState = uiState, focusRequester = focusRequester, diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt index cbb5189c..d49350ce 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt @@ -3,15 +3,10 @@ package com.susu.feature.received.envelopeedit import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.model.Envelope -import com.susu.core.model.Friend import com.susu.core.model.Relationship import com.susu.core.ui.base.BaseViewModel -import com.susu.core.ui.base.SideEffect import com.susu.core.ui.extension.decodeFromUri -import com.susu.core.ui.extension.encodeToUri -import com.susu.domain.usecase.envelope.DeleteEnvelopeUseCase import com.susu.domain.usecase.envelope.EditReceivedEnvelopeUseCase -import com.susu.domain.usecase.envelope.GetEnvelopeUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import com.susu.feature.received.navigation.ReceivedRoute import dagger.hilt.android.lifecycle.HiltViewModel @@ -19,7 +14,6 @@ import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import kotlinx.datetime.toKotlinLocalDateTime import kotlinx.serialization.json.Json -import timber.log.Timber import java.time.LocalDateTime import javax.inject.Inject @@ -48,8 +42,11 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( copy( envelope = envelope, relationshipConfig = it.map { - if (it.id == envelope.relationship.id) it.copy(customRelation = envelope.relationship.customRelation) - else it + if (it.id == envelope.relationship.id) { + it.copy(customRelation = envelope.relationship.customRelation) + } else { + it + } }.toPersistentList(), showCustomRelationButton = it.last().id == envelope.relationship.id, isRelationSaved = it.last().id == envelope.relationship.id, @@ -60,7 +57,7 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( fun editReceivedEnvelope() = viewModelScope.launch { editReceivedEnvelopeUseCase( - param = with(currentState) { + param = with(currentState) { EditReceivedEnvelopeUseCase.Param( envelopeId = envelope.id, friendId = envelope.friend.id, @@ -75,7 +72,7 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( handedOverAt = envelope.handedOverAt, hasVisited = envelope.hasVisited, ) - } + }, ).onSuccess { popBackStack() }.onFailure { @@ -83,7 +80,6 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( } } - fun popBackStack() = postSideEffect(ReceivedEnvelopeEditSideEffect.PopBackStack) fun updateMoney(money: String) = intent { @@ -110,7 +106,9 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( relationshipConfig = relationshipConfig.map { if (it.id == envelope.relationship.id) { it.copy(customRelation = customRelation) - } else it + } else { + it + } }.toPersistentList(), ) } diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt index 1457d05b..6b625f1b 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt @@ -69,8 +69,11 @@ class LedgerDetailViewModel @Inject constructor( copy( envelopeList = envelopeList.map { - if (it.envelope.id == searchEnvelope.envelope.id) searchEnvelope - else it + if (it.envelope.id == searchEnvelope.envelope.id) { + searchEnvelope + } else { + it + } }.toPersistentList(), ) } diff --git a/feature/received/src/main/java/com/susu/feature/received/received/ReceivedScreen.kt b/feature/received/src/main/java/com/susu/feature/received/received/ReceivedScreen.kt index 81cc293a..6e4d56f7 100644 --- a/feature/received/src/main/java/com/susu/feature/received/received/ReceivedScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/received/ReceivedScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items From 0a9c5be8aef45e2121ca840d800d626d00e68f55 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 17:46:48 +0900 Subject: [PATCH 18/95] =?UTF-8?q?feat:=20=ED=88=AC=ED=91=9C=20=ED=8E=B8?= =?UTF-8?q?=EC=A7=91=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../navigation/CommunityNavigation.kt | 25 ++- .../votedetail/VoteDetailContract.kt | 2 +- .../community/votedetail/VoteDetailScreen.kt | 5 +- .../votedetail/VoteDetailViewModel.kt | 5 + .../votedetail/component/VoteItem.kt | 4 +- .../community/voteedit/VoteEditContract.kt | 23 +++ .../community/voteedit/VoteEditScreen.kt | 165 ++++++++++++++++++ .../community/voteedit/VoteEditViewModel.kt | 85 +++++++++ .../susu/feature/navigator/MainNavigator.kt | 7 + .../com/susu/feature/navigator/MainScreen.kt | 1 + 10 files changed, 317 insertions(+), 5 deletions(-) create mode 100644 feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditContract.kt create mode 100644 feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt create mode 100644 feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt diff --git a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt index 69bfcba4..66420599 100644 --- a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt +++ b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt @@ -7,12 +7,16 @@ import androidx.navigation.NavOptions import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument +import com.susu.core.model.Vote import com.susu.core.ui.DialogToken import com.susu.core.ui.SnackbarToken +import com.susu.core.ui.extension.encodeToUri import com.susu.feature.community.community.CommunityRoute import com.susu.feature.community.voteadd.VoteAddRoute import com.susu.feature.community.votedetail.VoteDetailRoute +import com.susu.feature.community.voteedit.VoteEditRoute import com.susu.feature.community.votesearch.VoteSearchRoute +import kotlinx.serialization.json.Json fun NavController.navigateVoteAdd() { navigate(CommunityRoute.voteAddRoute) @@ -30,11 +34,16 @@ fun NavController.navigateVoteDetail(voteId: Long) { navigate(CommunityRoute.voteDetailRoute(voteId.toString())) } +fun NavController.navigateVoteEdit(vote: Vote) { + navigate(CommunityRoute.voteEditRoute(Json.encodeToUri(vote))) +} + fun NavGraphBuilder.communityNavGraph( padding: PaddingValues, navigateVoteAdd: () -> Unit, navigateVoteSearch: () -> Unit, navigateVoteDetail: (Long) -> Unit, + navigateVoteEdit: (Vote) -> Unit, popBackStack: () -> Unit, popBackStackWithVote: (String) -> Unit, popBackStackWithToUpdateVote: (String) -> Unit, @@ -79,6 +88,7 @@ fun NavGraphBuilder.communityNavGraph( ) { VoteDetailRoute( popBackStackWithToUpdateVote = popBackStackWithToUpdateVote, + navigateVoteEdit = navigateVoteEdit, handleException = handleException, ) } @@ -86,7 +96,19 @@ fun NavGraphBuilder.communityNavGraph( composable( route = CommunityRoute.voteSearchRoute, ) { - VoteSearchRoute(popBackStack = popBackStack, navigateVoteDetail = navigateVoteDetail) + VoteSearchRoute( + popBackStack = popBackStack, + navigateVoteDetail = navigateVoteDetail, + ) + } + + composable( + route = CommunityRoute.voteEditRoute("{${CommunityRoute.VOTE_ARGUMENT_NAME}}") + ) { + VoteEditRoute( + popBackStack = popBackStack, + handleException = handleException, + ) } } @@ -100,4 +122,5 @@ object CommunityRoute { const val TO_UPDATE_VOTE_ARGUMENT_NAME = "to-update-vote" fun voteDetailRoute(voteId: String) = "vote-detail/$voteId" + fun voteEditRoute(vote: String) = "vote-edit/$vote" } diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt index 665dd016..bf1a967c 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt @@ -11,6 +11,6 @@ data class VoteDetailState( sealed interface VoteDetailSideEffect : SideEffect { data class PopBackStackWithToUpdateVote(val vote: String) : VoteDetailSideEffect - data class PopBackStackWithVote(val vote: String) : VoteDetailSideEffect + data class NavigateVoteEdit(val vote: Vote) : VoteDetailSideEffect data class HandleException(val throwable: Throwable, val retry: () -> Unit) : VoteDetailSideEffect } diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt index 72ba86d4..27a2f15b 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt @@ -44,6 +44,7 @@ import com.susu.core.designsystem.theme.Gray15 import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.Vote import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.util.to_yyyy_dot_MM_dot_dd @@ -58,6 +59,7 @@ import java.time.temporal.ChronoUnit fun VoteDetailRoute( viewModel: VoteDetailViewModel = hiltViewModel(), popBackStackWithToUpdateVote: (String) -> Unit, + navigateVoteEdit: (Vote) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value @@ -65,7 +67,7 @@ fun VoteDetailRoute( when (sideEffect) { is VoteDetailSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) is VoteDetailSideEffect.PopBackStackWithToUpdateVote -> popBackStackWithToUpdateVote(sideEffect.vote) - is VoteDetailSideEffect.PopBackStackWithVote -> TODO() + is VoteDetailSideEffect.NavigateVoteEdit -> navigateVoteEdit(sideEffect.vote) } } @@ -92,6 +94,7 @@ fun VoteDetailRoute( uiState = uiState, currentTime = currentTime, onClickBack = viewModel::popBackStack, + onClickEdit = viewModel::navigateVoteEdit, onClickOption = viewModel::vote, ) } diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt index fe36e27a..6305d116 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt @@ -2,6 +2,7 @@ package com.susu.feature.community.votedetail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import com.susu.core.model.Vote import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.encodeToUri import com.susu.domain.usecase.vote.GetVoteDetailUseCase @@ -21,6 +22,7 @@ class VoteDetailViewModel @Inject constructor( VoteDetailState(), ) { private val voteId = savedStateHandle.get(CommunityRoute.VOTE_ID_ARGUMENT_NAME)!!.toLong() + private var vote: Vote = Vote() private var initCount: Long = 0 private var initIsVoted: Boolean = false @@ -31,6 +33,7 @@ class VoteDetailViewModel @Inject constructor( ).onSuccess { initCount = it.count initIsVoted = it.optionList.any { it.isVoted } + this@VoteDetailViewModel.vote = it intent { copy(vote = it) } }.onFailure { postSideEffect(VoteDetailSideEffect.HandleException(it, ::getVoteDetail)) @@ -89,5 +92,7 @@ class VoteDetailViewModel @Inject constructor( ) } + fun navigateVoteEdit() = postSideEffect(VoteDetailSideEffect.NavigateVoteEdit(vote)) + fun popBackStack() = postSideEffect(VoteDetailSideEffect.PopBackStackWithToUpdateVote(Json.encodeToUri(currentState.vote))) } diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/component/VoteItem.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/component/VoteItem.kt index b62bff56..535f83ab 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/component/VoteItem.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/component/VoteItem.kt @@ -39,8 +39,8 @@ private fun Int.safeDiv(parent: Int): Float { fun VoteItem( showResult: Boolean = true, isPick: Boolean = false, - voteCount: Long = 3, - totalVoteCount: Long = 13, + voteCount: Long = 0, + totalVoteCount: Long = 0, title: String = "", onClick: () -> Unit = {}, ) { diff --git a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditContract.kt b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditContract.kt new file mode 100644 index 00000000..ac70c2d0 --- /dev/null +++ b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditContract.kt @@ -0,0 +1,23 @@ +package com.susu.feature.community.voteedit + +import com.susu.core.model.Category +import com.susu.core.ui.base.SideEffect +import com.susu.core.ui.base.UiState +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf + +data class VoteEditState( + val categoryConfigList: PersistentList = persistentListOf(), + val selectedCategory: Category = Category(), + val voteOptionStateList: PersistentList = persistentListOf(), + val content: String = "", + val isLoading: Boolean = false, +) : UiState { + val buttonEnabled = content.length in 1..50 && + voteOptionStateList.all { it.length in 1..10 } +} + +sealed interface VoteEditSideEffect : SideEffect { + data object PopBackStack : VoteEditSideEffect + data class HandleException(val throwable: Throwable, val retry: () -> Unit) : VoteEditSideEffect +} diff --git a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt new file mode 100644 index 00000000..7ee24734 --- /dev/null +++ b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt @@ -0,0 +1,165 @@ +package com.susu.feature.community.voteedit + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar +import com.susu.core.designsystem.component.appbar.icon.BackIcon +import com.susu.core.designsystem.component.appbar.icon.RegisterText +import com.susu.core.designsystem.component.button.FilledButtonColor +import com.susu.core.designsystem.component.button.SusuFilledButton +import com.susu.core.designsystem.component.button.XSmallButtonStyle +import com.susu.core.designsystem.component.screen.LoadingScreen +import com.susu.core.designsystem.component.textfield.SusuBasicTextField +import com.susu.core.designsystem.component.textfieldbutton.SusuTextFieldFillMaxButton +import com.susu.core.designsystem.component.textfieldbutton.TextFieldButtonColor +import com.susu.core.designsystem.component.textfieldbutton.style.MediumTextFieldButtonStyle +import com.susu.core.designsystem.theme.Gray10 +import com.susu.core.designsystem.theme.Gray100 +import com.susu.core.designsystem.theme.Gray40 +import com.susu.core.designsystem.theme.Orange60 +import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.Category +import com.susu.core.model.Vote +import com.susu.core.ui.extension.collectWithLifecycle +import com.susu.core.ui.extension.susuClickable +import com.susu.feature.community.R +import com.susu.feature.community.community.component.VoteCard +import com.susu.feature.community.votedetail.component.VoteItem + +@Composable +fun VoteEditRoute( + viewModel: VoteEditViewModel = hiltViewModel(), + popBackStack: () -> Unit, + handleException: (Throwable, () -> Unit) -> Unit, +) { + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + viewModel.sideEffect.collectWithLifecycle { sideEffect -> + when (sideEffect) { + is VoteEditSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) + VoteEditSideEffect.PopBackStack -> popBackStack() + } + } + + LaunchedEffect(key1 = Unit) { + viewModel.initData() + viewModel.getCategoryConfig() + } + + VoteEditScreen( + uiState = uiState, + onClickBack = viewModel::popBackStack, + onClickRegister = viewModel::editVote, + onClickCategoryButton = viewModel::selectCategory, + ) +} + +@Composable +fun VoteEditScreen( + uiState: VoteEditState = VoteEditState(), + onClickBack: () -> Unit = {}, + onClickRegister: () -> Unit = {}, + onClickCategoryButton: (Category) -> Unit = {}, + onTextChangeContent: (String) -> Unit = {}, +) { + Column( + modifier = Modifier + .fillMaxSize() + .background(SusuTheme.colorScheme.background10), + ) { + SusuDefaultAppBar( + leftIcon = { + BackIcon( + onClick = onClickBack, + ) + }, + title = "투표 편집", + actions = { + RegisterText( + modifier = Modifier + .padding(end = SusuTheme.spacing.spacing_m) + .susuClickable( + rippleEnabled = false, + runIf = uiState.buttonEnabled, + onClick = onClickRegister, + ), + color = if (uiState.buttonEnabled) Gray100 else Gray40, + ) + }, + ) + + Column( + modifier = Modifier + .padding(SusuTheme.spacing.spacing_m) + .weight(1f, fill = false) + .verticalScroll(rememberScrollState()), + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxxxs), + ) { + uiState.categoryConfigList.forEach { + SusuFilledButton( + color = FilledButtonColor.Black, + style = XSmallButtonStyle.height28, + text = it.name, + isActive = it == uiState.selectedCategory, + onClick = { onClickCategoryButton(it) }, + ) + } + } + + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) + + SusuBasicTextField( + modifier = Modifier.fillMaxWidth(), + text = uiState.content, + onTextChange = onTextChangeContent, + textStyle = SusuTheme.typography.text_xxs, + maxLines = 10, + placeholder = stringResource(R.string.vote_add_screen_textfield_placeholder), + ) + + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_xl)) + + Column( + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + ) { + uiState.voteOptionStateList.forEach { option -> + VoteItem(title = option, showResult = false) + } + } + } + } + + if (uiState.isLoading) { + LoadingScreen() + } +} + +@Preview +@Composable +fun VoteEditScreenPreview() { + SusuTheme { + VoteEditScreen() + } +} diff --git a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt new file mode 100644 index 00000000..a30c8b3b --- /dev/null +++ b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt @@ -0,0 +1,85 @@ +package com.susu.feature.community.voteedit + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import com.susu.core.model.Category +import com.susu.core.model.Vote +import com.susu.core.ui.base.BaseViewModel +import com.susu.core.ui.extension.decodeFromUri +import com.susu.core.ui.extension.encodeToUri +import com.susu.domain.usecase.vote.CreateVoteUseCase +import com.susu.domain.usecase.vote.GetPostCategoryConfigUseCase +import com.susu.feature.community.navigation.CommunityRoute +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.launch +import kotlinx.serialization.json.Json +import javax.inject.Inject + +@HiltViewModel +class VoteEditViewModel @Inject constructor( + private val getPostCategoryConfigUseCase: GetPostCategoryConfigUseCase, + private val createVoteUseCase: CreateVoteUseCase, + savedStateHandle: SavedStateHandle, +) : BaseViewModel( + VoteEditState(), +) { + private val argument = savedStateHandle.get(CommunityRoute.VOTE_ARGUMENT_NAME)!! + private var isFirstVisit = true + + fun initData() { + if (isFirstVisit.not()) return + + Json.decodeFromUri(argument).let { vote -> + intent { + copy( + voteOptionStateList = vote.optionList.map { it.content }.toPersistentList(), + content = vote.content, + selectedCategory = Category() // TODO + ) + } + } + + isFirstVisit = false + } + + fun editVote() = viewModelScope.launch { + intent { copy(isLoading = true) } + createVoteUseCase( + param = CreateVoteUseCase.Param( + content = currentState.content, + optionList = currentState.voteOptionStateList, + categoryId = currentState.selectedCategory.id, + ), + ).onSuccess { + postSideEffect(VoteEditSideEffect.PopBackStack) + }.onFailure { + postSideEffect(VoteEditSideEffect.HandleException(it, ::editVote)) + } + intent { copy(isLoading = false) } + } + + fun getCategoryConfig() = viewModelScope.launch { + if (currentState.categoryConfigList.isNotEmpty()) return@launch + + getPostCategoryConfigUseCase() + .onSuccess { categoryConfig -> + intent { + copy( + categoryConfigList = categoryConfig.toPersistentList(), + selectedCategory = categoryConfig.first(), + ) + } + } + } + + fun popBackStack() = postSideEffect(VoteEditSideEffect.PopBackStack) + + fun selectCategory(category: Category) = intent { + copy(selectedCategory = category) + } + + fun updateContent(content: String) = intent { + copy(content = content) + } +} diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt index 42e9adf7..39db2eef 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt @@ -11,10 +11,12 @@ import androidx.navigation.navOptions import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.Envelope import com.susu.core.model.Ledger +import com.susu.core.model.Vote import com.susu.feature.community.navigation.CommunityRoute import com.susu.feature.community.navigation.navigateCommunity import com.susu.feature.community.navigation.navigateVoteAdd import com.susu.feature.community.navigation.navigateVoteDetail +import com.susu.feature.community.navigation.navigateVoteEdit import com.susu.feature.community.navigation.navigateVoteSearch import com.susu.feature.loginsignup.navigation.LoginSignupRoute import com.susu.feature.mypage.navigation.navigateMyPage @@ -68,6 +70,7 @@ internal class MainNavigator( CommunityRoute.voteAddRoute, CommunityRoute.voteSearchRoute, CommunityRoute.voteDetailRoute("{${CommunityRoute.VOTE_ID_ARGUMENT_NAME}}"), + CommunityRoute.voteEditRoute("{${CommunityRoute.VOTE_ARGUMENT_NAME}}"), ), -> SusuTheme.colorScheme.background10 @@ -188,6 +191,10 @@ internal class MainNavigator( navController.navigateVoteDetail(voteId) } + fun navigateVoteEdit(vote: Vote) { + navController.navigateVoteEdit(vote) + } + fun popBackStackIfNotHome() { if (!isSameCurrentDestination(SentRoute.route)) { navController.popBackStack() diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt index 29b30a9b..d90eb13b 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt @@ -149,6 +149,7 @@ internal fun MainScreen( navigateVoteAdd = navigator::navigateVoteAdd, navigateVoteSearch = navigator::navigateVoteSearch, navigateVoteDetail = navigator::navigateVoteDetail, + navigateVoteEdit = navigator::navigateVoteEdit, popBackStack = navigator::popBackStackIfNotHome, popBackStackWithVote = { vote -> navigator.navController.previousBackStackEntry?.savedStateHandle?.set( From fde7297323e16427d0dc408e4bd019fc9d2db7a9 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 17:52:21 +0900 Subject: [PATCH 19/95] =?UTF-8?q?fix:=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EA=B0=80=20=EC=97=86=EB=8A=94=20=EA=B2=BD=EC=9A=B0,?= =?UTF-8?q?=20=EA=B2=80=EC=83=89=20=EA=B2=B0=EA=B3=BC=20=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EA=B0=80=20=EB=B3=B4=EC=9D=B4=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/votesearch/VoteSearchScreen.kt | 41 ++++++++++--------- .../ledgersearch/LedgerSearchScreen.kt | 39 +++++++++--------- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/feature/community/src/main/java/com/susu/feature/community/votesearch/VoteSearchScreen.kt b/feature/community/src/main/java/com/susu/feature/community/votesearch/VoteSearchScreen.kt index 2b392d1e..cbdd54ba 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votesearch/VoteSearchScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votesearch/VoteSearchScreen.kt @@ -203,29 +203,32 @@ private fun SearchResultColumn( voteList: PersistentList, onClickItem: (Vote) -> Unit, ) { - if (showSearchResultEmpty) { - ResultEmptyColumn( - title = stringResource(R.string.vote_search_screen_search_result_empty_title), + Column( + modifier = Modifier.padding(top = SusuTheme.spacing.spacing_xxl), + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_m), + ) { + Text( + text = stringResource(com.susu.core.ui.R.string.word_search_result), + style = SusuTheme.typography.title_xxs, + color = Gray60, ) - } else { - Column( - modifier = Modifier.padding(top = SusuTheme.spacing.spacing_xxl), - verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_m), - ) { - Text( - text = stringResource(com.susu.core.ui.R.string.word_search_result), - style = SusuTheme.typography.title_xxs, - color = Gray60, + + if (showSearchResultEmpty) { + ResultEmptyColumn( + title = stringResource(R.string.vote_search_screen_search_result_empty_title), + ) + } + + + voteList.forEach { vote -> + SusuRecentSearchContainer( + typeIconId = R.drawable.ic_vote, + text = vote.content, + onClick = { onClickItem(vote) }, ) - voteList.forEach { vote -> - SusuRecentSearchContainer( - typeIconId = R.drawable.ic_vote, - text = vote.content, - onClick = { onClickItem(vote) }, - ) - } } } + } @Preview diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgersearch/LedgerSearchScreen.kt b/feature/received/src/main/java/com/susu/feature/received/ledgersearch/LedgerSearchScreen.kt index 31bb62ed..47579c4a 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgersearch/LedgerSearchScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgersearch/LedgerSearchScreen.kt @@ -203,27 +203,28 @@ private fun SearchResultColumn( ledgerList: PersistentList, onClickItem: (Ledger) -> Unit, ) { - if (showSearchResultEmpty) { - ResultEmptyColumn( - title = stringResource(R.string.ledger_search_screen_empty_search_result_title), + Column( + modifier = Modifier.padding(top = SusuTheme.spacing.spacing_xxl), + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_m), + ) { + Text( + text = stringResource(com.susu.core.ui.R.string.word_search_result), + style = SusuTheme.typography.title_xxs, + color = Gray60, ) - } else { - Column( - modifier = Modifier.padding(top = SusuTheme.spacing.spacing_xxl), - verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_m), - ) { - Text( - text = stringResource(com.susu.core.ui.R.string.word_search_result), - style = SusuTheme.typography.title_xxs, - color = Gray60, + + if (showSearchResultEmpty) { + ResultEmptyColumn( + title = stringResource(R.string.ledger_search_screen_empty_search_result_title), + ) + } + + ledgerList.forEach { ledger -> + SusuRecentSearchContainer( + typeIconId = R.drawable.ic_ledger, + text = ledger.title, + onClick = { onClickItem(ledger) }, ) - ledgerList.forEach { ledger -> - SusuRecentSearchContainer( - typeIconId = R.drawable.ic_ledger, - text = ledger.title, - onClick = { onClickItem(ledger) }, - ) - } } } } From f3230a481a1a114fc9c603048a8c2593b99227f1 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Wed, 31 Jan 2024 18:09:01 +0900 Subject: [PATCH 20/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B8=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=88=98=EC=A0=95=20Usecase=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelope/EditSentEnvelopeUseCase.kt | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt new file mode 100644 index 00000000..42ad74e0 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt @@ -0,0 +1,53 @@ +package com.susu.domain.usecase.envelope + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.core.model.Category +import com.susu.domain.repository.EnvelopesRepository +import com.susu.domain.repository.FriendRepository +import kotlinx.datetime.LocalDateTime +import javax.inject.Inject + +class EditSentEnvelopeUseCase @Inject constructor( + private val friendRepository: FriendRepository, + private val envelopesRepository: EnvelopesRepository, +) { + suspend operator fun invoke(param: Param) = runCatchingIgnoreCancelled { + with(param) { + friendRepository.editFriend( + id = friendId, + name = friendName, + phoneNumber = phoneNumber, + relationshipId = relationshipId, + customRelation = customRelation, + ) + + envelopesRepository.editEnvelope( + id = envelopeId, + type = "SENT", + friendId = friendId, + amount = amount, + gift = gift, + memo = memo, + categoryId = category.id.toLong(), + customCategory = category.customCategory, + hasVisited = hasVisited, + handedOverAt = handedOverAt, + ) + } + } + + data class Param( + val envelopeId: Long, + val friendId: Long, + val friendName: String, + val phoneNumber: String? = null, + val relationshipId: Long, + val customRelation: String? = null, + val category: Category, + val amount: Long, + val gift: String? = null, + val memo: String? = null, + val handedOverAt: LocalDateTime, + val hasVisited: Boolean? = null, + ) +} From b28b8f99299b7a0fca4dc91e7659157e97d46db4 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 18:09:45 +0900 Subject: [PATCH 21/95] =?UTF-8?q?fix:=20=EC=88=98=EC=88=98=20=EC=9D=B5?= =?UTF-8?q?=EB=AA=85=20=ED=94=84=EB=A1=9C=ED=95=84=20border=EC=9D=B4=20?= =?UTF-8?q?=EA=BD=89=20=EC=B0=A8=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/votedetail/VoteDetailScreen.kt | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt index 27a2f15b..9308188e 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -40,6 +41,7 @@ import com.susu.core.designsystem.component.badge.BadgeColor import com.susu.core.designsystem.component.badge.BadgeStyle import com.susu.core.designsystem.component.badge.SusuBadge import com.susu.core.designsystem.component.screen.LoadingScreen +import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray15 import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Orange60 @@ -153,14 +155,23 @@ fun VoteDetailScreen( horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), verticalAlignment = Alignment.CenterVertically, ) { - Image( - modifier = Modifier - .clip(CircleShape) + // border를 사용하지 않은 이유 see -> https://stackoverflow.com/questions/75964726/jetpack-compose-circle-shape-border-not-being-applied-as-expected + Box { + Box(modifier = Modifier .size(20.dp) - .border(width = 1.dp, color = Gray15, shape = CircleShape), - painter = painterResource(id = com.susu.core.ui.R.drawable.img_default_profile), - contentDescription = null, - ) + .clip(CircleShape) + .background(Gray15) + ) + Image( + modifier = Modifier + .size(18.dp) + .clip(CircleShape) + .align(Alignment.Center), + painter = painterResource(id = com.susu.core.ui.R.drawable.img_default_profile), + contentDescription = null, + ) + } + Text(text = stringResource(R.string.word_anonymous_susu), style = SusuTheme.typography.title_xxxs) From 4557df8b84ec8aa6e54bcc1bc047e7b3e6062794 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 21:11:02 +0900 Subject: [PATCH 22/95] =?UTF-8?q?feat:=20EditVoteUseCase,=20DeleteVoteUseC?= =?UTF-8?q?ase=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/VoteRepositoryImpl.kt | 21 +++++++++++++++- .../com/susu/data/remote/api/VoteService.kt | 14 +++++++++++ .../remote/model/request/EditVoteRequest.kt | 9 +++++++ .../susu/domain/repository/VoteRepository.kt | 10 ++++++++ .../domain/usecase/vote/DeleteVoteUseCase.kt | 15 +++++++++++ .../domain/usecase/vote/EditVoteUseCase.kt | 25 +++++++++++++++++++ 6 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 data/src/main/java/com/susu/data/remote/model/request/EditVoteRequest.kt create mode 100644 domain/src/main/java/com/susu/domain/usecase/vote/DeleteVoteUseCase.kt create mode 100644 domain/src/main/java/com/susu/domain/usecase/vote/EditVoteUseCase.kt diff --git a/data/src/main/java/com/susu/data/data/repository/VoteRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/VoteRepositoryImpl.kt index 06976942..aa884d06 100644 --- a/data/src/main/java/com/susu/data/data/repository/VoteRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/VoteRepositoryImpl.kt @@ -4,6 +4,7 @@ import com.susu.core.model.Category import com.susu.core.model.Vote import com.susu.data.remote.api.VoteService import com.susu.data.remote.model.request.CreateVoteRequest +import com.susu.data.remote.model.request.EditVoteRequest import com.susu.data.remote.model.request.VoteOption import com.susu.data.remote.model.request.VoteRequest import com.susu.data.remote.model.response.toModel @@ -54,11 +55,29 @@ class VoteRepositoryImpl @Inject constructor( override suspend fun getVoteDetail(id: Long): Vote = api.getVoteDetail(id).getOrThrow().toModel() - override suspend fun vote(id: Long, isCancel: Boolean, optionId: Long) = api.vote( + override suspend fun vote( + id: Long, + isCancel: Boolean, + optionId: Long, + ) = api.vote( id = id, voteRequest = VoteRequest( isCancel = isCancel, optionId = optionId, ), ).getOrThrow() + + override suspend fun editVote( + id: Long, + boardId: Long, + content: String, + ): Vote = api.editVote( + id = id, + EditVoteRequest( + boardId = boardId, + content = content, + ) + ).getOrThrow().toModel() + + override suspend fun deleteVote(id: Long) = api.deleteVote(id).getOrThrow() } diff --git a/data/src/main/java/com/susu/data/remote/api/VoteService.kt b/data/src/main/java/com/susu/data/remote/api/VoteService.kt index f00e43c7..f860b47f 100644 --- a/data/src/main/java/com/susu/data/remote/api/VoteService.kt +++ b/data/src/main/java/com/susu/data/remote/api/VoteService.kt @@ -1,6 +1,7 @@ package com.susu.data.remote.api import com.susu.data.remote.model.request.CreateVoteRequest +import com.susu.data.remote.model.request.EditVoteRequest import com.susu.data.remote.model.request.VoteRequest import com.susu.data.remote.model.response.PopularVoteResponse import com.susu.data.remote.model.response.PostCategoryConfig @@ -9,7 +10,9 @@ import com.susu.data.remote.model.response.VoteListResponse import com.susu.data.remote.model.response.VoteResponse import com.susu.data.remote.retrofit.ApiResult import retrofit2.http.Body +import retrofit2.http.DELETE import retrofit2.http.GET +import retrofit2.http.PATCH import retrofit2.http.POST import retrofit2.http.Path import retrofit2.http.Query @@ -48,4 +51,15 @@ interface VoteService { @Path("id") id: Long, @Body voteRequest: VoteRequest, ): ApiResult + + @PATCH("votes/{id}") + suspend fun editVote( + @Path("id") id: Long, + @Body editVoteRequest: EditVoteRequest, + ): ApiResult + + @DELETE("votes/{id}") + suspend fun deleteVote( + @Path("id") id: Long, + ): ApiResult } diff --git a/data/src/main/java/com/susu/data/remote/model/request/EditVoteRequest.kt b/data/src/main/java/com/susu/data/remote/model/request/EditVoteRequest.kt new file mode 100644 index 00000000..8f0dda21 --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/request/EditVoteRequest.kt @@ -0,0 +1,9 @@ +package com.susu.data.remote.model.request + +import kotlinx.serialization.Serializable + +@Serializable +data class EditVoteRequest( + val boardId: Long, + val content: String, +) diff --git a/domain/src/main/java/com/susu/domain/repository/VoteRepository.kt b/domain/src/main/java/com/susu/domain/repository/VoteRepository.kt index ad245c3a..3e8a3f40 100644 --- a/domain/src/main/java/com/susu/domain/repository/VoteRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/VoteRepository.kt @@ -33,4 +33,14 @@ interface VoteRepository { isCancel: Boolean, optionId: Long, ) + + suspend fun editVote( + id: Long, + boardId: Long, + content: String, + ): Vote + + suspend fun deleteVote( + id: Long, + ) } diff --git a/domain/src/main/java/com/susu/domain/usecase/vote/DeleteVoteUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/vote/DeleteVoteUseCase.kt new file mode 100644 index 00000000..a2e97342 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/vote/DeleteVoteUseCase.kt @@ -0,0 +1,15 @@ +package com.susu.domain.usecase.vote + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.VoteRepository +import javax.inject.Inject + +class DeleteVoteUseCase @Inject constructor( + private val voteRepository: VoteRepository, +) { + suspend operator fun invoke(id: Long) = runCatchingIgnoreCancelled { + voteRepository.deleteVote( + id = id, + ) + } +} diff --git a/domain/src/main/java/com/susu/domain/usecase/vote/EditVoteUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/vote/EditVoteUseCase.kt new file mode 100644 index 00000000..6b8a19a8 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/vote/EditVoteUseCase.kt @@ -0,0 +1,25 @@ +package com.susu.domain.usecase.vote + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.VoteRepository +import javax.inject.Inject + +class EditVoteUseCase @Inject constructor( + private val voteRepository: VoteRepository, +) { + suspend operator fun invoke(param: Param) = runCatchingIgnoreCancelled { + with(param) { + voteRepository.editVote( + id = id, + boardId = boardId, + content = content, + ) + } + } + + data class Param( + val id: Long, + val boardId: Long, + val content: String, + ) +} From c90c742840f48cb744df607f79f46ee6dfc13386 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 21:20:53 +0900 Subject: [PATCH 23/95] =?UTF-8?q?chore:=20=EB=B4=89=ED=88=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ledgerdetail/LedgerDetailViewModel.kt | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt index 6b625f1b..952d4b90 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt @@ -69,10 +69,18 @@ class LedgerDetailViewModel @Inject constructor( copy( envelopeList = envelopeList.map { - if (it.envelope.id == searchEnvelope.envelope.id) { - searchEnvelope - } else { - it + run { + if (it.envelope.id == searchEnvelope.envelope.id) { + searchEnvelope + } else { + it + } + }.run { + if (friend.id == searchEnvelope.friend.id) copy( + friend = searchEnvelope.friend, + relation = searchEnvelope.relation, + ) + else this } }.toPersistentList(), ) From 04f9ea982941951e9e3ba3d1b3ca0990c57e706e Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 21:27:47 +0900 Subject: [PATCH 24/95] =?UTF-8?q?feat:=20Vote=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EA=B0=92=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/model/src/main/java/com/susu/core/model/Vote.kt | 1 + .../data/remote/model/response/PopularVoteResponse.kt | 5 +++-- .../data/remote/model/response/VoteDetailResponse.kt | 5 +++-- .../susu/data/remote/model/response/VoteResponse.kt | 11 +++++++++-- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/core/model/src/main/java/com/susu/core/model/Vote.kt b/core/model/src/main/java/com/susu/core/model/Vote.kt index 08b5e0a7..3a48bea8 100644 --- a/core/model/src/main/java/com/susu/core/model/Vote.kt +++ b/core/model/src/main/java/com/susu/core/model/Vote.kt @@ -11,6 +11,7 @@ data class Vote( val id: Long = 0, val uid: Long = 0, val profile: Profile = Profile(), + val boardId: Long = 0, val boardName: String = "", val content: String = "", val count: Long = 0, diff --git a/data/src/main/java/com/susu/data/remote/model/response/PopularVoteResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/PopularVoteResponse.kt index bb44daec..62ff276b 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/PopularVoteResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/PopularVoteResponse.kt @@ -8,7 +8,7 @@ import java.time.LocalDateTime @Serializable data class PopularVoteResponse( val id: Long, - val boardName: String, + val board: BoardResponse, val content: String, val count: Long, val isModified: Boolean, @@ -17,7 +17,8 @@ data class PopularVoteResponse( internal fun PopularVoteResponse.toModel() = Vote( id = id, uid = 0, - boardName = boardName, + boardId = board.id, + boardName = board.name, content = content, isModified = isModified, count = count, diff --git a/data/src/main/java/com/susu/data/remote/model/response/VoteDetailResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/VoteDetailResponse.kt index 7cdf4266..5ca1b0f2 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/VoteDetailResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/VoteDetailResponse.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.Serializable data class VoteDetailResponse( val id: Long, val isMine: Boolean, - val boardName: String, + val board: BoardResponse, val content: String, val count: Long, val createdAt: LocalDateTime, @@ -29,7 +29,8 @@ internal fun VoteDetailResponse.toModel() = Vote( id = id, uid = creatorProfile.id, profile = creatorProfile.toModel(), - boardName = boardName, + boardId = board.id, + boardName = board.name, content = content, count = count, isModified = isModified, diff --git a/data/src/main/java/com/susu/data/remote/model/response/VoteResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/VoteResponse.kt index e6ead965..37431197 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/VoteResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/VoteResponse.kt @@ -11,7 +11,7 @@ import kotlinx.serialization.Serializable data class VoteResponse( val id: Long, val uid: Long = 0, - val boardName: String, + val board: BoardResponse, val content: String, val isModified: Boolean, val createdAt: LocalDateTime = java.time.LocalDateTime.now().toKotlinLocalDateTime(), @@ -20,10 +20,17 @@ data class VoteResponse( val optionList: List, ) +@Serializable +data class BoardResponse( + val id: Long, + val name: String, +) + internal fun VoteResponse.toModel() = Vote( id = id, uid = uid, - boardName = boardName, + boardId = board.id, + boardName = board.name, content = content, count = count, isModified = isModified, From c9bd2d01fbbf926cd8c7763257a2bfd9900e52f0 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 21:41:17 +0900 Subject: [PATCH 25/95] =?UTF-8?q?feat:=20=ED=88=AC=ED=91=9C=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/community/CommunityScreen.kt | 2 ++ .../community/community/CommunityViewModel.kt | 15 +++++++++++ .../navigation/CommunityNavigation.kt | 8 ++++-- .../votedetail/VoteDetailContract.kt | 3 +++ .../community/votedetail/VoteDetailScreen.kt | 27 +++++++++++++++++++ .../votedetail/VoteDetailViewModel.kt | 22 +++++++++++++++ .../community/src/main/res/values/strings.xml | 3 +++ .../com/susu/feature/navigator/MainScreen.kt | 7 +++++ 8 files changed, 85 insertions(+), 2 deletions(-) diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt index 2d9ce65a..ead105eb 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt @@ -64,6 +64,7 @@ import java.time.LocalDateTime fun CommunityRoute( padding: PaddingValues, vote: String?, + toDeleteVoteId: Long?, toUpdateVote: String?, viewModel: CommunityViewModel = hiltViewModel(), navigateVoteAdd: () -> Unit, @@ -100,6 +101,7 @@ fun CommunityRoute( viewModel.getPopularVoteList() viewModel.addVoteIfNeed(vote) viewModel.updateVoteIfNeed(toUpdateVote) + viewModel.deleteVoteIfNeed(toDeleteVoteId) } voteListState.OnBottomReached(minItemsCount = 4) { diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt index fc71b6eb..61e5fb63 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt @@ -75,6 +75,21 @@ class CommunityViewModel @Inject constructor( } } + fun deleteVoteIfNeed(toDeleteVoteId: Long?) { + if (toDeleteVoteId == null) return + + intent { + copy( + voteList = voteList + .filter { it.id != toDeleteVoteId } + .toPersistentList(), + popularVoteList = popularVoteList + .filter { it.id != toDeleteVoteId } + .toPersistentList() + ) + } + } + fun initData() { if (isFirstVisit.not()) return getVoteList() diff --git a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt index 66420599..fd5bcad3 100644 --- a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt +++ b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt @@ -47,19 +47,20 @@ fun NavGraphBuilder.communityNavGraph( popBackStack: () -> Unit, popBackStackWithVote: (String) -> Unit, popBackStackWithToUpdateVote: (String) -> Unit, - @Suppress("detekt:UnusedParameter") + popBackStackWithDeleteVoteId: (Long) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, - @Suppress("detekt:UnusedParameter") onShowDialog: (DialogToken) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, ) { composable(route = CommunityRoute.route) { navBackStackEntry -> val vote = navBackStackEntry.savedStateHandle.get(CommunityRoute.VOTE_ARGUMENT_NAME) val toUpdateVote = navBackStackEntry.savedStateHandle.get(CommunityRoute.TO_UPDATE_VOTE_ARGUMENT_NAME) + val toDeleteVoteId = navBackStackEntry.savedStateHandle.get(CommunityRoute.VOTE_ID_ARGUMENT_NAME) CommunityRoute( padding = padding, vote = vote, + toDeleteVoteId = toDeleteVoteId, toUpdateVote = toUpdateVote, navigateVoteAdd = navigateVoteAdd, navigateVoteSearch = navigateVoteSearch, @@ -88,7 +89,10 @@ fun NavGraphBuilder.communityNavGraph( ) { VoteDetailRoute( popBackStackWithToUpdateVote = popBackStackWithToUpdateVote, + popBackStackWithDeleteVoteId = popBackStackWithDeleteVoteId, navigateVoteEdit = navigateVoteEdit, + onShowDialog = onShowDialog, + onShowSnackbar = onShowSnackbar, handleException = handleException, ) } diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt index bf1a967c..22c639d5 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt @@ -10,6 +10,9 @@ data class VoteDetailState( ) : UiState sealed interface VoteDetailSideEffect : SideEffect { + data class PopBackStackWithDeleteVoteId(val voteId: Long) : VoteDetailSideEffect + data class ShowDeleteDialog(val onConfirmRequest: () -> Unit) : VoteDetailSideEffect + data object ShowDeleteSuccessSnackbar : VoteDetailSideEffect data class PopBackStackWithToUpdateVote(val vote: String) : VoteDetailSideEffect data class NavigateVoteEdit(val vote: Vote) : VoteDetailSideEffect data class HandleException(val throwable: Throwable, val retry: () -> Unit) : VoteDetailSideEffect diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt index 9308188e..09b915c9 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt @@ -27,6 +27,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -47,6 +48,8 @@ import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.Vote +import com.susu.core.ui.DialogToken +import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.util.to_yyyy_dot_MM_dot_dd @@ -60,16 +63,39 @@ import java.time.temporal.ChronoUnit @Composable fun VoteDetailRoute( viewModel: VoteDetailViewModel = hiltViewModel(), + popBackStackWithDeleteVoteId: (Long) -> Unit, popBackStackWithToUpdateVote: (String) -> Unit, navigateVoteEdit: (Vote) -> Unit, + onShowSnackbar: (SnackbarToken) -> Unit, + onShowDialog: (DialogToken) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val context = LocalContext.current viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { is VoteDetailSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) is VoteDetailSideEffect.PopBackStackWithToUpdateVote -> popBackStackWithToUpdateVote(sideEffect.vote) is VoteDetailSideEffect.NavigateVoteEdit -> navigateVoteEdit(sideEffect.vote) + is VoteDetailSideEffect.PopBackStackWithDeleteVoteId -> popBackStackWithDeleteVoteId(sideEffect.voteId) + is VoteDetailSideEffect.ShowDeleteDialog -> { + onShowDialog( + DialogToken( + title = context.getString(R.string.delete_vote_dialog_title), + text = context.getString(R.string.delete_vote_dialog_body), + confirmText = context.getString(com.susu.core.ui.R.string.word_delete), + dismissText = context.getString(com.susu.core.ui.R.string.word_cancel), + onConfirmRequest = sideEffect.onConfirmRequest, + ), + ) + } + VoteDetailSideEffect.ShowDeleteSuccessSnackbar -> { + onShowSnackbar( + SnackbarToken( + message = context.getString(R.string.delete_vote_success_toast), + ), + ) + } } } @@ -97,6 +123,7 @@ fun VoteDetailRoute( currentTime = currentTime, onClickBack = viewModel::popBackStack, onClickEdit = viewModel::navigateVoteEdit, + onClickDelete = viewModel::showDeleteDialog, onClickOption = viewModel::vote, ) } diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt index 6305d116..b1ce6818 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt @@ -3,8 +3,10 @@ package com.susu.feature.community.votedetail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.model.Vote +import com.susu.core.model.exception.NotFoundLedgerException import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.encodeToUri +import com.susu.domain.usecase.vote.DeleteVoteUseCase import com.susu.domain.usecase.vote.GetVoteDetailUseCase import com.susu.domain.usecase.vote.VoteUseCase import com.susu.feature.community.navigation.CommunityRoute @@ -15,6 +17,7 @@ import javax.inject.Inject @HiltViewModel class VoteDetailViewModel @Inject constructor( + private val deleteVoteUseCase: DeleteVoteUseCase, private val getVoteDetailUseCase: GetVoteDetailUseCase, private val voteUseCase: VoteUseCase, savedStateHandle: SavedStateHandle, @@ -92,6 +95,25 @@ class VoteDetailViewModel @Inject constructor( ) } + fun showDeleteDialog() = postSideEffect( + VoteDetailSideEffect.ShowDeleteDialog( + onConfirmRequest = ::deleteVote, + ), + ) + + private fun deleteVote() = viewModelScope.launch { + deleteVoteUseCase(voteId) + .onSuccess { + postSideEffect( + VoteDetailSideEffect.ShowDeleteSuccessSnackbar, + VoteDetailSideEffect.PopBackStackWithDeleteVoteId(voteId), + ) + } + .onFailure { throwable -> + postSideEffect(VoteDetailSideEffect.HandleException(throwable, ::deleteVote)) + } + } + fun navigateVoteEdit() = postSideEffect(VoteDetailSideEffect.NavigateVoteEdit(vote)) fun popBackStack() = postSideEffect(VoteDetailSideEffect.PopBackStackWithToUpdateVote(Json.encodeToUri(currentState.vote))) diff --git a/feature/community/src/main/res/values/strings.xml b/feature/community/src/main/res/values/strings.xml index d66fc827..9a41d87e 100644 --- a/feature/community/src/main/res/values/strings.xml +++ b/feature/community/src/main/res/values/strings.xml @@ -20,4 +20,7 @@ 궁금하신 것들의 키워드를\n검색해볼 수 있어요 어떤 투표를 찾아드릴까요? 원하는 검색 결과가 없나요? + 투표를 삭제할까요? + 삭제한 투표는 다시 복구할 수 없어요 + 투표가 삭제되었어요 diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt index d90eb13b..a7a8c49f 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt @@ -165,6 +165,13 @@ internal fun MainScreen( ) navigator.popBackStackIfNotHome() }, + popBackStackWithDeleteVoteId = { voteId -> + navigator.navController.previousBackStackEntry?.savedStateHandle?.set( + CommunityRoute.VOTE_ID_ARGUMENT_NAME, + voteId, + ) + navigator.popBackStackIfNotHome() + }, onShowSnackbar = viewModel::onShowSnackbar, onShowDialog = viewModel::onShowDialog, handleException = viewModel::handleException, From f106f44b0d7fd1305e7a8e90a4b6eeb4bd968826 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Wed, 31 Jan 2024 21:53:04 +0900 Subject: [PATCH 26/95] =?UTF-8?q?feat:=20=ED=88=AC=ED=91=9C=20=ED=8E=B8?= =?UTF-8?q?=EC=A7=91=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../navigation/CommunityNavigation.kt | 1 + .../community/voteedit/VoteEditContract.kt | 3 ++- .../community/voteedit/VoteEditScreen.kt | 26 +++++++++---------- .../community/voteedit/VoteEditViewModel.kt | 21 ++++++++------- .../community/src/main/res/values/strings.xml | 1 + 5 files changed, 28 insertions(+), 24 deletions(-) diff --git a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt index fd5bcad3..df47631f 100644 --- a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt +++ b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt @@ -111,6 +111,7 @@ fun NavGraphBuilder.communityNavGraph( ) { VoteEditRoute( popBackStack = popBackStack, + onShowSnackbar = onShowSnackbar, handleException = handleException, ) } diff --git a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditContract.kt b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditContract.kt index ac70c2d0..c35ef10d 100644 --- a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditContract.kt +++ b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditContract.kt @@ -8,7 +8,7 @@ import kotlinx.collections.immutable.persistentListOf data class VoteEditState( val categoryConfigList: PersistentList = persistentListOf(), - val selectedCategory: Category = Category(), + val selectedBoardId: Long = 0, val voteOptionStateList: PersistentList = persistentListOf(), val content: String = "", val isLoading: Boolean = false, @@ -18,6 +18,7 @@ data class VoteEditState( } sealed interface VoteEditSideEffect : SideEffect { + data object ShowCanNotChangeOptionSnackbar : VoteEditSideEffect data object PopBackStack : VoteEditSideEffect data class HandleException(val throwable: Throwable, val retry: () -> Unit) : VoteEditSideEffect } diff --git a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt index 7ee24734..7efa2ea6 100644 --- a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt @@ -10,15 +10,11 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.res.painterResource +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel @@ -31,33 +27,32 @@ import com.susu.core.designsystem.component.button.SusuFilledButton import com.susu.core.designsystem.component.button.XSmallButtonStyle import com.susu.core.designsystem.component.screen.LoadingScreen import com.susu.core.designsystem.component.textfield.SusuBasicTextField -import com.susu.core.designsystem.component.textfieldbutton.SusuTextFieldFillMaxButton -import com.susu.core.designsystem.component.textfieldbutton.TextFieldButtonColor -import com.susu.core.designsystem.component.textfieldbutton.style.MediumTextFieldButtonStyle -import com.susu.core.designsystem.theme.Gray10 import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray40 -import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.Category -import com.susu.core.model.Vote +import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable import com.susu.feature.community.R -import com.susu.feature.community.community.component.VoteCard import com.susu.feature.community.votedetail.component.VoteItem @Composable fun VoteEditRoute( viewModel: VoteEditViewModel = hiltViewModel(), popBackStack: () -> Unit, + onShowSnackbar: (SnackbarToken) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val context = LocalContext.current viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { is VoteEditSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) VoteEditSideEffect.PopBackStack -> popBackStack() + VoteEditSideEffect.ShowCanNotChangeOptionSnackbar -> onShowSnackbar( + SnackbarToken(message = context.getString(R.string.snackbar_can_not_change_option)) + ) } } @@ -71,6 +66,8 @@ fun VoteEditRoute( onClickBack = viewModel::popBackStack, onClickRegister = viewModel::editVote, onClickCategoryButton = viewModel::selectCategory, + onTextChangeContent = viewModel::updateContent, + onClickOption = viewModel::showCannotChangeOptionSnackbar ) } @@ -81,6 +78,7 @@ fun VoteEditScreen( onClickRegister: () -> Unit = {}, onClickCategoryButton: (Category) -> Unit = {}, onTextChangeContent: (String) -> Unit = {}, + onClickOption: () -> Unit = {}, ) { Column( modifier = Modifier @@ -122,7 +120,7 @@ fun VoteEditScreen( color = FilledButtonColor.Black, style = XSmallButtonStyle.height28, text = it.name, - isActive = it == uiState.selectedCategory, + isActive = it.id == uiState.selectedBoardId.toInt(), onClick = { onClickCategoryButton(it) }, ) } @@ -145,7 +143,7 @@ fun VoteEditScreen( verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { uiState.voteOptionStateList.forEach { option -> - VoteItem(title = option, showResult = false) + VoteItem(title = option, showResult = false, onClick = onClickOption) } } } diff --git a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt index a30c8b3b..319c90b2 100644 --- a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt @@ -6,8 +6,8 @@ import com.susu.core.model.Category import com.susu.core.model.Vote import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri -import com.susu.core.ui.extension.encodeToUri import com.susu.domain.usecase.vote.CreateVoteUseCase +import com.susu.domain.usecase.vote.EditVoteUseCase import com.susu.domain.usecase.vote.GetPostCategoryConfigUseCase import com.susu.feature.community.navigation.CommunityRoute import dagger.hilt.android.lifecycle.HiltViewModel @@ -19,23 +19,25 @@ import javax.inject.Inject @HiltViewModel class VoteEditViewModel @Inject constructor( private val getPostCategoryConfigUseCase: GetPostCategoryConfigUseCase, - private val createVoteUseCase: CreateVoteUseCase, + private val editVoteUseCase: EditVoteUseCase, savedStateHandle: SavedStateHandle, ) : BaseViewModel( VoteEditState(), ) { private val argument = savedStateHandle.get(CommunityRoute.VOTE_ARGUMENT_NAME)!! + private var voteId: Long = 0 private var isFirstVisit = true fun initData() { if (isFirstVisit.not()) return Json.decodeFromUri(argument).let { vote -> + voteId = vote.id intent { copy( voteOptionStateList = vote.optionList.map { it.content }.toPersistentList(), content = vote.content, - selectedCategory = Category() // TODO + selectedBoardId = vote.boardId, ) } } @@ -45,11 +47,11 @@ class VoteEditViewModel @Inject constructor( fun editVote() = viewModelScope.launch { intent { copy(isLoading = true) } - createVoteUseCase( - param = CreateVoteUseCase.Param( + editVoteUseCase( + param = EditVoteUseCase.Param( content = currentState.content, - optionList = currentState.voteOptionStateList, - categoryId = currentState.selectedCategory.id, + boardId = currentState.selectedBoardId, + id = voteId, ), ).onSuccess { postSideEffect(VoteEditSideEffect.PopBackStack) @@ -67,7 +69,6 @@ class VoteEditViewModel @Inject constructor( intent { copy( categoryConfigList = categoryConfig.toPersistentList(), - selectedCategory = categoryConfig.first(), ) } } @@ -76,10 +77,12 @@ class VoteEditViewModel @Inject constructor( fun popBackStack() = postSideEffect(VoteEditSideEffect.PopBackStack) fun selectCategory(category: Category) = intent { - copy(selectedCategory = category) + copy(selectedBoardId = category.id.toLong()) } fun updateContent(content: String) = intent { copy(content = content) } + + fun showCannotChangeOptionSnackbar() = postSideEffect(VoteEditSideEffect.ShowCanNotChangeOptionSnackbar) } diff --git a/feature/community/src/main/res/values/strings.xml b/feature/community/src/main/res/values/strings.xml index 9a41d87e..c37b12fc 100644 --- a/feature/community/src/main/res/values/strings.xml +++ b/feature/community/src/main/res/values/strings.xml @@ -23,4 +23,5 @@ 투표를 삭제할까요? 삭제한 투표는 다시 복구할 수 없어요 투표가 삭제되었어요 + 시작된 투표는 보기를 편집할 수 없어요 From d57c3d2c0625df7ac3181d0b39c24e1b2a664e74 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 11:08:35 +0900 Subject: [PATCH 27/95] =?UTF-8?q?feat:=20=EB=B4=89=ED=88=AC=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=20=EB=BC=88=EB=8C=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopefilter/EnvelopeFilterContract.kt | 17 ++ .../envelopefilter/EnvelopeFilterScreen.kt | 178 ++++++++++++++++++ .../envelopefilter/EnvelopeFilterViewModel.kt | 42 +++++ 3 files changed, 237 insertions(+) create mode 100644 feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt create mode 100644 feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt create mode 100644 feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt new file mode 100644 index 00000000..c5a2e37b --- /dev/null +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt @@ -0,0 +1,17 @@ +package com.susu.feature.envelopefilter + +import com.susu.core.model.Category +import com.susu.core.ui.base.SideEffect +import com.susu.core.ui.base.UiState +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf + +data class EnvelopeFilterState( + val categoryConfig: PersistentList = persistentListOf(), +) : UiState + +sealed interface EnvelopeFilterSideEffect : SideEffect { + data object PopBackStack : EnvelopeFilterSideEffect + data class PopBackStackWithFilter(val filter: String) : EnvelopeFilterSideEffect + data class HandleException(val throwable: Throwable, val retry: () -> Unit) : EnvelopeFilterSideEffect +} diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt new file mode 100644 index 00000000..0aec44ee --- /dev/null +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt @@ -0,0 +1,178 @@ +package com.susu.feature.envelopefilter + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar +import com.susu.core.designsystem.component.appbar.icon.BackIcon +import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuLimitDatePickerBottomSheet +import com.susu.core.designsystem.component.button.FilledButtonColor +import com.susu.core.designsystem.component.button.LinedButtonColor +import com.susu.core.designsystem.component.button.RefreshButton +import com.susu.core.designsystem.component.button.SelectedFilterButton +import com.susu.core.designsystem.component.button.SmallButtonStyle +import com.susu.core.designsystem.component.button.SusuFilledButton +import com.susu.core.designsystem.component.button.SusuLinedButton +import com.susu.core.designsystem.component.button.XSmallButtonStyle +import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.Category +import com.susu.core.ui.extension.collectWithLifecycle +import com.susu.core.ui.util.currentDate +import com.susu.core.ui.util.minDate + +@Composable +fun EnvelopeFilterRoute( + viewModel: EnvelopeFilterViewModel = hiltViewModel(), + popBackStack: () -> Unit, + popBackStackWithFilter: (String) -> Unit, + handleException: (Throwable, () -> Unit) -> Unit, +) { + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + viewModel.sideEffect.collectWithLifecycle { sideEffect -> + when (sideEffect) { + is EnvelopeFilterSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) + EnvelopeFilterSideEffect.PopBackStack -> popBackStack() + is EnvelopeFilterSideEffect.PopBackStackWithFilter -> popBackStackWithFilter(sideEffect.filter) + } + } + + LaunchedEffect(key1 = Unit) { + viewModel.initData() + } + + EnvelopeFilterScreen( + uiState = uiState, + onClickBackIcon = viewModel::popBackStack, + onClickApplyFilterButton = viewModel::popBackStackWithFilter, + ) +} + +@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) +@Composable +fun EnvelopeFilterScreen( + uiState: EnvelopeFilterState = EnvelopeFilterState(), + onClickBackIcon: () -> Unit = {}, + onClickApplyFilterButton: () -> Unit = {}, + onStartDateItemSelected: (Int, Int, Int) -> Unit = { _, _, _ -> }, + onClickStartDateText: () -> Unit = {}, + onDismissStartDateBottomSheet: () -> Unit = {}, + onEndDateItemSelected: (Int, Int, Int) -> Unit = { _, _, _ -> }, + onClickEndDateText: () -> Unit = {}, + onDismissEndDateBottomSheet: () -> Unit = {}, + onClickCategory: (Category) -> Unit = {}, + onClickCategoryClose: (Category) -> Unit = {}, + onClickDateClose: () -> Unit = {}, + onClickRefreshButton: () -> Unit = {}, +) { + Column( + modifier = Modifier + .background(SusuTheme.colorScheme.background10) + .fillMaxSize(), + ) { + SusuDefaultAppBar( + leftIcon = { + BackIcon(onClickBackIcon) + }, + title = stringResource(id = com.susu.core.ui.R.string.word_filter), + ) + + Column( + modifier = Modifier.padding( + top = SusuTheme.spacing.spacing_xl, + start = SusuTheme.spacing.spacing_m, + end = SusuTheme.spacing.spacing_m, + bottom = SusuTheme.spacing.spacing_xxs, + ), + ) { + Text(text = "보낸 사람", style = SusuTheme.typography.title_xs) + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) + Row( + horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + ) { + uiState.categoryConfig.forEach { category -> +// SusuLinedButton( +// color = LinedButtonColor.Black, +// style = XSmallButtonStyle.height28, +// isActive = category in uiState.selectedCategoryList, +// text = category.name, +// onClick = { onClickCategory(category) }, +// ) + } + } + + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_xxxxxxl)) + Text( + text = "받은 봉투 금액", + style = SusuTheme.typography.title_xs, + ) + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) + + Spacer(modifier = Modifier.weight(1f)) + + Column( + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + ) { + FlowRow( + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + ) { +// uiState.selectedCategoryList.forEach { category -> +// SelectedFilterButton( +// name = category.name, +// ) { onClickCategoryClose(category) } +// } +// +// if (uiState.startAt != null || uiState.endAt != null) { +// SelectedFilterButton( +// name = "${uiState.startAt?.to_yyyy_dot_MM_dot_dd() ?: ""}~${uiState.endAt?.to_yyyy_dot_MM_dot_dd() ?: ""}", +// onClickCloseIcon = onClickDateClose, +// ) +// } + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_m), + ) { + RefreshButton(onClick = onClickRefreshButton) + + SusuFilledButton( + modifier = Modifier.fillMaxWidth(), + color = FilledButtonColor.Black, + style = SmallButtonStyle.height48, + isActive = true, + text = stringResource(com.susu.core.ui.R.string.word_apply_filter), + onClick = onClickApplyFilterButton, + ) + } + } + } + } +} + +@Preview +@Composable +fun EnvelopeFilterScreenPreview() { + SusuTheme { + EnvelopeFilterScreen() + } +} diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt new file mode 100644 index 00000000..d711aed9 --- /dev/null +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt @@ -0,0 +1,42 @@ +package com.susu.feature.envelopefilter + +import androidx.lifecycle.SavedStateHandle +import com.susu.core.ui.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class EnvelopeFilterViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, +) : BaseViewModel( + EnvelopeFilterState(), +) { +// private val argument = savedStateHandle.get(ReceivedRoute.FILTER_ARGUMENT_NAME)!! +// private var filter = FilterArgument() + + fun initData() { + initFilter() + } + + private fun initFilter() { +// filter = Json.decodeFromUri(argument) +// intent { +// copy( +// selectedCategoryList = filter.selectedCategoryList.toPersistentList(), +// startAt = filter.startAt?.toJavaLocalDateTime(), +// endAt = filter.endAt?.toJavaLocalDateTime(), +// ) +// } + } + + fun popBackStack() = postSideEffect(EnvelopeFilterSideEffect.PopBackStack) + fun popBackStackWithFilter() { +// val filter = FilterArgument( +// selectedCategoryList = currentState.selectedCategoryList, +// startAt = currentState.startAt?.toKotlinLocalDateTime(), +// endAt = currentState.endAt?.toKotlinLocalDateTime(), +// ) +// +// postSideEffect(EnvelopeFilterSideEffect.PopBackStackWithFilter(Json.encodeToUri(filter))) + } +} From e1b839529c14845b28310a281f2dc039540623e2 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 12:02:52 +0900 Subject: [PATCH 28/95] =?UTF-8?q?feat:=20MoneySlider=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopefilter/component/MoneySlider.kt | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt new file mode 100644 index 00000000..ee2145ff --- /dev/null +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt @@ -0,0 +1,198 @@ +package com.susu.feature.envelopefilter.component + +import androidx.annotation.IntRange +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.RangeSlider +import androidx.compose.material3.RangeSliderState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.lerp +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.DrawScope +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import com.susu.core.designsystem.theme.Gray10 +import com.susu.core.designsystem.theme.Orange20 +import com.susu.core.designsystem.theme.Orange60 +import com.susu.core.designsystem.theme.SusuTheme + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MoneySlider( + modifier: Modifier = Modifier, + height: Dp = 8.dp, + value: ClosedFloatingPointRange, + valueRange: ClosedFloatingPointRange = 0f..1f, + onValueChange: (ClosedFloatingPointRange) -> Unit, + @IntRange(from = 0) + steps: Int = 0 +) { + Box( + contentAlignment = Alignment.Center + ) { + Box(modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .fillMaxWidth() + .height(height) + .background(Orange20) + ) + RangeSlider( + modifier = modifier, + valueRange = valueRange, + value = value, + onValueChange = onValueChange, + steps = steps, + startThumb = { + MoneySliderThumb() + }, + endThumb = { + MoneySliderThumb() + }, + track = { + MoneySliderTrack( + rangeSliderState = it, + height = height, + ) + } + ) + } + +} + +@Composable +private fun MoneySliderThumb() { + Box( + modifier = Modifier + .shadow(elevation = 8.dp, spotColor = Color(0x14000000), ambientColor = Color(0x14000000), clip = true, shape = CircleShape) + .size(24.dp) + .background(color = Gray10, shape = CircleShape) + .padding(SusuTheme.spacing.spacing_xxxs), + ) { + Box( + modifier = Modifier + .size(12.dp) + .background(color = Orange60, shape = CircleShape), + ) + } +} + +@Composable +@ExperimentalMaterial3Api +fun MoneySliderTrack( + modifier: Modifier = Modifier, + rangeSliderState: RangeSliderState, + height: Dp = 8.dp, +) { + Canvas( + modifier + .fillMaxWidth() + .height(height) + ) { + drawTrack( + activeRangeStart = rangeSliderState.coercedActiveRangeStartAsFraction, + activeRangeEnd = rangeSliderState.coercedActiveRangeEndAsFraction, + inactiveTrackColor = Orange20, + activeTrackColor = Orange60 + ) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +internal val RangeSliderState.coercedActiveRangeStartAsFraction + get() = calcFraction( + valueRange.start, + valueRange.endInclusive, + activeRangeStart + ) + +@OptIn(ExperimentalMaterial3Api::class) +internal val RangeSliderState.coercedActiveRangeEndAsFraction + get() = calcFraction( + valueRange.start, + valueRange.endInclusive, + activeRangeEnd + ) + +// Calculate the 0..1 fraction that `pos` value represents between `a` and `b` +private fun calcFraction(a: Float, b: Float, pos: Float) = + (if (b - a == 0f) 0f else (pos - a) / (b - a)).coerceIn(0f, 1f) + +private fun DrawScope.drawTrack( + activeRangeStart: Float, + activeRangeEnd: Float, + inactiveTrackColor: Color, + activeTrackColor: Color, +) { + val isRtl = layoutDirection == LayoutDirection.Rtl + val sliderLeft = Offset(0f, center.y) + val sliderRight = Offset(size.width, center.y) + val sliderStart = if (isRtl) sliderRight else sliderLeft + val sliderEnd = if (isRtl) sliderLeft else sliderRight + val trackStrokeWidth = 8.dp.toPx() + drawLine( + inactiveTrackColor, + sliderStart, + sliderEnd, + trackStrokeWidth, + StrokeCap.Round + ) + val sliderValueEnd = Offset( + sliderStart.x + + (sliderEnd.x - sliderStart.x) * activeRangeEnd, + center.y + ) + + val sliderValueStart = Offset( + sliderStart.x + + (sliderEnd.x - sliderStart.x) * activeRangeStart, + center.y + ) + + drawLine( + activeTrackColor, + sliderValueStart, + sliderValueEnd, + trackStrokeWidth, + StrokeCap.Round + ) +} + +@Preview(showBackground = true) +@Composable +fun MoneySliderPreview() { + var value by remember { + mutableStateOf(10f .. 10f) + } + + SusuTheme { + Column( + modifier = Modifier.padding(10.dp), + ) { + MoneySlider(value = value, valueRange = 10f .. 10f, onValueChange = { value = it }, steps = 10) + } + } +} From c542fc7120769ef024f03536c004e7a05e362aad Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 12:26:13 +0900 Subject: [PATCH 29/95] =?UTF-8?q?feat:=20MoneySlider=20step=20Preview=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopefilter/component/MoneySlider.kt | 42 +++++++++++++++---- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt index ee2145ff..b05eb09f 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt @@ -3,8 +3,10 @@ package com.susu.feature.envelopefilter.component import androidx.annotation.IntRange import androidx.compose.foundation.Canvas import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn @@ -16,6 +18,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.RangeSlider import androidx.compose.material3.RangeSliderState +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -39,6 +42,7 @@ import com.susu.core.designsystem.theme.Gray10 import com.susu.core.designsystem.theme.Orange20 import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme +import kotlin.math.roundToInt @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -48,8 +52,6 @@ fun MoneySlider( value: ClosedFloatingPointRange, valueRange: ClosedFloatingPointRange = 0f..1f, onValueChange: (ClosedFloatingPointRange) -> Unit, - @IntRange(from = 0) - steps: Int = 0 ) { Box( contentAlignment = Alignment.Center @@ -65,7 +67,7 @@ fun MoneySlider( valueRange = valueRange, value = value, onValueChange = onValueChange, - steps = steps, + steps = 0, startThumb = { MoneySliderThumb() }, @@ -184,15 +186,39 @@ private fun DrawScope.drawTrack( @Preview(showBackground = true) @Composable fun MoneySliderPreview() { - var value by remember { - mutableStateOf(10f .. 10f) - } - SusuTheme { + var value by remember { + mutableStateOf(0f .. 3_222f) + } + + val step = when { + value.endInclusive <= 10_000 -> 1f + value.endInclusive <= 10_000 -> 1000f + value.endInclusive <= 1_000_000 -> 10_000f // 0원 ~ 100만원 범위, 1만원 간격 + value.endInclusive <= 5_000_000 -> 50_000f // 101만원 ~ 500만원 범위, 5만원 간격 + else -> 10_0000f // 500만원 이상, 10만원 간격 + } + Column( modifier = Modifier.padding(10.dp), ) { - MoneySlider(value = value, valueRange = 10f .. 10f, onValueChange = { value = it }, steps = 10) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text(text = "${value.start}") + + Text(text = "${value.endInclusive}") + } + MoneySlider( + value = value, + valueRange = 0f..3_222f.roundToStep(step) + step, + onValueChange = { value = it.start.roundToStep(step) .. it.endInclusive.roundToStep(step) }, + ) } } } + +fun Float.roundToStep(step: Float): Float { + return (this / step).roundToInt() * step +} From f5676087b8e395697ac703bd4ed42a53ee759150 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 12:49:29 +0900 Subject: [PATCH 30/95] =?UTF-8?q?feat:=20=EB=B4=89=ED=88=AC=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ledgerfilter/LedgerFilterScreen.kt | 2 +- .../envelopefilter/EnvelopeFilterContract.kt | 5 +- .../envelopefilter/EnvelopeFilterScreen.kt | 71 ++++++++----------- .../envelopefilter/EnvelopeFilterViewModel.kt | 1 + .../envelopefilter/component/MoneySlider.kt | 41 +++++------ feature/sent/src/main/res/values/strings.xml | 2 + 6 files changed, 54 insertions(+), 68 deletions(-) diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerfilter/LedgerFilterScreen.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerfilter/LedgerFilterScreen.kt index 3835428c..af0aa9f8 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerfilter/LedgerFilterScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerfilter/LedgerFilterScreen.kt @@ -172,7 +172,7 @@ fun LedgerFilterScreen( Spacer(modifier = Modifier.weight(1f)) Column( - verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_m), ) { FlowRow( verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt index c5a2e37b..2cba5ee8 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterContract.kt @@ -1,13 +1,10 @@ package com.susu.feature.envelopefilter -import com.susu.core.model.Category import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.persistentListOf data class EnvelopeFilterState( - val categoryConfig: PersistentList = persistentListOf(), + val temp: String = "", ) : UiState sealed interface EnvelopeFilterSideEffect : SideEffect { diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt index 0aec44ee..134a6d07 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterScreen.kt @@ -11,20 +11,16 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon -import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuLimitDatePickerBottomSheet import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.LinedButtonColor import com.susu.core.designsystem.component.button.RefreshButton @@ -34,10 +30,9 @@ import com.susu.core.designsystem.component.button.SusuFilledButton import com.susu.core.designsystem.component.button.SusuLinedButton import com.susu.core.designsystem.component.button.XSmallButtonStyle import com.susu.core.designsystem.theme.SusuTheme -import com.susu.core.model.Category import com.susu.core.ui.extension.collectWithLifecycle -import com.susu.core.ui.util.currentDate -import com.susu.core.ui.util.minDate +import com.susu.feature.envelopefilter.component.MoneySlider +import com.susu.feature.sent.R @Composable fun EnvelopeFilterRoute( @@ -66,21 +61,13 @@ fun EnvelopeFilterRoute( ) } -@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalLayoutApi::class) @Composable fun EnvelopeFilterScreen( + @Suppress("detekt:UnusedParameter") uiState: EnvelopeFilterState = EnvelopeFilterState(), onClickBackIcon: () -> Unit = {}, onClickApplyFilterButton: () -> Unit = {}, - onStartDateItemSelected: (Int, Int, Int) -> Unit = { _, _, _ -> }, - onClickStartDateText: () -> Unit = {}, - onDismissStartDateBottomSheet: () -> Unit = {}, - onEndDateItemSelected: (Int, Int, Int) -> Unit = { _, _, _ -> }, - onClickEndDateText: () -> Unit = {}, - onDismissEndDateBottomSheet: () -> Unit = {}, - onClickCategory: (Category) -> Unit = {}, - onClickCategoryClose: (Category) -> Unit = {}, - onClickDateClose: () -> Unit = {}, onClickRefreshButton: () -> Unit = {}, ) { Column( @@ -103,50 +90,52 @@ fun EnvelopeFilterScreen( bottom = SusuTheme.spacing.spacing_xxs, ), ) { - Text(text = "보낸 사람", style = SusuTheme.typography.title_xs) + Text(text = stringResource(R.string.envelope_filter_screen_friend), style = SusuTheme.typography.title_xs) Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) - Row( + FlowRow( horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { - uiState.categoryConfig.forEach { category -> -// SusuLinedButton( -// color = LinedButtonColor.Black, -// style = XSmallButtonStyle.height28, -// isActive = category in uiState.selectedCategoryList, -// text = category.name, -// onClick = { onClickCategory(category) }, -// ) + listOf("이진욱", "김철수", "홍길동", "박예은", "박미영", "서한누리", "서한누리").forEach { category -> + SusuLinedButton( + color = LinedButtonColor.Black, + style = XSmallButtonStyle.height28, + isActive = true, + text = category, + onClick = { }, + ) } } Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_xxxxxxl)) Text( - text = "받은 봉투 금액", + text = stringResource(R.string.envelope_filter_screen_money), style = SusuTheme.typography.title_xs, ) Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) + Text(text = "20,000원~100,000원", style = SusuTheme.typography.title_m) + + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_xxs)) + + MoneySlider(value = 20_000f..100_000f, onValueChange = {}, valueRange = 0f..100_000f) + Spacer(modifier = Modifier.weight(1f)) Column( - verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_m), ) { FlowRow( verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), horizontalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { -// uiState.selectedCategoryList.forEach { category -> -// SelectedFilterButton( -// name = category.name, -// ) { onClickCategoryClose(category) } -// } -// -// if (uiState.startAt != null || uiState.endAt != null) { -// SelectedFilterButton( -// name = "${uiState.startAt?.to_yyyy_dot_MM_dot_dd() ?: ""}~${uiState.endAt?.to_yyyy_dot_MM_dot_dd() ?: ""}", -// onClickCloseIcon = onClickDateClose, -// ) -// } + SelectedFilterButton( + name = "이진욱", + ) + + SelectedFilterButton( + name = "20,000~10,000", + ) } Row( diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt index d711aed9..b16f4b49 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/EnvelopeFilterViewModel.kt @@ -7,6 +7,7 @@ import javax.inject.Inject @HiltViewModel class EnvelopeFilterViewModel @Inject constructor( + @Suppress("detekt:UnusedPrivateProperty") savedStateHandle: SavedStateHandle, ) : BaseViewModel( EnvelopeFilterState(), diff --git a/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt b/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt index b05eb09f..ea36a7e0 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopefilter/component/MoneySlider.kt @@ -1,6 +1,5 @@ package com.susu.feature.envelopefilter.component -import androidx.annotation.IntRange import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -9,7 +8,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -29,9 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.lerp import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.tooling.preview.Preview @@ -54,13 +50,15 @@ fun MoneySlider( onValueChange: (ClosedFloatingPointRange) -> Unit, ) { Box( - contentAlignment = Alignment.Center + modifier = Modifier.height(24.dp), + contentAlignment = Alignment.Center, ) { - Box(modifier = Modifier - .clip(RoundedCornerShape(4.dp)) - .fillMaxWidth() - .height(height) - .background(Orange20) + Box( + modifier = Modifier + .clip(RoundedCornerShape(4.dp)) + .fillMaxWidth() + .height(height) + .background(Orange20), ) RangeSlider( modifier = modifier, @@ -79,10 +77,9 @@ fun MoneySlider( rangeSliderState = it, height = height, ) - } + }, ) } - } @Composable @@ -112,13 +109,13 @@ fun MoneySliderTrack( Canvas( modifier .fillMaxWidth() - .height(height) + .height(height), ) { drawTrack( activeRangeStart = rangeSliderState.coercedActiveRangeStartAsFraction, activeRangeEnd = rangeSliderState.coercedActiveRangeEndAsFraction, inactiveTrackColor = Orange20, - activeTrackColor = Orange60 + activeTrackColor = Orange60, ) } } @@ -128,7 +125,7 @@ internal val RangeSliderState.coercedActiveRangeStartAsFraction get() = calcFraction( valueRange.start, valueRange.endInclusive, - activeRangeStart + activeRangeStart, ) @OptIn(ExperimentalMaterial3Api::class) @@ -136,7 +133,7 @@ internal val RangeSliderState.coercedActiveRangeEndAsFraction get() = calcFraction( valueRange.start, valueRange.endInclusive, - activeRangeEnd + activeRangeEnd, ) // Calculate the 0..1 fraction that `pos` value represents between `a` and `b` @@ -160,18 +157,18 @@ private fun DrawScope.drawTrack( sliderStart, sliderEnd, trackStrokeWidth, - StrokeCap.Round + StrokeCap.Round, ) val sliderValueEnd = Offset( sliderStart.x + (sliderEnd.x - sliderStart.x) * activeRangeEnd, - center.y + center.y, ) val sliderValueStart = Offset( sliderStart.x + (sliderEnd.x - sliderStart.x) * activeRangeStart, - center.y + center.y, ) drawLine( @@ -179,7 +176,7 @@ private fun DrawScope.drawTrack( sliderValueStart, sliderValueEnd, trackStrokeWidth, - StrokeCap.Round + StrokeCap.Round, ) } @@ -188,7 +185,7 @@ private fun DrawScope.drawTrack( fun MoneySliderPreview() { SusuTheme { var value by remember { - mutableStateOf(0f .. 3_222f) + mutableStateOf(0f..3_222f) } val step = when { @@ -213,7 +210,7 @@ fun MoneySliderPreview() { MoneySlider( value = value, valueRange = 0f..3_222f.roundToStep(step) + step, - onValueChange = { value = it.start.roundToStep(step) .. it.endInclusive.roundToStep(step) }, + onValueChange = { value = it.start.roundToStep(step)..it.endInclusive.roundToStep(step) }, ) } } diff --git a/feature/sent/src/main/res/values/strings.xml b/feature/sent/src/main/res/values/strings.xml index 9c9f206a..be7042f6 100644 --- a/feature/sent/src/main/res/values/strings.xml +++ b/feature/sent/src/main/res/values/strings.xml @@ -53,4 +53,6 @@ "%s을 " %s님에게 언제 보냈나요 %s님에게 + 보낸 사람 + 받은 봉투 금액 From 0f6a9b847661d2537ada3f842b13111e39bb0b75 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 00:53:08 +0900 Subject: [PATCH 31/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B8=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=88=98=EC=A0=95=20UI=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopeedit/SentEnvelopeEditContract.kt | 22 ++ .../envelopeedit/SentEnvelopeEditScreen.kt | 238 ++++++++++-------- .../envelopeedit/SentEnvelopeEditViewModel.kt | 139 +++++++++- 3 files changed, 294 insertions(+), 105 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt index 966772fa..ab48bc6b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt @@ -1,12 +1,34 @@ package com.susu.feature.envelopeedit +import com.susu.core.model.Category +import com.susu.core.model.Relationship import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState +import com.susu.core.ui.util.currentDate +import java.time.LocalDateTime + data class SentEnvelopeEditState( val isLoading: Boolean = false, + val categoryConfig: List = emptyList(), + val relationshipConfig: List = emptyList(), + val amount: Long = 0L, + val gift: String? = null, + val memo: String? = null, + val hasVisited: Boolean? = null, + val handedOverAt: LocalDateTime = currentDate, + val friendName: String = "", + val relationshipId: Long = 0, + val customRelationship: String? = null, + val phoneNumber: String? = null, + val categoryId: Int = 0, + val customCategory: String? = null, + val showCustomCategory: Boolean = false, + val showCustomRelationship: Boolean = false, + val showDatePickerSheet: Boolean = false, ) : UiState sealed interface SentEnvelopeEditSideEffect : SideEffect { data object PopBackStack : SentEnvelopeEditSideEffect + data class HandleException(val throwable: Throwable, val retry: () -> Unit) : SentEnvelopeEditSideEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 7c6e24a9..79ad0307 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -15,10 +15,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.RectangleShape @@ -27,6 +24,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuDatePickerBottomSheet @@ -37,13 +35,19 @@ import com.susu.core.designsystem.component.button.SmallButtonStyle import com.susu.core.designsystem.component.button.SusuFilledButton import com.susu.core.designsystem.component.textfield.SusuBasicTextField import com.susu.core.designsystem.component.textfield.SusuPriceTextField +import com.susu.core.designsystem.component.textfieldbutton.SusuTextFieldWrapContentButton +import com.susu.core.designsystem.component.textfieldbutton.TextFieldButtonColor +import com.susu.core.designsystem.component.textfieldbutton.style.SmallTextFieldButtonStyle import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray30 import com.susu.core.designsystem.theme.Gray40 import com.susu.core.designsystem.theme.Gray70 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.Category +import com.susu.core.model.Relationship import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable +import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay import com.susu.feature.envelopeedit.component.EditDetailItem import com.susu.feature.sent.R @@ -53,15 +57,43 @@ fun SentEnvelopeEditRoute( popBackStack: () -> Unit, navigateSentEnvelopeDetail: () -> Unit, ) { + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeEditSideEffect.PopBackStack -> popBackStack() + is SentEnvelopeEditSideEffect.HandleException -> {} + } + } + + LaunchedEffect(key1 = Unit) { + viewModel.run { + initData() + getEnvelopConfig() } } SentEnvelopeEditScreen( + uiState = uiState, onClickBackIcon = viewModel::popBackStack, onClickSave = navigateSentEnvelopeDetail, + onMoneyUpdated = viewModel::updateAmount, + onSelectCategory = { viewModel.updateCategoryId(it.id) }, + onClickCustomCategoryAdd = viewModel::showCustomCategoryInput, + onCustomCategoryUpdated = viewModel::updateCustomCategory, + onCustomCategoryCleared = { viewModel.updateCustomCategory("") }, + onFriendNameUpdated = viewModel::updateFriendName, + onSelectRelationship = { viewModel.updateRelationshipId(it.id) }, + onClickCustomRelationshipAdd = viewModel::showCustomRelationshipInput, + onCustomRelationshipUpdated = viewModel::updateCustomRelationship, + onCustomRelationshipCleared = { viewModel.updateCustomRelationship("") }, + onClickDateText = viewModel::showDatePickerSheet, + onHasVisitedUpdated = viewModel::updateHasVisited, + onGiftUpdated = viewModel::updateGift, + onMemoUpdated = viewModel::updateMemo, + onPhoneNumberUpdated = viewModel::updatePhoneNumber, + onDateUpdated = viewModel::updateHandedOverAt, + onDatePickerSheetDismissed = viewModel::hideDatePickerSheet, ) } @@ -69,17 +101,27 @@ fun SentEnvelopeEditRoute( @Composable fun SentEnvelopeEditScreen( modifier: Modifier = Modifier, + uiState: SentEnvelopeEditState = SentEnvelopeEditState(), onClickBackIcon: () -> Unit = {}, onClickSave: () -> Unit = {}, + onMoneyUpdated: (Long) -> Unit = {}, + onSelectCategory: (Category) -> Unit = {}, + onClickCustomCategoryAdd: () -> Unit = {}, + onCustomCategoryUpdated: (String) -> Unit = {}, + onCustomCategoryCleared: () -> Unit = {}, + onFriendNameUpdated: (String) -> Unit = {}, + onSelectRelationship: (Relationship) -> Unit = {}, + onClickCustomRelationshipAdd: () -> Unit = {}, + onCustomRelationshipUpdated: (String) -> Unit = {}, + onCustomRelationshipCleared: () -> Unit = {}, + onClickDateText: () -> Unit = {}, + onHasVisitedUpdated: (Boolean) -> Unit = {}, + onGiftUpdated: (String) -> Unit = {}, + onMemoUpdated: (String) -> Unit = {}, + onPhoneNumberUpdated: (String) -> Unit = {}, + onDateUpdated: (year: Int, month: Int, day: Int) -> Unit = { _, _, _ -> }, + onDatePickerSheetDismissed: () -> Unit = {}, ) { - // TODO: 수정 필요 - var money by remember { mutableStateOf(150000) } - var name by remember { mutableStateOf("김철수") } - var present by remember { mutableStateOf("") } - var phone by remember { mutableStateOf("") } - var memo by remember { mutableStateOf("") } - var isSheetOpen by remember { mutableStateOf(false) } - Box( modifier = modifier .fillMaxSize() @@ -94,7 +136,7 @@ fun SentEnvelopeEditScreen( }, ) Column( - modifier = modifier + modifier = Modifier .verticalScroll(rememberScrollState()) .weight(1f) .padding( @@ -104,110 +146,92 @@ fun SentEnvelopeEditScreen( ), ) { SusuPriceTextField( - text = money.toString(), - onTextChange = { money = it.toInt() }, + text = uiState.amount.toString(), + onTextChange = { onMoneyUpdated(it.toLongOrNull() ?: 0L) }, textStyle = SusuTheme.typography.title_xxl, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) - Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_m)) + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_event), categoryTextAlign = Alignment.Top, ) { - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_event_wedding), - isActive = true, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_event_first_birth), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_edit_funeral), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_edit_birthday), - isActive = false, - onClick = {}, - ) + uiState.categoryConfig.forEach { category -> + SusuFilledButton( + color = FilledButtonColor.Orange, + style = SmallButtonStyle.height32, + text = category.name, + isActive = category.id == uiState.categoryId, + onClick = { onSelectCategory(category) }, + ) + } AddConditionButton( - onClick = {}, + onClick = onClickCustomCategoryAdd, ) + if (uiState.showCustomCategory) { + SusuTextFieldWrapContentButton( + style = SmallTextFieldButtonStyle.height32, + color = TextFieldButtonColor.Orange, + text = uiState.customCategory ?: "", + onTextChange = onCustomCategoryUpdated, + onClickClearIcon = onCustomCategoryCleared, + onClickCloseIcon = { /* 무슨 동작을 해야하지?? */ }, + ) + } } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_name), categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = name, - onTextChange = { name = it }, + text = uiState.friendName, + onTextChange = onFriendNameUpdated, placeholder = stringResource(R.string.sent_envelope_edit_category_name_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_relationship), categoryTextAlign = Alignment.Top, ) { - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_relationship_friend), - isActive = true, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_relationship_family), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_relationship_relatives), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_relationship_colleague), - isActive = false, - onClick = {}, - ) + uiState.relationshipConfig.forEach { relationship -> + SusuFilledButton( + color = FilledButtonColor.Orange, + style = SmallButtonStyle.height32, + text = relationship.relation, + isActive = relationship.id == uiState.relationshipId, + onClick = { onSelectRelationship(relationship) }, + ) + } AddConditionButton( - onClick = {}, + onClick = onClickCustomRelationshipAdd, ) + if (uiState.showCustomRelationship) { + SusuTextFieldWrapContentButton( + style = SmallTextFieldButtonStyle.height32, + color = TextFieldButtonColor.Orange, + text = uiState.customRelationship ?: "", + onTextChange = onCustomRelationshipUpdated, + onClickClearIcon = onCustomRelationshipCleared, + onClickCloseIcon = { /* 무슨 동작을 해야하지?? */ }, + ) + } } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_date), categoryTextAlign = Alignment.CenterVertically, ) { Text( - text = "2023년 11월 25일", + text = uiState.handedOverAt.to_yyyy_korYear_M_korMonth_d_korDay(), style = SusuTheme.typography.title_s, color = Gray100, - modifier = modifier + modifier = Modifier .fillMaxWidth() .susuClickable( rippleEnabled = false, - onClick = { isSheetOpen = true }, + onClick = onClickDateText, ), ) } @@ -219,68 +243,68 @@ fun SentEnvelopeEditScreen( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, text = stringResource(R.string.sent_envelope_edit_category_visited_yes), - isActive = true, - onClick = {}, - modifier = modifier.weight(1f), + isActive = uiState.hasVisited == true, + onClick = { onHasVisitedUpdated(true) }, + modifier = Modifier.weight(1f), ) SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, text = stringResource(R.string.sent_envelope_edit_category_visited_no), - isActive = false, - onClick = {}, - modifier = modifier.weight(1f), + isActive = uiState.hasVisited == false, + onClick = { onHasVisitedUpdated(false) }, + modifier = Modifier.weight(1f), ) } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_present), - categoryTextColor = if (present.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (uiState.gift != null) Gray70 else Gray40, categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = present, - onTextChange = { present = it }, + text = uiState.gift ?: "", + onTextChange = onGiftUpdated, placeholder = stringResource(R.string.sent_envelope_edit_category_present_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_phone), - categoryTextColor = if (phone.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (uiState.phoneNumber != null) Gray70 else Gray40, categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = phone, - onTextChange = { phone = it }, + text = uiState.phoneNumber ?: "", + onTextChange = onPhoneNumberUpdated, placeholder = stringResource(R.string.sent_envelope_edit_category_phone_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_memo), - categoryTextColor = if (memo.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (uiState.memo != null) Gray70 else Gray40, categoryTextAlign = Alignment.Top, ) { SusuBasicTextField( - text = memo, - onTextChange = { memo = it }, + text = uiState.memo ?: "", + onTextChange = onMemoUpdated, placeholder = stringResource(R.string.sent_envelope_edit_category_memo_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, maxLines = 2, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } - Spacer(modifier = modifier.size(240.dp)) + Spacer(modifier = Modifier.size(240.dp)) } SusuFilledButton( - modifier = modifier + modifier = Modifier .fillMaxWidth() .imePadding(), color = FilledButtonColor.Black, @@ -291,11 +315,17 @@ fun SentEnvelopeEditScreen( ) } - // DatePickerBottomSheet - if (isSheetOpen) { + if (uiState.showDatePickerSheet) { SusuDatePickerBottomSheet( maximumContainerHeight = 346.dp, - onDismissRequest = { _, _, _ -> isSheetOpen = false }, + initialYear = uiState.handedOverAt.year, + initialMonth = uiState.handedOverAt.monthValue, + initialDay = uiState.handedOverAt.dayOfMonth, + onDismissRequest = { year, month, day -> + onDateUpdated(year, month, day) + onDatePickerSheetDismissed() + }, + onItemSelected = { year, month, day -> onDateUpdated(year, month, day) }, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index 63d1dbe8..eba0d666 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -1,12 +1,149 @@ package com.susu.feature.envelopeedit +import androidx.lifecycle.viewModelScope +import com.susu.core.model.Category +import com.susu.core.model.Envelope +import com.susu.core.model.Friend +import com.susu.core.model.Relationship import com.susu.core.ui.base.BaseViewModel +import com.susu.core.ui.util.currentDate +import com.susu.core.ui.util.getSafeLocalDateTime +import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase +import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import kotlinx.datetime.toJavaLocalDateTime +import kotlinx.datetime.toKotlinLocalDateTime import javax.inject.Inject @HiltViewModel -class SentEnvelopeEditViewModel @Inject constructor() : BaseViewModel( +class SentEnvelopeEditViewModel @Inject constructor( + private val getCategoryConfigUseCase: GetCategoryConfigUseCase, + private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, +) : BaseViewModel( SentEnvelopeEditState(), ) { + + fun initData() { + // TODO: Argument로 받은 데이터로 설정. UI 개발을 위해 임의의 데이터 사용. + val category = Category( + id = 1, + name = "결혼식", + ) + val envelope = Envelope( + amount = 150000, + hasVisited = true, + handedOverAt = currentDate.toKotlinLocalDateTime(), + friend = Friend( + name = "김철수", + ), + relationship = Relationship( + id = 1, + relation = "친구", + ), + ) + + intent { + copy( + amount = envelope.amount, + gift = envelope.gift, + memo = envelope.memo, + hasVisited = envelope.hasVisited, + handedOverAt = envelope.handedOverAt.toJavaLocalDateTime(), + friendName = envelope.friend.name, + relationshipId = envelope.relationship.id, + customRelationship = envelope.relationship.customRelation, + phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, + categoryId = category.id, + customCategory = category.customCategory, + ) + } + } + + fun getEnvelopConfig() { + viewModelScope.launch { + getRelationShipConfigListUseCase().onSuccess { + intent { copy(relationshipConfig = it.dropLast(1)) } + } + getCategoryConfigUseCase().onSuccess { + intent { copy(categoryConfig = it.dropLast(1)) } + } + } + } + fun popBackStack() = postSideEffect(SentEnvelopeEditSideEffect.PopBackStack) + + fun updateAmount(amount: Long) { + intent { copy(amount = amount) } + } + + fun updateGift(gift: String?) { + intent { copy(gift = gift?.ifEmpty { null }) } + } + + fun updateMemo(memo: String?) { + intent { copy(memo = memo?.ifEmpty { null }) } + } + + fun updateHasVisited(hasVisited: Boolean?) { + intent { + if (hasVisited == currentState.hasVisited) { + copy(hasVisited = null) + } else { + copy(hasVisited = hasVisited) + } + } + } + + fun updateHandedOverAt(year: Int, month: Int, day: Int) { + intent { copy(handedOverAt = getSafeLocalDateTime(year = year, month = month, day = day)) } + } + + fun updateFriendName(name: String) { + intent { copy(friendName = name) } + } + + fun updateRelationshipId(relationshipId: Long) { + intent { copy(relationshipId = relationshipId) } + } + + fun updateCustomRelationship(customRelationship: String) { + intent { copy(customRelationship = customRelationship) } + } + + fun updatePhoneNumber(phoneNumber: String?) { + intent { copy(phoneNumber = phoneNumber?.ifEmpty { null }) } + } + + fun updateCategoryId(categoryId: Int) { + intent { copy(categoryId = categoryId) } + } + + fun updateCustomCategory(customCategory: String?) { + intent { copy(customCategory = customCategory) } + } + + fun showCustomCategoryInput() { + intent { copy(showCustomCategory = true) } + } + + fun hideCustomCategoryInput() { + intent { copy(showCustomCategory = false) } + } + + fun showCustomRelationshipInput() { + intent { copy(showCustomRelationship = true) } + } + + fun hideCustomRelationshipInput() { + intent { copy(showCustomRelationship = false) } + } + + fun showDatePickerSheet() { + intent { copy(showDatePickerSheet = true) } + } + + fun hideDatePickerSheet() { + intent { copy(showDatePickerSheet = false) } + } } From 9d3bf3cf3963500ed532445aabfbc54c5322f403 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 14:23:52 +0900 Subject: [PATCH 32/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B8=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=88=98=EC=A0=95=20custom=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC,=20=EA=B4=80=EA=B3=84=20=EC=9E=85=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopeedit/SentEnvelopeEditContract.kt | 5 +- .../envelopeedit/SentEnvelopeEditScreen.kt | 72 ++++++++++++++++--- .../envelopeedit/SentEnvelopeEditViewModel.kt | 34 +++++++-- 3 files changed, 95 insertions(+), 16 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt index ab48bc6b..70ece152 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt @@ -7,7 +7,6 @@ import com.susu.core.ui.base.UiState import com.susu.core.ui.util.currentDate import java.time.LocalDateTime - data class SentEnvelopeEditState( val isLoading: Boolean = false, val categoryConfig: List = emptyList(), @@ -20,9 +19,11 @@ data class SentEnvelopeEditState( val friendName: String = "", val relationshipId: Long = 0, val customRelationship: String? = null, + val customRelationshipSaved: Boolean = false, val phoneNumber: String? = null, val categoryId: Int = 0, val customCategory: String? = null, + val customCategorySaved: Boolean = false, val showCustomCategory: Boolean = false, val showCustomRelationship: Boolean = false, val showDatePickerSheet: Boolean = false, @@ -31,4 +32,6 @@ data class SentEnvelopeEditState( sealed interface SentEnvelopeEditSideEffect : SideEffect { data object PopBackStack : SentEnvelopeEditSideEffect data class HandleException(val throwable: Throwable, val retry: () -> Unit) : SentEnvelopeEditSideEffect + data object FocusCustomCategory : SentEnvelopeEditSideEffect + data object FocusCustomRelationship : SentEnvelopeEditSideEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 79ad0307..cef9ae82 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -1,5 +1,6 @@ package com.susu.feature.envelopeedit +import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,8 +17,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType @@ -50,6 +54,8 @@ import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay import com.susu.feature.envelopeedit.component.EditDetailItem import com.susu.feature.sent.R +import kotlinx.coroutines.android.awaitFrame +import kotlinx.coroutines.launch @Composable fun SentEnvelopeEditRoute( @@ -58,11 +64,27 @@ fun SentEnvelopeEditRoute( navigateSentEnvelopeDetail: () -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val categoryFocusRequester = remember { FocusRequester() } + val relationshipFocusRequester = remember { FocusRequester() } + val scope = rememberCoroutineScope() viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeEditSideEffect.PopBackStack -> popBackStack() is SentEnvelopeEditSideEffect.HandleException -> {} + SentEnvelopeEditSideEffect.FocusCustomCategory -> { + scope.launch { + awaitFrame() + categoryFocusRequester.requestFocus() + } + } + + SentEnvelopeEditSideEffect.FocusCustomRelationship -> { + scope.launch { + awaitFrame() + relationshipFocusRequester.requestFocus() + } + } } } @@ -73,6 +95,10 @@ fun SentEnvelopeEditRoute( } } + BackHandler { + popBackStack() + } + SentEnvelopeEditScreen( uiState = uiState, onClickBackIcon = viewModel::popBackStack, @@ -94,6 +120,12 @@ fun SentEnvelopeEditRoute( onPhoneNumberUpdated = viewModel::updatePhoneNumber, onDateUpdated = viewModel::updateHandedOverAt, onDatePickerSheetDismissed = viewModel::hideDatePickerSheet, + categoryFocusRequester = categoryFocusRequester, + relationshipFocusRequester = relationshipFocusRequester, + onCustomCategoryClosed = viewModel::hideCustomCategoryInput, + onCustomCategoryInnerButtonClicked = viewModel::toggleCustomCategoryInputSaved, + onCustomRelationshipClosed = viewModel::hideCustomRelationshipInput, + onCustomRelationshipInnerButtonClicked = viewModel::toggleCustomRelationshipInputSaved, ) } @@ -102,6 +134,8 @@ fun SentEnvelopeEditRoute( fun SentEnvelopeEditScreen( modifier: Modifier = Modifier, uiState: SentEnvelopeEditState = SentEnvelopeEditState(), + categoryFocusRequester: FocusRequester = remember { FocusRequester() }, + relationshipFocusRequester: FocusRequester = remember { FocusRequester() }, onClickBackIcon: () -> Unit = {}, onClickSave: () -> Unit = {}, onMoneyUpdated: (Long) -> Unit = {}, @@ -109,11 +143,15 @@ fun SentEnvelopeEditScreen( onClickCustomCategoryAdd: () -> Unit = {}, onCustomCategoryUpdated: (String) -> Unit = {}, onCustomCategoryCleared: () -> Unit = {}, + onCustomCategoryClosed: () -> Unit = {}, + onCustomCategoryInnerButtonClicked: () -> Unit = {}, onFriendNameUpdated: (String) -> Unit = {}, onSelectRelationship: (Relationship) -> Unit = {}, onClickCustomRelationshipAdd: () -> Unit = {}, onCustomRelationshipUpdated: (String) -> Unit = {}, onCustomRelationshipCleared: () -> Unit = {}, + onCustomRelationshipClosed: () -> Unit = {}, + onCustomRelationshipInnerButtonClicked: () -> Unit = {}, onClickDateText: () -> Unit = {}, onHasVisitedUpdated: (Boolean) -> Unit = {}, onGiftUpdated: (String) -> Unit = {}, @@ -156,7 +194,7 @@ fun SentEnvelopeEditScreen( categoryText = stringResource(R.string.sent_envelope_edit_category_event), categoryTextAlign = Alignment.Top, ) { - uiState.categoryConfig.forEach { category -> + uiState.categoryConfig.dropLast(1).forEach { category -> SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, @@ -165,17 +203,23 @@ fun SentEnvelopeEditScreen( onClick = { onSelectCategory(category) }, ) } - AddConditionButton( - onClick = onClickCustomCategoryAdd, - ) if (uiState.showCustomCategory) { SusuTextFieldWrapContentButton( - style = SmallTextFieldButtonStyle.height32, + focusRequester = categoryFocusRequester, color = TextFieldButtonColor.Orange, + style = SmallTextFieldButtonStyle.height32, text = uiState.customCategory ?: "", + isFocused = uiState.categoryId == uiState.categoryConfig.last().id, + isSaved = uiState.customCategorySaved, onTextChange = onCustomCategoryUpdated, onClickClearIcon = onCustomCategoryCleared, - onClickCloseIcon = { /* 무슨 동작을 해야하지?? */ }, + onClickCloseIcon = onCustomCategoryClosed, + onClickFilledButton = onCustomCategoryInnerButtonClicked, + onClickButton = { onSelectCategory(uiState.categoryConfig.last()) }, + ) + } else { + AddConditionButton( + onClick = onClickCustomCategoryAdd, ) } } @@ -196,7 +240,7 @@ fun SentEnvelopeEditScreen( categoryText = stringResource(R.string.sent_envelope_edit_category_relationship), categoryTextAlign = Alignment.Top, ) { - uiState.relationshipConfig.forEach { relationship -> + uiState.relationshipConfig.dropLast(1).forEach { relationship -> SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, @@ -205,17 +249,23 @@ fun SentEnvelopeEditScreen( onClick = { onSelectRelationship(relationship) }, ) } - AddConditionButton( - onClick = onClickCustomRelationshipAdd, - ) if (uiState.showCustomRelationship) { SusuTextFieldWrapContentButton( + focusRequester = relationshipFocusRequester, style = SmallTextFieldButtonStyle.height32, color = TextFieldButtonColor.Orange, text = uiState.customRelationship ?: "", + isFocused = uiState.relationshipId == uiState.relationshipConfig.last().id, + isSaved = uiState.customRelationshipSaved, onTextChange = onCustomRelationshipUpdated, onClickClearIcon = onCustomRelationshipCleared, - onClickCloseIcon = { /* 무슨 동작을 해야하지?? */ }, + onClickCloseIcon = onCustomRelationshipClosed, + onClickFilledButton = onCustomRelationshipInnerButtonClicked, + onClickButton = { onSelectRelationship(uiState.relationshipConfig.last()) }, + ) + } else { + AddConditionButton( + onClick = onClickCustomRelationshipAdd, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index eba0d666..7f329e1b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -63,10 +63,10 @@ class SentEnvelopeEditViewModel @Inject constructor( fun getEnvelopConfig() { viewModelScope.launch { getRelationShipConfigListUseCase().onSuccess { - intent { copy(relationshipConfig = it.dropLast(1)) } + intent { copy(relationshipConfig = it) } } getCategoryConfigUseCase().onSuccess { - intent { copy(categoryConfig = it.dropLast(1)) } + intent { copy(categoryConfig = it) } } } } @@ -124,7 +124,20 @@ class SentEnvelopeEditViewModel @Inject constructor( } fun showCustomCategoryInput() { - intent { copy(showCustomCategory = true) } + intent { + copy( + showCustomCategory = true, + categoryId = categoryConfig.last().id, + customCategorySaved = false, + ) + } + postSideEffect(SentEnvelopeEditSideEffect.FocusCustomCategory) + } + + fun toggleCustomCategoryInputSaved() = intent { + copy( + customCategorySaved = !customCategorySaved, + ) } fun hideCustomCategoryInput() { @@ -132,7 +145,20 @@ class SentEnvelopeEditViewModel @Inject constructor( } fun showCustomRelationshipInput() { - intent { copy(showCustomRelationship = true) } + intent { + copy( + showCustomRelationship = true, + relationshipId = relationshipConfig.last().id, + customRelationshipSaved = false, + ) + } + postSideEffect(SentEnvelopeEditSideEffect.FocusCustomRelationship) + } + + fun toggleCustomRelationshipInputSaved() = intent { + copy( + customRelationshipSaved = !customRelationshipSaved, + ) } fun hideCustomRelationshipInput() { From 50f8c169a1df36b084b60c793eb65871179269e2 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 14:41:59 +0900 Subject: [PATCH 33/95] =?UTF-8?q?feat:=20=EB=B4=89=ED=88=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80/=ED=8E=B8=EC=A7=91=20=EC=8B=9C=20=EA=B8=B0=EB=B3=B8?= =?UTF-8?q?=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=82=BD=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/EnvelopesRepository.kt | 2 +- .../envelope/CreateReceivedEnvelopeUseCase.kt | 4 +++ .../envelope/EditReceivedEnvelopeUseCase.kt | 4 +++ .../susu/feature/navigator/MainNavigator.kt | 12 +++---- .../ReceivedEnvelopeAddViewModel.kt | 13 +++++-- .../ReceivedEnvelopeDetailContract.kt | 3 +- .../ReceivedEnvelopeDetailScreen.kt | 5 +-- .../ReceivedEnvelopeDetailViewModel.kt | 7 ++-- .../ReceivedEnvelopeEditViewModel.kt | 9 +++-- .../ledgerdetail/LedgerDetailContract.kt | 4 +-- .../ledgerdetail/LedgerDetailScreen.kt | 8 ++--- .../ledgerdetail/LedgerDetailViewModel.kt | 4 +-- .../received/navigation/ReceivedNavigation.kt | 36 ++++++++----------- 13 files changed, 65 insertions(+), 46 deletions(-) diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index 274b9149..3993c587 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -60,7 +60,7 @@ interface EnvelopesRepository { memo: String? = null, hasVisited: Boolean? = null, handedOverAt: LocalDateTime, - categoryId: Long? = null, + categoryId: Long, customCategory: String? = null, ): Envelope } diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/CreateReceivedEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/CreateReceivedEnvelopeUseCase.kt index 4a8a3e8f..07b1ceb0 100644 --- a/domain/src/main/java/com/susu/domain/usecase/envelope/CreateReceivedEnvelopeUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/CreateReceivedEnvelopeUseCase.kt @@ -22,6 +22,8 @@ class CreateReceivedEnvelopeUseCase @Inject constructor( envelopesRepository.createEnvelope( type = "RECEIVED", friendId = friendId, + categoryId = categoryId, + customCategory = customCategory, ledgerId = ledgerId, amount = amount, gift = gift, @@ -35,6 +37,8 @@ class CreateReceivedEnvelopeUseCase @Inject constructor( data class Param( val friendId: Long? = null, val friendName: String? = null, + val categoryId: Long, + val customCategory: String?, val phoneNumber: String? = null, val relationshipId: Long? = null, val customRelation: String? = null, diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt index ff6a743d..1de8ac32 100644 --- a/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/EditReceivedEnvelopeUseCase.kt @@ -28,6 +28,8 @@ class EditReceivedEnvelopeUseCase @Inject constructor( amount = amount, gift = gift, memo = memo, + categoryId = categoryId, + customCategory = customCategory, hasVisited = hasVisited, handedOverAt = handedOverAt, ) @@ -38,6 +40,8 @@ class EditReceivedEnvelopeUseCase @Inject constructor( val envelopeId: Long, val friendId: Long, val friendName: String, + val categoryId: Long, + val customCategory: String?, val phoneNumber: String? = null, val relationshipId: Long, val customRelation: String? = null, diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt index 39db2eef..5edba195 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt @@ -163,20 +163,20 @@ internal class MainNavigator( navController.navigateMyPageSocial() } - fun navigateReceivedEnvelopeAdd(categoryName: String, ledgerId: Long) { - navController.navigateReceivedEnvelopeAdd(categoryName, ledgerId) + fun navigateReceivedEnvelopeAdd(ledger: Ledger) { + navController.navigateReceivedEnvelopeAdd(ledger) } fun navigateMyPagePrivacyPolicy() { navController.navigateMyPagePrivacyPolicy() } - fun navigateReceivedEnvelopeDetail(envelope: Envelope, ledgerId: Long) { - navController.navigateReceivedEnvelopeDetail(envelope, ledgerId) + fun navigateReceivedEnvelopeDetail(envelope: Envelope, ledger: Ledger) { + navController.navigateReceivedEnvelopeDetail(envelope, ledger) } - fun navigateReceivedEnvelopeEdit(envelope: Envelope, ledgerId: Long) { - navController.navigateReceivedEnvelopeEdit(envelope, ledgerId) + fun navigateReceivedEnvelopeEdit(envelope: Envelope, ledger: Ledger) { + navController.navigateReceivedEnvelopeEdit(envelope, ledger) } fun navigateVoteAdd() { diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeadd/ReceivedEnvelopeAddViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeadd/ReceivedEnvelopeAddViewModel.kt index 9b602db7..ceb9284c 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeadd/ReceivedEnvelopeAddViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeadd/ReceivedEnvelopeAddViewModel.kt @@ -2,9 +2,11 @@ package com.susu.feature.received.envelopeadd import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import com.susu.core.model.Ledger import com.susu.core.model.Relationship import com.susu.core.model.exception.AlreadyRegisteredFriendPhoneNumberException import com.susu.core.ui.base.BaseViewModel +import com.susu.core.ui.extension.decodeFromUri import com.susu.core.ui.extension.encodeToUri import com.susu.domain.usecase.envelope.CreateReceivedEnvelopeUseCase import com.susu.feature.received.navigation.ReceivedRoute @@ -22,8 +24,11 @@ class ReceivedEnvelopeAddViewModel @Inject constructor( ) : BaseViewModel( ReceivedEnvelopeAddState(), ) { - val categoryName = savedStateHandle.get(ReceivedRoute.CATEGORY_ARGUMENT_NAME)!! - private val ledgerId = savedStateHandle.get(ReceivedRoute.LEDGER_ID_ARGUMENT_NAME)!! + private val ledger = run { + Json.decodeFromUri(savedStateHandle.get(ReceivedRoute.LEDGER_ARGUMENT_NAME)!!) + } + val categoryName = ledger.category.customCategory ?: ledger.category.name + private val ledgerId = ledger.id private var money: Long = 0 private var name: String = "" @@ -44,10 +49,12 @@ class ReceivedEnvelopeAddViewModel @Inject constructor( param = CreateReceivedEnvelopeUseCase.Param( friendId = friendId, friendName = name, + categoryId = ledger.category.id.toLong(), + customCategory = ledger.category.customCategory, phoneNumber = phoneNumber, relationshipId = relationShip?.id, customRelation = relationShip?.customRelation, - ledgerId = ledgerId.toLong(), + ledgerId = ledgerId, amount = money, gift = present, memo = memo, diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt index bda1f1cf..90c96f4c 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailContract.kt @@ -1,6 +1,7 @@ package com.susu.feature.received.envelopedetail import com.susu.core.model.Envelope +import com.susu.core.model.Ledger import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState @@ -9,7 +10,7 @@ data class ReceivedEnvelopeDetailState( ) : UiState sealed interface ReceivedEnvelopeDetailSideEffect : SideEffect { - data class NavigateReceivedEnvelopeEdit(val envelope: Envelope, val ledgerId: Long) : ReceivedEnvelopeDetailSideEffect + data class NavigateReceivedEnvelopeEdit(val envelope: Envelope, val ledger: Ledger) : ReceivedEnvelopeDetailSideEffect data class PopBackStackWithReceivedEnvelope(val envelope: String) : ReceivedEnvelopeDetailSideEffect data class PopBackStackWithDeleteReceivedEnvelopeId(val envelopeId: Long) : ReceivedEnvelopeDetailSideEffect data class ShowDeleteDialog(val onConfirmRequest: () -> Unit) : ReceivedEnvelopeDetailSideEffect diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt index f1515785..aacc5e5b 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailScreen.kt @@ -26,6 +26,7 @@ import com.susu.core.designsystem.component.appbar.icon.EditText import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.Envelope +import com.susu.core.model.Ledger import com.susu.core.ui.DialogToken import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle @@ -40,7 +41,7 @@ fun ReceivedEnvelopeDetailRoute( viewModel: ReceivedEnvelopeDetailViewModel = hiltViewModel(), popBackStackWithDeleteReceivedEnvelopeId: (Long) -> Unit, popBackStackWithReceivedEnvelope: (String) -> Unit, - navigateReceivedEnvelopeEdit: (Envelope, Long) -> Unit, + navigateReceivedEnvelopeEdit: (Envelope, Ledger) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, onShowDialog: (DialogToken) -> Unit, @@ -50,7 +51,7 @@ fun ReceivedEnvelopeDetailRoute( viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { is ReceivedEnvelopeDetailSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) - is ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit -> navigateReceivedEnvelopeEdit(sideEffect.envelope, sideEffect.ledgerId) + is ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit -> navigateReceivedEnvelopeEdit(sideEffect.envelope, sideEffect.ledger) is ReceivedEnvelopeDetailSideEffect.PopBackStackWithDeleteReceivedEnvelopeId -> popBackStackWithDeleteReceivedEnvelopeId( sideEffect.envelopeId, ) diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt index 3c100068..3de95b86 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopedetail/ReceivedEnvelopeDetailViewModel.kt @@ -3,6 +3,7 @@ package com.susu.feature.received.envelopedetail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.model.Envelope +import com.susu.core.model.Ledger import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri import com.susu.core.ui.extension.encodeToUri @@ -23,7 +24,9 @@ class ReceivedEnvelopeDetailViewModel @Inject constructor( ReceivedEnvelopeDetailState(), ) { private val argument = savedStateHandle.get(ReceivedRoute.ENVELOPE_ARGUMENT_NAME)!! - private val ledgerId = savedStateHandle.get(ReceivedRoute.LEDGER_ID_ARGUMENT_NAME)!!.toLong() + private val ledger = run { + Json.decodeFromUri(savedStateHandle.get(ReceivedRoute.LEDGER_ARGUMENT_NAME)!!) + } private var envelope = Envelope() @@ -40,7 +43,7 @@ class ReceivedEnvelopeDetailViewModel @Inject constructor( } } - fun navigateEnvelopeEdit() = postSideEffect(ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit(envelope, ledgerId)) + fun navigateEnvelopeEdit() = postSideEffect(ReceivedEnvelopeDetailSideEffect.NavigateReceivedEnvelopeEdit(envelope, ledger)) fun popBackStackWithEnvelope() = postSideEffect(ReceivedEnvelopeDetailSideEffect.PopBackStackWithReceivedEnvelope(Json.encodeToUri(envelope))) fun showDeleteDialog() = postSideEffect( ReceivedEnvelopeDetailSideEffect.ShowDeleteDialog( diff --git a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt index d49350ce..68198454 100644 --- a/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/envelopeedit/ReceivedEnvelopeEditViewModel.kt @@ -3,6 +3,7 @@ package com.susu.feature.received.envelopeedit import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.model.Envelope +import com.susu.core.model.Ledger import com.susu.core.model.Relationship import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri @@ -26,7 +27,9 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( ReceivedEnvelopeEditState(), ) { private val argument = savedStateHandle.get(ReceivedRoute.ENVELOPE_ARGUMENT_NAME)!! - private val ledgerId = savedStateHandle.get(ReceivedRoute.LEDGER_ID_ARGUMENT_NAME)!!.toLong() + private val ledger = run { + Json.decodeFromUri(savedStateHandle.get(ReceivedRoute.LEDGER_ARGUMENT_NAME)!!) + } private var isFirstVisited: Boolean = true @@ -62,10 +65,12 @@ class ReceivedEnvelopeEditViewModel @Inject constructor( envelopeId = envelope.id, friendId = envelope.friend.id, friendName = envelope.friend.name, + categoryId = ledger.category.id.toLong(), + customCategory = ledger.category.customCategory, phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, relationshipId = envelope.relationship.id, customRelation = envelope.relationship.customRelation, - ledgerId = ledgerId, + ledgerId = ledger.id, amount = envelope.amount, gift = envelope.gift, memo = envelope.memo, diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailContract.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailContract.kt index 3ddaae39..d750f26c 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailContract.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailContract.kt @@ -19,8 +19,8 @@ data class LedgerDetailState( ) : UiState sealed interface LedgerDetailSideEffect : SideEffect { - data class NavigateEnvelopeAdd(val categoryName: String, val ledgerId: Long) : LedgerDetailSideEffect - data class NavigateEnvelopeDetail(val envelope: Envelope, val ledgerId: Long) : LedgerDetailSideEffect + data class NavigateEnvelopeAdd(val ledger: Ledger) : LedgerDetailSideEffect + data class NavigateEnvelopeDetail(val envelope: Envelope, val ledger: Ledger) : LedgerDetailSideEffect data class NavigateLedgerEdit(val ledger: Ledger) : LedgerDetailSideEffect data class PopBackStackWithLedger(val ledger: String) : LedgerDetailSideEffect data class PopBackStackWithDeleteLedgerId(val ledgerId: Long) : LedgerDetailSideEffect diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt index 2da3f53c..7b512d6e 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailScreen.kt @@ -58,8 +58,8 @@ fun LedgerDetailRoute( envelope: String?, toDeleteEnvelopeId: Long?, navigateLedgerEdit: (Ledger) -> Unit, - navigateEnvelopAdd: (String, Long) -> Unit, - navigateEnvelopeDetail: (Envelope, Long) -> Unit, + navigateEnvelopAdd: (Ledger) -> Unit, + navigateEnvelopeDetail: (Envelope, Ledger) -> Unit, popBackStackWithLedger: (String) -> Unit, popBackStackWithDeleteLedgerId: (Long) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, @@ -96,8 +96,8 @@ fun LedgerDetailRoute( is LedgerDetailSideEffect.PopBackStackWithDeleteLedgerId -> popBackStackWithDeleteLedgerId(sideEffect.ledgerId) is LedgerDetailSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) is LedgerDetailSideEffect.ShowSnackbar -> onShowSnackbar(SnackbarToken(message = sideEffect.msg)) - is LedgerDetailSideEffect.NavigateEnvelopeAdd -> navigateEnvelopAdd(sideEffect.categoryName, sideEffect.ledgerId) - is LedgerDetailSideEffect.NavigateEnvelopeDetail -> navigateEnvelopeDetail(sideEffect.envelope, sideEffect.ledgerId) + is LedgerDetailSideEffect.NavigateEnvelopeAdd -> navigateEnvelopAdd(sideEffect.ledger) + is LedgerDetailSideEffect.NavigateEnvelopeDetail -> navigateEnvelopeDetail(sideEffect.envelope, sideEffect.ledger) } } diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt index 952d4b90..9748126a 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt @@ -183,8 +183,8 @@ class LedgerDetailViewModel @Inject constructor( } fun navigateEnvelopeAdd() = postSideEffect( - LedgerDetailSideEffect.NavigateEnvelopeAdd(ledger.category.customCategory ?: ledger.category.name, ledger.id), + LedgerDetailSideEffect.NavigateEnvelopeAdd(ledger), ) - fun navigateEnvelopeDetail(envelope: Envelope) = postSideEffect(LedgerDetailSideEffect.NavigateEnvelopeDetail(envelope, ledger.id)) + fun navigateEnvelopeDetail(envelope: Envelope) = postSideEffect(LedgerDetailSideEffect.NavigateEnvelopeDetail(envelope, ledger)) } diff --git a/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt b/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt index a464b0d9..ed209b3a 100644 --- a/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt +++ b/feature/received/src/main/java/com/susu/feature/received/navigation/ReceivedNavigation.kt @@ -48,16 +48,16 @@ fun NavController.navigateLedgerAdd() { navigate(ReceivedRoute.ledgerAddRoute) } -fun NavController.navigateReceivedEnvelopeAdd(categoryName: String, ledgerId: Long) { - navigate(ReceivedRoute.envelopeAddRoute(categoryName, ledgerId.toString())) +fun NavController.navigateReceivedEnvelopeAdd(ledger: Ledger) { + navigate(ReceivedRoute.envelopeAddRoute(Json.encodeToUri(ledger))) } -fun NavController.navigateReceivedEnvelopeDetail(envelope: Envelope, ledgerId: Long) { - navigate(ReceivedRoute.envelopeDetailRoute(Json.encodeToUri(envelope), ledgerId.toString())) +fun NavController.navigateReceivedEnvelopeDetail(envelope: Envelope, ledger: Ledger) { + navigate(ReceivedRoute.envelopeDetailRoute(Json.encodeToUri(envelope), Json.encodeToUri(ledger))) } -fun NavController.navigateReceivedEnvelopeEdit(envelope: Envelope, ledgerId: Long) { - navigate(ReceivedRoute.envelopeEditRoute(Json.encodeToUri(envelope), ledgerId.toString())) +fun NavController.navigateReceivedEnvelopeEdit(envelope: Envelope, ledger: Ledger) { + navigate(ReceivedRoute.envelopeEditRoute(Json.encodeToUri(envelope), Json.encodeToUri(ledger))) } @Suppress("detekt:LongMethod") @@ -72,9 +72,9 @@ fun NavGraphBuilder.receivedNavGraph( navigateLedgerEdit: (Ledger) -> Unit, navigateLedgerFilter: (FilterArgument) -> Unit, navigateLedgerAdd: () -> Unit, - navigateEnvelopAdd: (String, Long) -> Unit, - navigateEnvelopeDetail: (Envelope, Long) -> Unit, - navigateEnvelopeEdit: (Envelope, Long) -> Unit, + navigateEnvelopAdd: (Ledger) -> Unit, + navigateEnvelopeDetail: (Envelope, Ledger) -> Unit, + navigateEnvelopeEdit: (Envelope, Ledger) -> Unit, popBackStackWithEnvelope: (String) -> Unit, popBackStackWithDeleteReceivedEnvelopeId: (Long) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, @@ -158,13 +158,7 @@ fun NavGraphBuilder.receivedNavGraph( composable( route = ReceivedRoute.envelopeAddRoute( - categoryName = "{${ReceivedRoute.CATEGORY_ARGUMENT_NAME}}", - ledgerId = "{${ReceivedRoute.LEDGER_ID_ARGUMENT_NAME}}", - ), - arguments = listOf( - navArgument(ReceivedRoute.CATEGORY_ARGUMENT_NAME) { - type = NavType.StringType - }, + ledger = "{${ReceivedRoute.LEDGER_ARGUMENT_NAME}}", ), ) { ReceivedEnvelopeAddRoute( @@ -178,7 +172,7 @@ fun NavGraphBuilder.receivedNavGraph( composable( route = ReceivedRoute.envelopeDetailRoute( envelope = "{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}", - ledgerId = "{${ReceivedRoute.LEDGER_ID_ARGUMENT_NAME}}", + ledger = "{${ReceivedRoute.LEDGER_ARGUMENT_NAME}}", ), ) { ReceivedEnvelopeDetailRoute( @@ -194,7 +188,7 @@ fun NavGraphBuilder.receivedNavGraph( composable( route = ReceivedRoute.envelopeEditRoute( envelope = "{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}", - ledgerId = "{${ReceivedRoute.LEDGER_ID_ARGUMENT_NAME}}", + ledger = "{${ReceivedRoute.LEDGER_ARGUMENT_NAME}}", ), ) { ReceivedEnvelopeEditRoute( @@ -220,7 +214,7 @@ object ReceivedRoute { const val ledgerAddRoute = "ledger-add" - fun envelopeAddRoute(categoryName: String, ledgerId: String) = "envelope-add/$categoryName/$ledgerId" - fun envelopeDetailRoute(envelope: String, ledgerId: String) = "envelope-detail/$envelope/$ledgerId" - fun envelopeEditRoute(envelope: String, ledgerId: String) = "envelope-edit/$envelope/$ledgerId" + fun envelopeAddRoute(ledger: String) = "envelope-add/$ledger" + fun envelopeDetailRoute(envelope: String, ledger: String) = "envelope-detail/$envelope/$ledger" + fun envelopeEditRoute(envelope: String, ledger: String) = "envelope-edit/$envelope/$ledger" } From dd636d0575acfce909bd0bdfe1f8c19fafb44025 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 14:51:35 +0900 Subject: [PATCH 34/95] =?UTF-8?q?fix:=20=EC=B4=88=EA=B8=B0=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EA=B0=80=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC/?= =?UTF-8?q?=EA=B4=80=EA=B3=84=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopeedit/SentEnvelopeEditContract.kt | 6 +- .../envelopeedit/SentEnvelopeEditScreen.kt | 1 - .../envelopeedit/SentEnvelopeEditViewModel.kt | 61 +++++++++++-------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt index 70ece152..4f2b69b0 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt @@ -5,12 +5,14 @@ import com.susu.core.model.Relationship import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState import com.susu.core.ui.util.currentDate +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf import java.time.LocalDateTime data class SentEnvelopeEditState( val isLoading: Boolean = false, - val categoryConfig: List = emptyList(), - val relationshipConfig: List = emptyList(), + val categoryConfig: PersistentList = persistentListOf(), + val relationshipConfig: PersistentList = persistentListOf(), val amount: Long = 0L, val gift: String? = null, val memo: String? = null, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index cef9ae82..1330fd7d 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -91,7 +91,6 @@ fun SentEnvelopeEditRoute( LaunchedEffect(key1 = Unit) { viewModel.run { initData() - getEnvelopConfig() } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index 7f329e1b..9587d95b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -11,6 +11,7 @@ import com.susu.core.ui.util.getSafeLocalDateTime import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import kotlinx.datetime.toJavaLocalDateTime import kotlinx.datetime.toKotlinLocalDateTime @@ -27,8 +28,9 @@ class SentEnvelopeEditViewModel @Inject constructor( fun initData() { // TODO: Argument로 받은 데이터로 설정. UI 개발을 위해 임의의 데이터 사용. val category = Category( - id = 1, - name = "결혼식", + id = 5, + name = "커스텀", + customCategory = "커스텀", ) val envelope = Envelope( amount = 150000, @@ -38,35 +40,40 @@ class SentEnvelopeEditViewModel @Inject constructor( name = "김철수", ), relationship = Relationship( - id = 1, - relation = "친구", + id = 5, + relation = "나", + customRelation = "나", ), ) - intent { - copy( - amount = envelope.amount, - gift = envelope.gift, - memo = envelope.memo, - hasVisited = envelope.hasVisited, - handedOverAt = envelope.handedOverAt.toJavaLocalDateTime(), - friendName = envelope.friend.name, - relationshipId = envelope.relationship.id, - customRelationship = envelope.relationship.customRelation, - phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, - categoryId = category.id, - customCategory = category.customCategory, - ) - } - } - - fun getEnvelopConfig() { viewModelScope.launch { - getRelationShipConfigListUseCase().onSuccess { - intent { copy(relationshipConfig = it) } - } - getCategoryConfigUseCase().onSuccess { - intent { copy(categoryConfig = it) } + val relationshipConfigResult = getRelationShipConfigListUseCase() + val categoryConfigResult = getCategoryConfigUseCase() + + if (relationshipConfigResult.isSuccess && categoryConfigResult.isSuccess) { + val relationshipConfig = relationshipConfigResult.getOrDefault(emptyList()).toPersistentList() + val categoryConfig = categoryConfigResult.getOrDefault(emptyList()).toPersistentList() + intent { + copy( + categoryConfig = categoryConfig, + relationshipConfig = relationshipConfig, + amount = envelope.amount, + gift = envelope.gift, + memo = envelope.memo, + hasVisited = envelope.hasVisited, + handedOverAt = envelope.handedOverAt.toJavaLocalDateTime(), + friendName = envelope.friend.name, + relationshipId = envelope.relationship.id, + customRelationship = envelope.relationship.customRelation, + phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, + categoryId = category.id, + customCategory = category.customCategory, + showCustomCategory = category.id == categoryConfig.last().id, + customCategorySaved = category.id == categoryConfig.last().id, + showCustomRelationship = envelope.relationship.id == relationshipConfig.last().id, + customRelationshipSaved = envelope.relationship.id == relationshipConfig.last().id, + ) + } } } } From 5c5f6fa470599e6a85f3146b3c4df39079194f1a Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 15:06:00 +0900 Subject: [PATCH 35/95] =?UTF-8?q?feat:=20=EB=B0=9B=EC=9D=80=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=88=98=EC=A0=95=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelope/EditSentEnvelopeUseCase.kt | 8 ++--- .../envelopeedit/SentEnvelopeEditScreen.kt | 7 +++- .../envelopeedit/SentEnvelopeEditViewModel.kt | 34 +++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt index 42ad74e0..fc684b8c 100644 --- a/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt @@ -1,7 +1,6 @@ package com.susu.domain.usecase.envelope import com.susu.core.common.runCatchingIgnoreCancelled -import com.susu.core.model.Category import com.susu.domain.repository.EnvelopesRepository import com.susu.domain.repository.FriendRepository import kotlinx.datetime.LocalDateTime @@ -28,8 +27,8 @@ class EditSentEnvelopeUseCase @Inject constructor( amount = amount, gift = gift, memo = memo, - categoryId = category.id.toLong(), - customCategory = category.customCategory, + categoryId = categoryId.toLong(), + customCategory = customCategory, hasVisited = hasVisited, handedOverAt = handedOverAt, ) @@ -43,7 +42,8 @@ class EditSentEnvelopeUseCase @Inject constructor( val phoneNumber: String? = null, val relationshipId: Long, val customRelation: String? = null, - val category: Category, + val categoryId: Int, + val customCategory: String? = null, val amount: Long, val gift: String? = null, val memo: String? = null, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 1330fd7d..e8547ccc 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -37,6 +37,7 @@ import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.MediumButtonStyle import com.susu.core.designsystem.component.button.SmallButtonStyle import com.susu.core.designsystem.component.button.SusuFilledButton +import com.susu.core.designsystem.component.screen.LoadingScreen import com.susu.core.designsystem.component.textfield.SusuBasicTextField import com.susu.core.designsystem.component.textfield.SusuPriceTextField import com.susu.core.designsystem.component.textfieldbutton.SusuTextFieldWrapContentButton @@ -101,7 +102,7 @@ fun SentEnvelopeEditRoute( SentEnvelopeEditScreen( uiState = uiState, onClickBackIcon = viewModel::popBackStack, - onClickSave = navigateSentEnvelopeDetail, + onClickSave = viewModel::editEnvelope, onMoneyUpdated = viewModel::updateAmount, onSelectCategory = { viewModel.updateCategoryId(it.id) }, onClickCustomCategoryAdd = viewModel::showCustomCategoryInput, @@ -377,6 +378,10 @@ fun SentEnvelopeEditScreen( onItemSelected = { year, month, day -> onDateUpdated(year, month, day) }, ) } + + if (uiState.isLoading) { + LoadingScreen() + } } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index 9587d95b..f88433d4 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -9,6 +9,7 @@ import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.util.currentDate import com.susu.core.ui.util.getSafeLocalDateTime import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase +import com.susu.domain.usecase.envelope.EditSentEnvelopeUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList @@ -21,9 +22,11 @@ import javax.inject.Inject class SentEnvelopeEditViewModel @Inject constructor( private val getCategoryConfigUseCase: GetCategoryConfigUseCase, private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, + private val editSentEnvelopeUseCase: EditSentEnvelopeUseCase, ) : BaseViewModel( SentEnvelopeEditState(), ) { + private var originalEnvelope: Envelope = Envelope() fun initData() { // TODO: Argument로 받은 데이터로 설정. UI 개발을 위해 임의의 데이터 사용. @@ -45,6 +48,7 @@ class SentEnvelopeEditViewModel @Inject constructor( customRelation = "나", ), ) + originalEnvelope = envelope viewModelScope.launch { val relationshipConfigResult = getRelationShipConfigListUseCase() @@ -78,6 +82,36 @@ class SentEnvelopeEditViewModel @Inject constructor( } } + fun editEnvelope() { + viewModelScope.launch { + intent { copy(isLoading = true) } + editSentEnvelopeUseCase( + param = with(currentState) { + EditSentEnvelopeUseCase.Param( + envelopeId = originalEnvelope.id, + friendId = originalEnvelope.friend.id, + friendName = friendName, + phoneNumber = phoneNumber, + relationshipId = relationshipId, + customRelation = customRelationship, + categoryId = categoryId, + customCategory = customCategory, + amount = amount, + gift = gift, + memo = memo, + handedOverAt = handedOverAt.toKotlinLocalDateTime(), + hasVisited = hasVisited, + ) + }, + ).onSuccess { + popBackStack() + }.onFailure { + postSideEffect(SentEnvelopeEditSideEffect.HandleException(it, ::editEnvelope)) + } + intent { copy(isLoading = false) } + } + } + fun popBackStack() = postSideEffect(SentEnvelopeEditSideEffect.PopBackStack) fun updateAmount(amount: Long) { From d3025dc7ae3faa2de618f0312917954f6e85d5c0 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 15:09:55 +0900 Subject: [PATCH 36/95] =?UTF-8?q?chore:=20SentEnvelopeEditScreen=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopeedit/SentEnvelopeEditScreen.kt | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index e8547ccc..15b4fdb4 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -101,31 +101,31 @@ fun SentEnvelopeEditRoute( SentEnvelopeEditScreen( uiState = uiState, + categoryFocusRequester = categoryFocusRequester, + relationshipFocusRequester = relationshipFocusRequester, onClickBackIcon = viewModel::popBackStack, onClickSave = viewModel::editEnvelope, - onMoneyUpdated = viewModel::updateAmount, - onSelectCategory = { viewModel.updateCategoryId(it.id) }, onClickCustomCategoryAdd = viewModel::showCustomCategoryInput, + onClickCustomRelationshipAdd = viewModel::showCustomRelationshipInput, + onClickDateText = viewModel::showDatePickerSheet, + onClickCustomCategoryInnerButton = viewModel::toggleCustomCategoryInputSaved, + onClickCustomRelationshipInnerButton = viewModel::toggleCustomRelationshipInputSaved, + onCloseCustomCategory = viewModel::hideCustomCategoryInput, + onCloseCustomRelationship = viewModel::hideCustomRelationshipInput, + onSelectCategory = { viewModel.updateCategoryId(it.id) }, + onSelectRelationship = { viewModel.updateRelationshipId(it.id) }, + onMoneyUpdated = viewModel::updateAmount, + onFriendNameUpdated = viewModel::updateFriendName, onCustomCategoryUpdated = viewModel::updateCustomCategory, onCustomCategoryCleared = { viewModel.updateCustomCategory("") }, - onFriendNameUpdated = viewModel::updateFriendName, - onSelectRelationship = { viewModel.updateRelationshipId(it.id) }, - onClickCustomRelationshipAdd = viewModel::showCustomRelationshipInput, onCustomRelationshipUpdated = viewModel::updateCustomRelationship, onCustomRelationshipCleared = { viewModel.updateCustomRelationship("") }, - onClickDateText = viewModel::showDatePickerSheet, + onDatePickerSheetDismissed = viewModel::hideDatePickerSheet, + onDateUpdated = viewModel::updateHandedOverAt, onHasVisitedUpdated = viewModel::updateHasVisited, + onPhoneNumberUpdated = viewModel::updatePhoneNumber, onGiftUpdated = viewModel::updateGift, onMemoUpdated = viewModel::updateMemo, - onPhoneNumberUpdated = viewModel::updatePhoneNumber, - onDateUpdated = viewModel::updateHandedOverAt, - onDatePickerSheetDismissed = viewModel::hideDatePickerSheet, - categoryFocusRequester = categoryFocusRequester, - relationshipFocusRequester = relationshipFocusRequester, - onCustomCategoryClosed = viewModel::hideCustomCategoryInput, - onCustomCategoryInnerButtonClicked = viewModel::toggleCustomCategoryInputSaved, - onCustomRelationshipClosed = viewModel::hideCustomRelationshipInput, - onCustomRelationshipInnerButtonClicked = viewModel::toggleCustomRelationshipInputSaved, ) } @@ -143,15 +143,15 @@ fun SentEnvelopeEditScreen( onClickCustomCategoryAdd: () -> Unit = {}, onCustomCategoryUpdated: (String) -> Unit = {}, onCustomCategoryCleared: () -> Unit = {}, - onCustomCategoryClosed: () -> Unit = {}, - onCustomCategoryInnerButtonClicked: () -> Unit = {}, + onCloseCustomCategory: () -> Unit = {}, + onClickCustomCategoryInnerButton: () -> Unit = {}, onFriendNameUpdated: (String) -> Unit = {}, onSelectRelationship: (Relationship) -> Unit = {}, onClickCustomRelationshipAdd: () -> Unit = {}, onCustomRelationshipUpdated: (String) -> Unit = {}, onCustomRelationshipCleared: () -> Unit = {}, - onCustomRelationshipClosed: () -> Unit = {}, - onCustomRelationshipInnerButtonClicked: () -> Unit = {}, + onCloseCustomRelationship: () -> Unit = {}, + onClickCustomRelationshipInnerButton: () -> Unit = {}, onClickDateText: () -> Unit = {}, onHasVisitedUpdated: (Boolean) -> Unit = {}, onGiftUpdated: (String) -> Unit = {}, @@ -213,8 +213,8 @@ fun SentEnvelopeEditScreen( isSaved = uiState.customCategorySaved, onTextChange = onCustomCategoryUpdated, onClickClearIcon = onCustomCategoryCleared, - onClickCloseIcon = onCustomCategoryClosed, - onClickFilledButton = onCustomCategoryInnerButtonClicked, + onClickCloseIcon = onCloseCustomCategory, + onClickFilledButton = onClickCustomCategoryInnerButton, onClickButton = { onSelectCategory(uiState.categoryConfig.last()) }, ) } else { @@ -259,8 +259,8 @@ fun SentEnvelopeEditScreen( isSaved = uiState.customRelationshipSaved, onTextChange = onCustomRelationshipUpdated, onClickClearIcon = onCustomRelationshipCleared, - onClickCloseIcon = onCustomRelationshipClosed, - onClickFilledButton = onCustomRelationshipInnerButtonClicked, + onClickCloseIcon = onCloseCustomRelationship, + onClickFilledButton = onClickCustomRelationshipInnerButton, onClickButton = { onSelectRelationship(uiState.relationshipConfig.last()) }, ) } else { From d0d17f011ee15b793185b0df04f1274a91c2e756 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 16:44:49 +0900 Subject: [PATCH 37/95] =?UTF-8?q?feat:=20BlockUserUseCase,=20ReportVoteUse?= =?UTF-8?q?Case=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/data/data/di/RepositoryModule.kt | 14 +++++++++ .../data/repository/BlockRepositoryImpl.kt | 26 +++++++++++++++++ .../data/repository/ReportRepositoryImpl.kt | 29 +++++++++++++++++++ .../com/susu/data/remote/api/BlockService.kt | 28 ++++++++++++++++++ .../com/susu/data/remote/api/ReportService.kt | 27 +++++++++++++++++ .../susu/data/remote/di/ApiServiceModule.kt | 14 +++++++++ .../remote/model/request/BlockUserRequest.kt | 9 ++++++ .../remote/model/request/ReportVoteRequest.kt | 10 +++++++ .../susu/domain/repository/BlockRepository.kt | 8 +++++ .../domain/repository/ReportRepository.kt | 8 +++++ .../domain/usecase/block/BlockUserUseCase.kt | 14 +++++++++ .../usecase/report/ReportVoteUseCase.kt | 13 +++++++++ 12 files changed, 200 insertions(+) create mode 100644 data/src/main/java/com/susu/data/data/repository/BlockRepositoryImpl.kt create mode 100644 data/src/main/java/com/susu/data/data/repository/ReportRepositoryImpl.kt create mode 100644 data/src/main/java/com/susu/data/remote/api/BlockService.kt create mode 100644 data/src/main/java/com/susu/data/remote/api/ReportService.kt create mode 100644 data/src/main/java/com/susu/data/remote/model/request/BlockUserRequest.kt create mode 100644 data/src/main/java/com/susu/data/remote/model/request/ReportVoteRequest.kt create mode 100644 domain/src/main/java/com/susu/domain/repository/BlockRepository.kt create mode 100644 domain/src/main/java/com/susu/domain/repository/ReportRepository.kt create mode 100644 domain/src/main/java/com/susu/domain/usecase/block/BlockUserUseCase.kt create mode 100644 domain/src/main/java/com/susu/domain/usecase/report/ReportVoteUseCase.kt diff --git a/data/src/main/java/com/susu/data/data/di/RepositoryModule.kt b/data/src/main/java/com/susu/data/data/di/RepositoryModule.kt index cf3bf2f4..80c68c38 100644 --- a/data/src/main/java/com/susu/data/data/di/RepositoryModule.kt +++ b/data/src/main/java/com/susu/data/data/di/RepositoryModule.kt @@ -1,5 +1,6 @@ package com.susu.data.data.di +import com.susu.data.data.repository.BlockRepositoryImpl import com.susu.data.data.repository.CategoryConfigRepositoryImpl import com.susu.data.data.repository.EnvelopesRepositoryImpl import com.susu.data.data.repository.ExcelRepositoryImpl @@ -7,6 +8,7 @@ import com.susu.data.data.repository.FriendRepositoryImpl import com.susu.data.data.repository.LedgerRecentSearchRepositoryImpl import com.susu.data.data.repository.LedgerRepositoryImpl import com.susu.data.data.repository.LoginRepositoryImpl +import com.susu.data.data.repository.ReportRepositoryImpl import com.susu.data.data.repository.SignUpRepositoryImpl import com.susu.data.data.repository.StatisticsRepositoryImpl import com.susu.data.data.repository.TermRepositoryImpl @@ -14,6 +16,7 @@ import com.susu.data.data.repository.TokenRepositoryImpl import com.susu.data.data.repository.UserRepositoryImpl import com.susu.data.data.repository.VoteRecentSearchRepositoryImpl import com.susu.data.data.repository.VoteRepositoryImpl +import com.susu.domain.repository.BlockRepository import com.susu.domain.repository.CategoryConfigRepository import com.susu.domain.repository.EnvelopesRepository import com.susu.domain.repository.ExcelRepository @@ -21,6 +24,7 @@ import com.susu.domain.repository.FriendRepository import com.susu.domain.repository.LedgerRecentSearchRepository import com.susu.domain.repository.LedgerRepository import com.susu.domain.repository.LoginRepository +import com.susu.domain.repository.ReportRepository import com.susu.domain.repository.SignUpRepository import com.susu.domain.repository.StatisticsRepository import com.susu.domain.repository.TermRepository @@ -106,4 +110,14 @@ abstract class RepositoryModule { abstract fun bindVoteRecentSearchRepository( voteRecentSearchRepositoryImpl: VoteRecentSearchRepositoryImpl, ): VoteRecentSearchRepository + + @Binds + abstract fun bindBlockRepository( + blockRepositoryImpl: BlockRepositoryImpl, + ): BlockRepository + + @Binds + abstract fun bindReportRepository( + reportRepositoryImpl: ReportRepositoryImpl, + ): ReportRepository } diff --git a/data/src/main/java/com/susu/data/data/repository/BlockRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/BlockRepositoryImpl.kt new file mode 100644 index 00000000..14d728fa --- /dev/null +++ b/data/src/main/java/com/susu/data/data/repository/BlockRepositoryImpl.kt @@ -0,0 +1,26 @@ +package com.susu.data.data.repository + +import com.susu.core.model.Envelope +import com.susu.core.model.EnvelopeStatics +import com.susu.core.model.Relationship +import com.susu.core.model.SearchEnvelope +import com.susu.data.remote.api.BlockService +import com.susu.data.remote.api.EnvelopesService +import com.susu.data.remote.model.request.BlockUserRequest +import com.susu.data.remote.model.request.CategoryRequest +import com.susu.data.remote.model.request.EnvelopeRequest +import com.susu.data.remote.model.response.toModel +import com.susu.domain.repository.BlockRepository +import com.susu.domain.repository.EnvelopesRepository +import kotlinx.datetime.LocalDateTime +import javax.inject.Inject + +class BlockRepositoryImpl @Inject constructor( + private val blockService: BlockService, +) : BlockRepository { + override suspend fun blockUser(targetId: Long) = blockService.blockUser( + BlockUserRequest( + targetId = targetId, + ), + ).getOrThrow() +} diff --git a/data/src/main/java/com/susu/data/data/repository/ReportRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/ReportRepositoryImpl.kt new file mode 100644 index 00000000..34a4299b --- /dev/null +++ b/data/src/main/java/com/susu/data/data/repository/ReportRepositoryImpl.kt @@ -0,0 +1,29 @@ +package com.susu.data.data.repository + +import com.susu.core.model.Envelope +import com.susu.core.model.EnvelopeStatics +import com.susu.core.model.Relationship +import com.susu.core.model.SearchEnvelope +import com.susu.data.remote.api.BlockService +import com.susu.data.remote.api.EnvelopesService +import com.susu.data.remote.api.ReportService +import com.susu.data.remote.model.request.BlockUserRequest +import com.susu.data.remote.model.request.CategoryRequest +import com.susu.data.remote.model.request.EnvelopeRequest +import com.susu.data.remote.model.request.ReportVoteRequest +import com.susu.data.remote.model.response.toModel +import com.susu.domain.repository.BlockRepository +import com.susu.domain.repository.EnvelopesRepository +import com.susu.domain.repository.ReportRepository +import kotlinx.datetime.LocalDateTime +import javax.inject.Inject + +class ReportRepositoryImpl @Inject constructor( + private val reportService: ReportService, +) : ReportRepository { + override suspend fun reportVote(targetId: Long) = reportService.reportVote( + ReportVoteRequest( + targetId = targetId, + ), + ).getOrThrow() +} diff --git a/data/src/main/java/com/susu/data/remote/api/BlockService.kt b/data/src/main/java/com/susu/data/remote/api/BlockService.kt new file mode 100644 index 00000000..b0e0bc97 --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/api/BlockService.kt @@ -0,0 +1,28 @@ +package com.susu.data.remote.api + +import com.susu.data.remote.model.request.BlockUserRequest +import com.susu.data.remote.model.request.CreateVoteRequest +import com.susu.data.remote.model.request.EditVoteRequest +import com.susu.data.remote.model.request.ReportVoteRequest +import com.susu.data.remote.model.request.VoteRequest +import com.susu.data.remote.model.response.PopularVoteResponse +import com.susu.data.remote.model.response.PostCategoryConfig +import com.susu.data.remote.model.response.VoteDetailResponse +import com.susu.data.remote.model.response.VoteListResponse +import com.susu.data.remote.model.response.VoteResponse +import com.susu.data.remote.retrofit.ApiResult +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.PATCH +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.Query + +interface BlockService { + + @POST("blocks") + suspend fun blockUser( + @Body blockUserRequest: BlockUserRequest, + ): ApiResult +} diff --git a/data/src/main/java/com/susu/data/remote/api/ReportService.kt b/data/src/main/java/com/susu/data/remote/api/ReportService.kt new file mode 100644 index 00000000..8864e297 --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/api/ReportService.kt @@ -0,0 +1,27 @@ +package com.susu.data.remote.api + +import com.susu.data.remote.model.request.CreateVoteRequest +import com.susu.data.remote.model.request.EditVoteRequest +import com.susu.data.remote.model.request.ReportVoteRequest +import com.susu.data.remote.model.request.VoteRequest +import com.susu.data.remote.model.response.PopularVoteResponse +import com.susu.data.remote.model.response.PostCategoryConfig +import com.susu.data.remote.model.response.VoteDetailResponse +import com.susu.data.remote.model.response.VoteListResponse +import com.susu.data.remote.model.response.VoteResponse +import com.susu.data.remote.retrofit.ApiResult +import retrofit2.http.Body +import retrofit2.http.DELETE +import retrofit2.http.GET +import retrofit2.http.PATCH +import retrofit2.http.POST +import retrofit2.http.Path +import retrofit2.http.Query + +interface ReportService { + + @POST("reports") + suspend fun reportVote( + @Body reportVoteRequest: ReportVoteRequest, + ): ApiResult +} diff --git a/data/src/main/java/com/susu/data/remote/di/ApiServiceModule.kt b/data/src/main/java/com/susu/data/remote/di/ApiServiceModule.kt index 60ea9578..6b5041c4 100644 --- a/data/src/main/java/com/susu/data/remote/di/ApiServiceModule.kt +++ b/data/src/main/java/com/susu/data/remote/di/ApiServiceModule.kt @@ -1,10 +1,12 @@ package com.susu.data.remote.di import com.susu.data.remote.api.AuthService +import com.susu.data.remote.api.BlockService import com.susu.data.remote.api.CategoryService import com.susu.data.remote.api.EnvelopesService import com.susu.data.remote.api.FriendService import com.susu.data.remote.api.LedgerService +import com.susu.data.remote.api.ReportService import com.susu.data.remote.api.SignUpService import com.susu.data.remote.api.StatisticsService import com.susu.data.remote.api.TermService @@ -87,4 +89,16 @@ object ApiServiceModule { fun providesVoteService(retrofit: Retrofit): VoteService { return retrofit.create(VoteService::class.java) } + + @Singleton + @Provides + fun providesReportService(retrofit: Retrofit): ReportService { + return retrofit.create(ReportService::class.java) + } + + @Singleton + @Provides + fun providesBlockService(retrofit: Retrofit): BlockService { + return retrofit.create(BlockService::class.java) + } } diff --git a/data/src/main/java/com/susu/data/remote/model/request/BlockUserRequest.kt b/data/src/main/java/com/susu/data/remote/model/request/BlockUserRequest.kt new file mode 100644 index 00000000..4756bb05 --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/request/BlockUserRequest.kt @@ -0,0 +1,9 @@ +package com.susu.data.remote.model.request + +import kotlinx.serialization.Serializable + +@Serializable +data class BlockUserRequest( + val targetId: Long, + val targetType: String = "USER", +) diff --git a/data/src/main/java/com/susu/data/remote/model/request/ReportVoteRequest.kt b/data/src/main/java/com/susu/data/remote/model/request/ReportVoteRequest.kt new file mode 100644 index 00000000..9616315f --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/request/ReportVoteRequest.kt @@ -0,0 +1,10 @@ +package com.susu.data.remote.model.request + +import kotlinx.serialization.Serializable + +@Serializable +data class ReportVoteRequest( + val metadataId: Long = 1, + val targetId: Long, + val targetType: String = "POST", +) diff --git a/domain/src/main/java/com/susu/domain/repository/BlockRepository.kt b/domain/src/main/java/com/susu/domain/repository/BlockRepository.kt new file mode 100644 index 00000000..fb56d1de --- /dev/null +++ b/domain/src/main/java/com/susu/domain/repository/BlockRepository.kt @@ -0,0 +1,8 @@ +package com.susu.domain.repository + +interface BlockRepository { + + suspend fun blockUser( + targetId: Long, + ) +} diff --git a/domain/src/main/java/com/susu/domain/repository/ReportRepository.kt b/domain/src/main/java/com/susu/domain/repository/ReportRepository.kt new file mode 100644 index 00000000..b9344301 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/repository/ReportRepository.kt @@ -0,0 +1,8 @@ +package com.susu.domain.repository + +interface ReportRepository { + + suspend fun reportVote( + targetId: Long, + ) +} diff --git a/domain/src/main/java/com/susu/domain/usecase/block/BlockUserUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/block/BlockUserUseCase.kt new file mode 100644 index 00000000..aeb0b0d5 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/block/BlockUserUseCase.kt @@ -0,0 +1,14 @@ +package com.susu.domain.usecase.block + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.BlockRepository +import com.susu.domain.repository.EnvelopesRepository +import javax.inject.Inject + +class BlockUserUseCase @Inject constructor( + private val blockRepository: BlockRepository, +) { + suspend operator fun invoke(id: Long) = runCatchingIgnoreCancelled { + blockRepository.blockUser(id) + } +} diff --git a/domain/src/main/java/com/susu/domain/usecase/report/ReportVoteUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/report/ReportVoteUseCase.kt new file mode 100644 index 00000000..94c7109c --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/report/ReportVoteUseCase.kt @@ -0,0 +1,13 @@ +package com.susu.domain.usecase.report + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.ReportRepository +import javax.inject.Inject + +class ReportVoteUseCase @Inject constructor( + private val reportRepository: ReportRepository, +) { + suspend operator fun invoke(id: Long) = runCatchingIgnoreCancelled { + reportRepository.reportVote(id) + } +} From 382ebcd899f75ca875d01370467af30bc32f7553 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 17:08:18 +0900 Subject: [PATCH 38/95] =?UTF-8?q?feat:=20CommunityScreen=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EC=8B=A0=EA=B3=A0,=20=EC=B0=A8=EB=8B=A8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/EnvelopesRepository.kt | 2 +- .../community/community/CommunityContract.kt | 1 + .../community/community/CommunityScreen.kt | 25 ++++++++++++++- .../community/community/CommunityViewModel.kt | 32 ++++++++++++++++++- .../community/community/component/VoteCard.kt | 3 ++ .../navigation/CommunityNavigation.kt | 1 + 6 files changed, 61 insertions(+), 3 deletions(-) diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index 3993c587..274b9149 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -60,7 +60,7 @@ interface EnvelopesRepository { memo: String? = null, hasVisited: Boolean? = null, handedOverAt: LocalDateTime, - categoryId: Long, + categoryId: Long? = null, customCategory: String? = null, ): Envelope } diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt index 71ec5147..a64e0f80 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt @@ -22,4 +22,5 @@ sealed interface CommunitySideEffect : SideEffect { data object NavigateVoteAdd : CommunitySideEffect data object NavigateVoteSearch : CommunitySideEffect data class NavigateVoteDetail(val voteId: Long) : CommunitySideEffect + data class ShowDeleteDialog(val onConfirmRequest: () -> Unit, val onCheckedAction: () -> Unit) : CommunitySideEffect } diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt index ead105eb..ba95dc92 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -51,6 +52,8 @@ import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.Category +import com.susu.core.model.Vote +import com.susu.core.ui.DialogToken import com.susu.core.ui.extension.OnBottomReached import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable @@ -70,15 +73,30 @@ fun CommunityRoute( navigateVoteAdd: () -> Unit, navigateVoteSearch: () -> Unit, navigateVoteDetail: (Long) -> Unit, + onShowDialog: (DialogToken) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val context = LocalContext.current viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { is CommunitySideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) CommunitySideEffect.NavigateVoteAdd -> navigateVoteAdd() is CommunitySideEffect.NavigateVoteDetail -> navigateVoteDetail(sideEffect.voteId) CommunitySideEffect.NavigateVoteSearch -> navigateVoteSearch() + is CommunitySideEffect.ShowDeleteDialog -> onShowDialog( + DialogToken( + title = "해당 글을 신고할까요?", + text = "신고된 글은 수수에서 확인한 후에 모두에게 제재돼요\n" + + "이 작성자의 글을 보고 싶지 않다면 작성자를 차단해주세요", + confirmText = "신고하기", + dismissText = "닫기", + checkboxText = "작성자도 바로 차단하기", + defaultChecked = false, + onConfirmRequest = sideEffect.onConfirmRequest, + onCheckedAction = sideEffect.onCheckedAction, + ), + ) } } @@ -119,6 +137,7 @@ fun CommunityRoute( onClickShowVotePopular = viewModel::toggleShowVotePopular, onClickVote = viewModel::navigateVoteDetail, onClickSearchIcon = viewModel::navigateVoteSearch, + onClickReport = viewModel::showReportDialog, ) } @@ -135,6 +154,7 @@ fun CommunityScreen( onClickCategory: (Category?) -> Unit = {}, onClickShowVotePopular: () -> Unit = {}, onClickShowMine: () -> Unit = {}, + onClickReport: (Vote) -> Unit = {}, ) { Box( modifier = Modifier @@ -295,13 +315,16 @@ fun CommunityScreen( vote = vote, currentTime = currentTime, onClick = { onClickVote(vote.id) }, + onClickReport = onClickReport, ) } } if (uiState.voteList.isEmpty()) { Box( - modifier = Modifier.fillMaxWidth().weight(1f), + modifier = Modifier + .fillMaxWidth() + .weight(1f), contentAlignment = Alignment.Center, ) { Text( diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt index 61e5fb63..08fe13f0 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt @@ -3,13 +3,17 @@ package com.susu.feature.community.community import androidx.lifecycle.viewModelScope import com.susu.core.model.Category import com.susu.core.model.Vote +import com.susu.core.model.exception.NotFoundLedgerException import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri +import com.susu.domain.usecase.block.BlockUserUseCase +import com.susu.domain.usecase.report.ReportVoteUseCase import com.susu.domain.usecase.vote.GetPopularVoteListUseCase import com.susu.domain.usecase.vote.GetPostCategoryConfigUseCase import com.susu.domain.usecase.vote.GetVoteListUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -21,6 +25,8 @@ class CommunityViewModel @Inject constructor( private val getVoteListUseCase: GetVoteListUseCase, private val getPostCategoryConfigUseCase: GetPostCategoryConfigUseCase, private val getPopularVoteListUseCase: GetPopularVoteListUseCase, + private val reportVoteUseCase: ReportVoteUseCase, + private val blockUserUseCase: BlockUserUseCase, ) : BaseViewModel( CommunityState(), ) { @@ -85,7 +91,7 @@ class CommunityViewModel @Inject constructor( .toPersistentList(), popularVoteList = popularVoteList .filter { it.id != toDeleteVoteId } - .toPersistentList() + .toPersistentList(), ) } } @@ -181,4 +187,28 @@ class CommunityViewModel @Inject constructor( fun navigateVoteDetail(id: Long) = postSideEffect(CommunitySideEffect.NavigateVoteDetail(id)) fun navigateVoteSearch() = postSideEffect(CommunitySideEffect.NavigateVoteSearch) + + fun showReportDialog(vote: Vote) = postSideEffect( + CommunitySideEffect.ShowDeleteDialog( + onConfirmRequest = { reportVote(vote.id) }, + onCheckedAction = { blockUser(vote.uid) }, + ), + ) + + private fun reportVote(voteId: Long): Job = viewModelScope.launch { + reportVoteUseCase(voteId) + .onFailure { throwable -> + postSideEffect(CommunitySideEffect.HandleException(throwable = throwable, retry = { reportVote(voteId) })) + } + } + + private fun blockUser(uid: Long): Job = viewModelScope.launch { + blockUserUseCase(uid) + .onSuccess { + getVoteList(true) + } + .onFailure { throwable -> + postSideEffect(CommunitySideEffect.HandleException(throwable = throwable, retry = { blockUser(uid) })) + } + } } diff --git a/feature/community/src/main/java/com/susu/feature/community/community/component/VoteCard.kt b/feature/community/src/main/java/com/susu/feature/community/community/component/VoteCard.kt index 68639a82..4357c7b5 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/component/VoteCard.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/component/VoteCard.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -41,6 +42,7 @@ fun VoteCard( vote: Vote = Vote(), currentTime: LocalDateTime = LocalDateTime.now(), onClick: () -> Unit = {}, + onClickReport: (Vote) -> Unit = {}, ) { Column( modifier = Modifier @@ -126,6 +128,7 @@ fun VoteCard( ) Image( + modifier = Modifier.clip(CircleShape).susuClickable(onClick = { onClickReport(vote) }), painter = painterResource(id = R.drawable.ic_report), contentDescription = stringResource(com.susu.core.ui.R.string.content_description_report_button), ) diff --git a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt index df47631f..d5683c3a 100644 --- a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt +++ b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt @@ -65,6 +65,7 @@ fun NavGraphBuilder.communityNavGraph( navigateVoteAdd = navigateVoteAdd, navigateVoteSearch = navigateVoteSearch, navigateVoteDetail = navigateVoteDetail, + onShowDialog = onShowDialog, handleException = handleException, ) } From bcb3d83fa1211d4a362848bfba78316ab87095d7 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 17:14:06 +0900 Subject: [PATCH 39/95] =?UTF-8?q?feat:=20Report=20Dialog=20String=20Res=20?= =?UTF-8?q?=EC=B6=94=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/community/community/CommunityScreen.kt | 12 +++++------- feature/community/src/main/res/values/strings.xml | 5 +++++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt index ba95dc92..36e885b7 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt @@ -86,13 +86,11 @@ fun CommunityRoute( CommunitySideEffect.NavigateVoteSearch -> navigateVoteSearch() is CommunitySideEffect.ShowDeleteDialog -> onShowDialog( DialogToken( - title = "해당 글을 신고할까요?", - text = "신고된 글은 수수에서 확인한 후에 모두에게 제재돼요\n" + - "이 작성자의 글을 보고 싶지 않다면 작성자를 차단해주세요", - confirmText = "신고하기", - dismissText = "닫기", - checkboxText = "작성자도 바로 차단하기", - defaultChecked = false, + title = context.getString(R.string.dialog_report_title), + text = context.getString(R.string.dialog_report_body), + confirmText = context.getString(R.string.dialog_report_confirm_text), + dismissText = context.getString(R.string.dialog_report_dismiss_text), + checkboxText = context.getString(R.string.dialog_report_checkbox_text), onConfirmRequest = sideEffect.onConfirmRequest, onCheckedAction = sideEffect.onCheckedAction, ), diff --git a/feature/community/src/main/res/values/strings.xml b/feature/community/src/main/res/values/strings.xml index c37b12fc..bc1ec2d0 100644 --- a/feature/community/src/main/res/values/strings.xml +++ b/feature/community/src/main/res/values/strings.xml @@ -24,4 +24,9 @@ 삭제한 투표는 다시 복구할 수 없어요 투표가 삭제되었어요 시작된 투표는 보기를 편집할 수 없어요 + 해당 글을 신고할까요? + 신고된 글은 수수에서 확인한 후에 모두에게 제재돼요\n이 작성자의 글을 보고 싶지 않다면 작성자를 차단해주세요 + 신고하기 + 닫기 + 작성자도 바로 차단하기 From 499514c764490f10859c68ff35a908e3826f739c Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 19:16:16 +0900 Subject: [PATCH 40/95] =?UTF-8?q?feat:=20=ED=94=BC=EB=93=9C=EB=B0=B1=20?= =?UTF-8?q?=EB=82=A8=EA=B8=B0=EA=B8=B0,=20=EB=AC=B8=EC=9D=98=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20=EA=B5=AC=EA=B8=80?= =?UTF-8?q?=ED=8F=BC=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/src/main/java/com/susu/core/ui/Consts.kt | 2 ++ .../com/susu/feature/mypage/main/MyPageDefaultScreen.kt | 8 ++++++++ .../com/susu/feature/mypage/social/MyPageSocialScreen.kt | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/core/ui/src/main/java/com/susu/core/ui/Consts.kt b/core/ui/src/main/java/com/susu/core/ui/Consts.kt index bdb9c1db..8be70e1b 100644 --- a/core/ui/src/main/java/com/susu/core/ui/Consts.kt +++ b/core/ui/src/main/java/com/susu/core/ui/Consts.kt @@ -26,6 +26,8 @@ val USER_BIRTH_RANGE = 1930..2030 const val INTENT_ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE" const val PRIVACY_POLICY_URL = "https://sites.google.com/view/team-oksusu/%ED%99%88" +const val SUSU_GOOGLE_FROM_URL = "https://forms.gle/FHky26kAQdde9RcD7" + enum class SnsProviders( val path: String, @StringRes val nameId: Int, diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt index 5d9a251a..80e77fb8 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt @@ -1,6 +1,8 @@ package com.susu.feature.mypage.main import android.content.Context +import android.content.Intent +import android.net.Uri import android.os.Environment import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -41,6 +43,7 @@ import com.susu.core.designsystem.theme.Gray60 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.DialogToken import com.susu.core.ui.R +import com.susu.core.ui.SUSU_GOOGLE_FROM_URL import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable @@ -137,6 +140,9 @@ fun MyPageDefaultRoute( onLogout = viewModel::showLogoutDialog, onWithdraw = viewModel::showWithdrawDialog, onExport = viewModel::showExportDialog, + onClickFeedback = { + context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(SUSU_GOOGLE_FROM_URL))) + }, navigateToInfo = navigateToInfo, navigateToSocial = navigateToSocial, navigateToPrivacyPolicy = navigateToPrivacyPolicy, @@ -150,6 +156,7 @@ fun MyPageDefaultScreen( onLogout: () -> Unit = {}, onWithdraw: () -> Unit = {}, onExport: () -> Unit = {}, + onClickFeedback: () -> Unit = {}, navigateToInfo: () -> Unit = {}, navigateToSocial: () -> Unit = {}, navigateToPrivacyPolicy: () -> Unit = {}, @@ -248,6 +255,7 @@ fun MyPageDefaultScreen( color = GhostButtonColor.Orange, style = SmallButtonStyle.height40, text = stringResource(com.susu.feature.mypage.R.string.mypage_feedback), + onClick = onClickFeedback, ) } } diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/social/MyPageSocialScreen.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/social/MyPageSocialScreen.kt index aeb64fc2..2b104e78 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/social/MyPageSocialScreen.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/social/MyPageSocialScreen.kt @@ -1,5 +1,7 @@ package com.susu.feature.mypage.social +import android.content.Intent +import android.net.Uri import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -17,6 +19,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -32,6 +35,7 @@ import com.susu.core.designsystem.theme.Gray25 import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Gray70 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.ui.SUSU_GOOGLE_FROM_URL import com.susu.core.ui.SnsProviders import com.susu.feature.mypage.R @@ -48,6 +52,8 @@ fun MyPageSocialScreen( padding: PaddingValues = PaddingValues(), popBackStack: () -> Unit = {}, ) { + val context = LocalContext.current + Column( modifier = Modifier.fillMaxSize().padding(padding), horizontalAlignment = Alignment.CenterHorizontally, @@ -83,6 +89,7 @@ fun MyPageSocialScreen( style = XSmallButtonStyle.height36, isActive = false, isClickable = true, + onClick = { context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(SUSU_GOOGLE_FROM_URL))) }, ) } } From adcd80d288d0ac519b5c76f0fe50a9a2fa57e370 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 19:53:20 +0900 Subject: [PATCH 41/95] =?UTF-8?q?chore:=20google=20play=20app=20update=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/mypage/build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts index 6bfd827c..2b52f041 100644 --- a/feature/mypage/build.gradle.kts +++ b/feature/mypage/build.gradle.kts @@ -9,4 +9,5 @@ android { dependencies { implementation(libs.kakao.sdk.user) + implementation(libs.play.app.update) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bdf20bf1..cdf7900d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,6 +65,7 @@ protobuf = "3.24.4" junit-junit = "4.13.2" kakao-user = "2.18.0" +play-app-update-ktx = "2.1.0" [plugins] ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } @@ -185,6 +186,7 @@ protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref junit-junit = { group = "junit", name = "junit", version.ref = "junit-junit" } kakao-sdk-user = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-user"} +play-app-update = { group = "com.google.android.play", name = "app-update-ktx", version.ref = "play-app-update-ktx"} [bundles] firebase = ["firebase-analytics"] From 04c8ef157c7afc1406568911bbcf9c57c4fe38d8 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 19:53:39 +0900 Subject: [PATCH 42/95] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=95=B1=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/mypage/main/MyPageContract.kt | 2 +- .../mypage/main/MyPageDefaultScreen.kt | 29 +++++++++++++++---- .../feature/mypage/main/MyPageViewModel.kt | 10 +++---- .../mypage/src/main/res/values/strings.xml | 1 + 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageContract.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageContract.kt index 23924bbb..1671f8c3 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageContract.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageContract.kt @@ -20,5 +20,5 @@ sealed interface MyPageEffect : SideEffect { data class MyPageState( val isLoading: Boolean = false, val userName: String = "", - val appVersion: String = "", + val canUpdate: Boolean = false, ) : UiState diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt index 5d9a251a..20507753 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -29,6 +30,8 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.install.model.UpdateAvailability import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.LogoIcon import com.susu.core.designsystem.component.button.GhostButtonColor @@ -59,6 +62,14 @@ fun MyPageDefaultRoute( handleException: (Throwable, () -> Unit) -> Unit, ) { val context = LocalContext.current + val appUpdateManager = remember { AppUpdateManagerFactory.create(context) } + + LaunchedEffect(key1 = Unit) { + viewModel.getUserInfo() + appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> + viewModel.updateCanUpdate(appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) + } + } viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { @@ -212,11 +223,19 @@ fun MyPageDefaultScreen( MyPageMenuItem( titleText = stringResource(com.susu.feature.mypage.R.string.mypage_app_version), action = { - Text( - text = stringResource(com.susu.feature.mypage.R.string.mypage_update), - style = SusuTheme.typography.title_xxs, - color = Gray60, - ) + if (uiState.canUpdate) { + Text( + text = stringResource(com.susu.feature.mypage.R.string.mypage_update), + style = SusuTheme.typography.title_xxs, + color = Gray60, + ) + } else { + Text( + text = stringResource(com.susu.feature.mypage.R.string.mypage_default_recent_version, currentAppVersion), + style = SusuTheme.typography.title_xxs, + color = Gray60, + ) + } }, ) diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageViewModel.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageViewModel.kt index 10096a3f..259d1150 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageViewModel.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageViewModel.kt @@ -21,15 +21,11 @@ class MyPageViewModel @Inject constructor( private val downloadExcelUseCase: DownloadExcelUseCase, ) : BaseViewModel(MyPageState()) { - init { - getUserInfo() - } - fun showExportDialog() = postSideEffect(MyPageEffect.ShowExportDialog) fun showLogoutDialog() = postSideEffect(MyPageEffect.ShowLogoutDialog) fun showWithdrawDialog() = postSideEffect(MyPageEffect.ShowWithdrawDialog) - private fun getUserInfo() { + fun getUserInfo() { viewModelScope.launch { intent { copy(isLoading = true) } getUserUseCase().onSuccess { @@ -44,6 +40,10 @@ class MyPageViewModel @Inject constructor( } } + fun updateCanUpdate(canUpdate: Boolean) { + intent { copy(canUpdate = canUpdate) } + } + fun logout() { UserApiClient.instance.logout { error -> if (error != null) { diff --git a/feature/mypage/src/main/res/values/strings.xml b/feature/mypage/src/main/res/values/strings.xml index 7344015c..c3acb499 100644 --- a/feature/mypage/src/main/res/values/strings.xml +++ b/feature/mypage/src/main/res/values/strings.xml @@ -28,4 +28,5 @@ 문의 남기기 프로필 이미지 이름은 한글 또는 영문 10글자로 입력해주세요 + 최신 버전 %s From f58bdbaa5c46d4c303c5dad75446c0dca8d583b5 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 19:53:20 +0900 Subject: [PATCH 43/95] =?UTF-8?q?chore:=20google=20play=20app=20update=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/mypage/build.gradle.kts | 1 + gradle/libs.versions.toml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/feature/mypage/build.gradle.kts b/feature/mypage/build.gradle.kts index 6bfd827c..2b52f041 100644 --- a/feature/mypage/build.gradle.kts +++ b/feature/mypage/build.gradle.kts @@ -9,4 +9,5 @@ android { dependencies { implementation(libs.kakao.sdk.user) + implementation(libs.play.app.update) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bdf20bf1..cdf7900d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -65,6 +65,7 @@ protobuf = "3.24.4" junit-junit = "4.13.2" kakao-user = "2.18.0" +play-app-update-ktx = "2.1.0" [plugins] ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } @@ -185,6 +186,7 @@ protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref junit-junit = { group = "junit", name = "junit", version.ref = "junit-junit" } kakao-sdk-user = { group = "com.kakao.sdk", name = "v2-user", version.ref = "kakao-user"} +play-app-update = { group = "com.google.android.play", name = "app-update-ktx", version.ref = "play-app-update-ktx"} [bundles] firebase = ["firebase-analytics"] From 5ba77c2dd44d83d83394d4de2540b09277ed5d2a Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 19:53:39 +0900 Subject: [PATCH 44/95] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=95=B1=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B0=80=EB=8A=A5=20=EC=97=AC=EB=B6=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/mypage/main/MyPageContract.kt | 2 +- .../mypage/main/MyPageDefaultScreen.kt | 29 +++++++++++++++---- .../feature/mypage/main/MyPageViewModel.kt | 10 +++---- .../mypage/src/main/res/values/strings.xml | 1 + 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageContract.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageContract.kt index 23924bbb..1671f8c3 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageContract.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageContract.kt @@ -20,5 +20,5 @@ sealed interface MyPageEffect : SideEffect { data class MyPageState( val isLoading: Boolean = false, val userName: String = "", - val appVersion: String = "", + val canUpdate: Boolean = false, ) : UiState diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt index 80e77fb8..af390a6a 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -31,6 +32,8 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.android.play.core.appupdate.AppUpdateManagerFactory +import com.google.android.play.core.install.model.UpdateAvailability import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.LogoIcon import com.susu.core.designsystem.component.button.GhostButtonColor @@ -62,6 +65,14 @@ fun MyPageDefaultRoute( handleException: (Throwable, () -> Unit) -> Unit, ) { val context = LocalContext.current + val appUpdateManager = remember { AppUpdateManagerFactory.create(context) } + + LaunchedEffect(key1 = Unit) { + viewModel.getUserInfo() + appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> + viewModel.updateCanUpdate(appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) + } + } viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { @@ -219,11 +230,19 @@ fun MyPageDefaultScreen( MyPageMenuItem( titleText = stringResource(com.susu.feature.mypage.R.string.mypage_app_version), action = { - Text( - text = stringResource(com.susu.feature.mypage.R.string.mypage_update), - style = SusuTheme.typography.title_xxs, - color = Gray60, - ) + if (uiState.canUpdate) { + Text( + text = stringResource(com.susu.feature.mypage.R.string.mypage_update), + style = SusuTheme.typography.title_xxs, + color = Gray60, + ) + } else { + Text( + text = stringResource(com.susu.feature.mypage.R.string.mypage_default_recent_version, currentAppVersion), + style = SusuTheme.typography.title_xxs, + color = Gray60, + ) + } }, ) diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageViewModel.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageViewModel.kt index 10096a3f..259d1150 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageViewModel.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageViewModel.kt @@ -21,15 +21,11 @@ class MyPageViewModel @Inject constructor( private val downloadExcelUseCase: DownloadExcelUseCase, ) : BaseViewModel(MyPageState()) { - init { - getUserInfo() - } - fun showExportDialog() = postSideEffect(MyPageEffect.ShowExportDialog) fun showLogoutDialog() = postSideEffect(MyPageEffect.ShowLogoutDialog) fun showWithdrawDialog() = postSideEffect(MyPageEffect.ShowWithdrawDialog) - private fun getUserInfo() { + fun getUserInfo() { viewModelScope.launch { intent { copy(isLoading = true) } getUserUseCase().onSuccess { @@ -44,6 +40,10 @@ class MyPageViewModel @Inject constructor( } } + fun updateCanUpdate(canUpdate: Boolean) { + intent { copy(canUpdate = canUpdate) } + } + fun logout() { UserApiClient.instance.logout { error -> if (error != null) { diff --git a/feature/mypage/src/main/res/values/strings.xml b/feature/mypage/src/main/res/values/strings.xml index 7344015c..c3acb499 100644 --- a/feature/mypage/src/main/res/values/strings.xml +++ b/feature/mypage/src/main/res/values/strings.xml @@ -28,4 +28,5 @@ 문의 남기기 프로필 이미지 이름은 한글 또는 영문 10글자로 입력해주세요 + 최신 버전 %s From a8080c8d71a3fa2793789016c435ce716172563e Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 20:13:07 +0900 Subject: [PATCH 45/95] =?UTF-8?q?feat:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=95=B1=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EA=B0=80=EB=8A=A5=20=EC=8B=9C=20=ED=94=8C=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=ED=86=A0=EC=96=B4=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/src/main/java/com/susu/core/ui/Consts.kt | 1 + .../susu/feature/mypage/main/MyPageDefaultScreen.kt | 11 +++++++++++ .../feature/mypage/main/component/MyPageMenuItem.kt | 2 ++ 3 files changed, 14 insertions(+) diff --git a/core/ui/src/main/java/com/susu/core/ui/Consts.kt b/core/ui/src/main/java/com/susu/core/ui/Consts.kt index 8be70e1b..65ee0c3c 100644 --- a/core/ui/src/main/java/com/susu/core/ui/Consts.kt +++ b/core/ui/src/main/java/com/susu/core/ui/Consts.kt @@ -27,6 +27,7 @@ val USER_BIRTH_RANGE = 1930..2030 const val INTENT_ACTION_DOWNLOAD_COMPLETE = "android.intent.action.DOWNLOAD_COMPLETE" const val PRIVACY_POLICY_URL = "https://sites.google.com/view/team-oksusu/%ED%99%88" const val SUSU_GOOGLE_FROM_URL = "https://forms.gle/FHky26kAQdde9RcD7" +const val SUSU_GOOGLE_PLAY_STORE_URL = "https://play.google.com/store/apps/details?id=com.oksusu.susu" enum class SnsProviders( val path: String, diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt index af390a6a..9ddd6b34 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt @@ -47,6 +47,7 @@ import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.DialogToken import com.susu.core.ui.R import com.susu.core.ui.SUSU_GOOGLE_FROM_URL +import com.susu.core.ui.SUSU_GOOGLE_PLAY_STORE_URL import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable @@ -229,9 +230,19 @@ fun MyPageDefaultScreen( MyPageMenuItem( titleText = stringResource(com.susu.feature.mypage.R.string.mypage_app_version), + rippleEnabled = uiState.canUpdate, action = { if (uiState.canUpdate) { Text( + modifier = Modifier.susuClickable( + onClick = { + val intent = Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(SUSU_GOOGLE_PLAY_STORE_URL) + setPackage("com.android.vending") // Google Play 스토어 앱으로 연결되게 함. + } + context.startActivity(intent) + } + ), text = stringResource(com.susu.feature.mypage.R.string.mypage_update), style = SusuTheme.typography.title_xxs, color = Gray60, diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/component/MyPageMenuItem.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/component/MyPageMenuItem.kt index 9520aa05..b24701a2 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/component/MyPageMenuItem.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/component/MyPageMenuItem.kt @@ -30,11 +30,13 @@ fun MyPageMenuItem( action: @Composable (() -> Unit)? = null, actionItemPadding: Dp = SusuTheme.spacing.spacing_m, onMenuClick: () -> Unit = {}, + rippleEnabled: Boolean = true, ) { Row( modifier = modifier .fillMaxWidth() .susuClickable( + rippleEnabled = rippleEnabled, onClick = onMenuClick, ) .padding(padding), From f6a148b67b77aa56279e177d3b836bee94a10e50 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 20:26:19 +0900 Subject: [PATCH 46/95] chore: ktlint check --- .../java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt index 9ddd6b34..b0f240d0 100644 --- a/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt +++ b/feature/mypage/src/main/java/com/susu/feature/mypage/main/MyPageDefaultScreen.kt @@ -241,7 +241,7 @@ fun MyPageDefaultScreen( setPackage("com.android.vending") // Google Play 스토어 앱으로 연결되게 함. } context.startActivity(intent) - } + }, ), text = stringResource(com.susu.feature.mypage.R.string.mypage_update), style = SusuTheme.typography.title_xxs, From b4fc264f7686902268daee48a13d253f7cd4fa6f Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 20:42:26 +0900 Subject: [PATCH 47/95] =?UTF-8?q?feat:=20=EB=B3=80=EA=B2=BD=EB=90=9C=20?= =?UTF-8?q?=EC=9E=A5=EB=B6=80=20=ED=8E=B8=EC=A7=91=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../received/ledgeredit/LedgerEditContract.kt | 8 +- .../received/ledgeredit/LedgerEditScreen.kt | 76 ++++++++++++++----- .../ledgeredit/LedgerEditViewModel.kt | 20 ++++- .../src/main/res/drawable/ic_date_change.xml | 10 +++ .../received/src/main/res/values/strings.xml | 4 + 5 files changed, 96 insertions(+), 22 deletions(-) create mode 100644 feature/received/src/main/res/drawable/ic_date_change.xml diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditContract.kt b/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditContract.kt index 84aa49a3..71cd52a4 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditContract.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditContract.kt @@ -13,18 +13,20 @@ data class LedgerEditState( val startYear: Int = 0, val startMonth: Int = 0, val startDay: Int = 0, - val endYear: Int = 0, - val endMonth: Int = 0, - val endDay: Int = 0, + val endYear: Int? = null, + val endMonth: Int? = null, + val endDay: Int? = null, val categoryConfigList: PersistentList = persistentListOf(Category()), val showCustomCategoryButton: Boolean = false, val isCustomCategoryChipSaved: Boolean = false, val showStartDateBottomSheet: Boolean = false, val showEndDateBottomSheet: Boolean = false, + val showOnlyStartDate: Boolean = false, ) : UiState { val isSelectedCustomCategory = selectedCategoryId == categoryConfigList.last().id val saveButtonEnabled = when { name.isEmpty() -> false + endYear == null -> false isSelectedCustomCategory && (customCategory.isEmpty() || isCustomCategoryChipSaved.not()) -> false else -> true } diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditScreen.kt b/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditScreen.kt index 67b3c768..0a2cb4a3 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember @@ -21,6 +22,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -31,18 +33,25 @@ import com.susu.core.designsystem.component.appbar.icon.BackIcon import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuLimitDatePickerBottomSheet import com.susu.core.designsystem.component.button.AddConditionButton import com.susu.core.designsystem.component.button.FilledButtonColor +import com.susu.core.designsystem.component.button.LinedButtonColor import com.susu.core.designsystem.component.button.MediumButtonStyle import com.susu.core.designsystem.component.button.SmallButtonStyle import com.susu.core.designsystem.component.button.SusuFilledButton +import com.susu.core.designsystem.component.button.SusuLinedButton +import com.susu.core.designsystem.component.button.XSmallButtonStyle import com.susu.core.designsystem.component.textfield.SusuBasicTextField import com.susu.core.designsystem.component.textfieldbutton.SusuTextFieldWrapContentButton import com.susu.core.designsystem.component.textfieldbutton.TextFieldButtonColor import com.susu.core.designsystem.component.textfieldbutton.style.SmallTextFieldButtonStyle +import com.susu.core.designsystem.theme.Gray100 +import com.susu.core.designsystem.theme.Gray30 import com.susu.core.designsystem.theme.Gray80 +import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.util.AnnotatedText +import com.susu.core.ui.util.currentDate import com.susu.feature.received.R import com.susu.feature.received.ledgeredit.component.LedgerEditContainer import kotlinx.coroutines.android.awaitFrame @@ -90,6 +99,8 @@ fun LedgerEditRoute( onClickEndDateText = viewModel::showEndDateBottomSheet, onDismissEndDateBottomSheet = viewModel::hideEndDateBottomSheet, onClickSaveButton = viewModel::editLedger, + onClickAddEndDateButton = viewModel::showEndDateText, + onClickSetStartDateButton = viewModel::showOnlyStartDateText, ) } @@ -113,6 +124,8 @@ fun LedgerEditScreen( onClickEndDateText: () -> Unit = {}, onDismissEndDateBottomSheet: () -> Unit = {}, onClickSaveButton: () -> Unit = {}, + onClickAddEndDateButton: () -> Unit = {}, + onClickSetStartDateButton: () -> Unit = {}, ) { Box( modifier = Modifier @@ -195,7 +208,7 @@ fun LedgerEditScreen( AnnotatedText( modifier = Modifier.susuClickable(rippleEnabled = false, onClick = onClickStartDateText), originalText = stringResource( - R.string.ledger_edit_screen_from_date, + if (uiState.showOnlyStartDate) R.string.ledger_edit_screen_date else R.string.ledger_edit_screen_from_date, uiState.startYear, uiState.startMonth, uiState.startDay, @@ -203,27 +216,54 @@ fun LedgerEditScreen( targetTextList = listOf( stringResource(R.string.ledger_edit_screen_year), stringResource(R.string.ledger_edit_screen_month), - stringResource(R.string.ledger_edit_screen_from_day), - ), - originalTextStyle = SusuTheme.typography.title_m, - spanStyle = SusuTheme.typography.title_m.copy(Gray80).toSpanStyle(), - ) - AnnotatedText( - modifier = Modifier.susuClickable(rippleEnabled = false, onClick = onClickEndDateText), - originalText = stringResource( - R.string.ledger_edit_screen_until_date, - uiState.endYear, - uiState.endMonth, - uiState.endDay, - ), - targetTextList = listOf( - stringResource(R.string.ledger_edit_screen_year), - stringResource(R.string.ledger_edit_screen_month), - stringResource(R.string.ledger_edit_screen_until_day), + stringResource(if (uiState.showOnlyStartDate) R.string.ledger_edit_screen_day else R.string.ledger_edit_screen_from_day), ), originalTextStyle = SusuTheme.typography.title_m, spanStyle = SusuTheme.typography.title_m.copy(Gray80).toSpanStyle(), ) + + if (uiState.showOnlyStartDate.not()) { + AnnotatedText( + modifier = Modifier.susuClickable(rippleEnabled = false, onClick = onClickEndDateText), + originalText = stringResource( + R.string.ledger_edit_screen_until_date, + uiState.endYear ?: currentDate.year, + uiState.endMonth ?: currentDate.month.value, + uiState.endDay ?: currentDate.dayOfMonth, + ), + targetTextList = listOf( + stringResource(R.string.ledger_edit_screen_year), + stringResource(R.string.ledger_edit_screen_month), + stringResource(R.string.ledger_edit_screen_until_day), + ), + originalTextStyle = SusuTheme.typography.title_m.copy(if (uiState.endYear == null) Gray30 else Gray100), + spanStyle = SusuTheme.typography.title_m.copy(Gray80).toSpanStyle(), + ) + } + + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_xxs)) + + if (uiState.showOnlyStartDate) { + SusuLinedButton( + color = LinedButtonColor.Orange, + style = XSmallButtonStyle.height28, + text = stringResource(R.string.ledger_edit_screen_add_end_date), + leftIcon = { + Icon(painter = painterResource(id = R.drawable.ic_date_change), contentDescription = null, tint = Orange60) + }, + onClick = onClickAddEndDateButton + ) + } else { + SusuLinedButton( + color = LinedButtonColor.Orange, + style = XSmallButtonStyle.height28, + text = stringResource(R.string.ledger_edit_screen_add_start_date), + leftIcon = { + Icon(painter = painterResource(id = R.drawable.ic_date_change), contentDescription = null, tint = Orange60) + }, + onClick = onClickSetStartDateButton + ) + } } }, ) diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditViewModel.kt index 5913be14..e7dd95ce 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditViewModel.kt @@ -34,7 +34,7 @@ class LedgerEditViewModel @Inject constructor( id = ledgerId, title = name, startAt = LocalDateTime.of(startYear, startMonth, startDay, 0, 0).toKotlinLocalDateTime(), - endAt = LocalDateTime.of(endYear, endMonth, endDay, 0, 0).toKotlinLocalDateTime(), + endAt = LocalDateTime.of(endYear ?: startYear, endMonth ?: startMonth, endDay ?: startDay, 0, 0).toKotlinLocalDateTime(), category = Category( id = selectedCategoryId, customCategory = customCategory.ifEmpty { null }, @@ -75,6 +75,7 @@ class LedgerEditViewModel @Inject constructor( endYear = endDate.year, endMonth = endDate.monthValue, endDay = endDate.dayOfMonth, + showOnlyStartDate = startDate == endDate, customCategory = customCategory ?: "", isCustomCategoryChipSaved = customCategory.isNullOrEmpty().not(), showCustomCategoryButton = ledger.category.customCategory != null, @@ -147,4 +148,21 @@ class LedgerEditViewModel @Inject constructor( fun hideEndDateBottomSheet() = intent { copy(showEndDateBottomSheet = false) } fun popBackStack() = postSideEffect(LedgerEditSideEffect.PopBackStack) + fun showEndDateText() = intent { + copy( + endYear = null, + endMonth = null, + endDay = null, + showOnlyStartDate = false, + ) + } + + fun showOnlyStartDateText() = intent { + copy( + endYear = startYear, + endMonth = startMonth, + endDay = startDay, + showOnlyStartDate = true, + ) + } } diff --git a/feature/received/src/main/res/drawable/ic_date_change.xml b/feature/received/src/main/res/drawable/ic_date_change.xml new file mode 100644 index 00000000..dfb21423 --- /dev/null +++ b/feature/received/src/main/res/drawable/ic_date_change.xml @@ -0,0 +1,10 @@ + + + diff --git a/feature/received/src/main/res/values/strings.xml b/feature/received/src/main/res/values/strings.xml index edc97036..842fda91 100644 --- a/feature/received/src/main/res/values/strings.xml +++ b/feature/received/src/main/res/values/strings.xml @@ -10,10 +10,12 @@ 아직 보낸 봉투가 없어요 받은 봉투 추가하기 %d년 %d월 %d일 부터 + %d년 %d월 %d일 %d년 %d월 %d일 까지 일 부터 + 일 까지 부터 까지 @@ -64,4 +66,6 @@ 봉투를 삭제할까요? 삭제한 봉투는 다시 복구할 수 없어요 봉투가 삭제됐어요 + 종료일 추가 + 시작일만 지정 From 56e6bf27e740d97b5c277480b83afe9b53e625c0 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Thu, 1 Feb 2024 20:43:02 +0900 Subject: [PATCH 48/95] chore: ktlint --- .../susu/feature/received/ledgeredit/LedgerEditScreen.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditScreen.kt b/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditScreen.kt index 0a2cb4a3..0c1dc054 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditScreen.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgeredit/LedgerEditScreen.kt @@ -216,7 +216,9 @@ fun LedgerEditScreen( targetTextList = listOf( stringResource(R.string.ledger_edit_screen_year), stringResource(R.string.ledger_edit_screen_month), - stringResource(if (uiState.showOnlyStartDate) R.string.ledger_edit_screen_day else R.string.ledger_edit_screen_from_day), + stringResource( + if (uiState.showOnlyStartDate) R.string.ledger_edit_screen_day else R.string.ledger_edit_screen_from_day, + ), ), originalTextStyle = SusuTheme.typography.title_m, spanStyle = SusuTheme.typography.title_m.copy(Gray80).toSpanStyle(), @@ -251,7 +253,7 @@ fun LedgerEditScreen( leftIcon = { Icon(painter = painterResource(id = R.drawable.ic_date_change), contentDescription = null, tint = Orange60) }, - onClick = onClickAddEndDateButton + onClick = onClickAddEndDateButton, ) } else { SusuLinedButton( @@ -261,7 +263,7 @@ fun LedgerEditScreen( leftIcon = { Icon(painter = painterResource(id = R.drawable.ic_date_change), contentDescription = null, tint = Orange60) }, - onClick = onClickSetStartDateButton + onClick = onClickSetStartDateButton, ) } } From ded17495063295abcbc7e3ca8f982dbc0c19cc18 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 25 Jan 2024 18:44:38 +0900 Subject: [PATCH 49/95] =?UTF-8?q?refactor:=20RecentSpentGraph=20graph=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=84=20=EC=A7=80=EC=A0=95=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/susu/feature/statistics/StatisticsScreen.kt | 2 +- .../com/susu/feature/statistics/component/RecentSpentGraph.kt | 3 ++- .../feature/statistics/content/{ => my}/MyStatisticsContent.kt | 3 ++- .../statistics/content/{ => my}/MyStatisticsContract.kt | 2 +- .../statistics/content/{ => my}/MyStatisticsViewModel.kt | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) rename feature/statistics/src/main/java/com/susu/feature/statistics/content/{ => my}/MyStatisticsContent.kt (97%) rename feature/statistics/src/main/java/com/susu/feature/statistics/content/{ => my}/MyStatisticsContract.kt (89%) rename feature/statistics/src/main/java/com/susu/feature/statistics/content/{ => my}/MyStatisticsViewModel.kt (95%) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt index 04e13826..45e444c6 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt @@ -26,7 +26,7 @@ import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.DialogToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.feature.statistics.component.StatisticsTab -import com.susu.feature.statistics.content.MyStatisticsRoute +import com.susu.feature.statistics.content.my.MyStatisticsRoute @Composable fun StatisticsRoute( diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt index fad28860..6ca70edc 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt @@ -59,6 +59,7 @@ fun RecentSpentGraph( spentData: PersistentList = persistentListOf(), totalAmount: Int = 0, maximumAmount: Int = 0, + graphTitle: String = "", ) { Column( modifier = modifier @@ -74,7 +75,7 @@ fun RecentSpentGraph( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = stringResource(R.string.statistics_recent_8_total_money), + text = graphTitle, style = SusuTheme.typography.title_xs, color = Gray100, ) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt similarity index 97% rename from feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsContent.kt rename to feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt index cee4fd65..017cc1dc 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt @@ -1,4 +1,4 @@ -package com.susu.feature.statistics.content +package com.susu.feature.statistics.content.my import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -76,6 +76,7 @@ fun MyStatisticsContent( spentData = uiState.statistics.recentSpent.toPersistentList(), maximumAmount = uiState.statistics.recentMaximumSpent, totalAmount = uiState.statistics.recentTotalSpent, + graphTitle = stringResource(R.string.statistics_recent_8_total_money) ) Row( modifier = Modifier diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsContract.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContract.kt similarity index 89% rename from feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsContract.kt rename to feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContract.kt index 98384780..14b418e6 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsContract.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContract.kt @@ -1,4 +1,4 @@ -package com.susu.feature.statistics.content +package com.susu.feature.statistics.content.my import com.susu.core.model.MyStatistics import com.susu.core.ui.base.SideEffect diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsViewModel.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsViewModel.kt similarity index 95% rename from feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsViewModel.kt rename to feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsViewModel.kt index 77854360..949a85c6 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/MyStatisticsViewModel.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsViewModel.kt @@ -1,4 +1,4 @@ -package com.susu.feature.statistics.content +package com.susu.feature.statistics.content.my import androidx.lifecycle.viewModelScope import com.susu.core.ui.base.BaseViewModel From 8b8e4454561304857ae1f3f48d1a28248f52ea5a Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 25 Jan 2024 19:43:59 +0900 Subject: [PATCH 50/95] =?UTF-8?q?feat:=20=EC=88=98=EC=88=98=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EC=98=B5=EC=85=98=20=EC=84=A0=ED=83=9D=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/susu/SusuStatisticsOptionSlot.kt | 135 ++++++++++++++++++ .../res/drawable/ic_statistics_arrow_down.xml | 9 ++ .../src/main/res/values/strings.xml | 3 + 3 files changed, 147 insertions(+) create mode 100644 feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt create mode 100644 feature/statistics/src/main/res/drawable/ic_statistics_arrow_down.xml diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt new file mode 100644 index 00000000..5c9ca942 --- /dev/null +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt @@ -0,0 +1,135 @@ +package com.susu.feature.statistics.content.susu + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.susu.core.designsystem.theme.Gray10 +import com.susu.core.designsystem.theme.Gray50 +import com.susu.core.designsystem.theme.Gray80 +import com.susu.core.designsystem.theme.Orange10 +import com.susu.core.designsystem.theme.Orange60 +import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.ui.extension.susuClickable +import com.susu.core.ui.extension.toMoneyFormat +import com.susu.feature.statistics.R + +@Composable +fun SusuStatisticsOptionSlot( + modifier: Modifier = Modifier, + age: String = "", + relationship: String = "", + category: String = "", + money: Int = 0, + title: String = "", + onAgeClick: () -> Unit = {}, + onRelationshipClick: () -> Unit = {}, + onCategoryClick: () -> Unit = {}, +) { + Column( + modifier = modifier.fillMaxWidth() + .background(color = Gray10, shape = RoundedCornerShape(4.dp)) + .padding(SusuTheme.spacing.spacing_m), + ) { + Text( + text = title, + style = SusuTheme.typography.title_xs, + color = Gray50, + ) + Spacer(modifier = Modifier.height(SusuTheme.spacing.spacing_xxs)) + Column( + modifier = Modifier.fillMaxWidth().background(color = Orange10, shape = RoundedCornerShape(4.dp)) + .padding(horizontal = 12.dp, vertical = 8.dp), + verticalArrangement = Arrangement.Bottom, + ) { + Row { + OptionSlot(text = age, onClick = onAgeClick) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = stringResource(R.string.word_statistics_is), + style = SusuTheme.typography.title_xxs, + color = Gray80, + ) + OptionSlot(text = relationship, onClick = onRelationshipClick) + Spacer(modifier = Modifier.width(4.dp)) + OptionSlot(text = category, onClick = onCategoryClick) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = stringResource(R.string.word_statistics_to), + style = SusuTheme.typography.title_xxs, + color = Gray80, + ) + } + Spacer(modifier = Modifier.height(8.dp)) + Row( + verticalAlignment = Alignment.Bottom, + ) { + Text( + text = stringResource(id = com.susu.core.ui.R.string.money_unit_format, money.toMoneyFormat()), + style = SusuTheme.typography.title_s, + color = Orange60, + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = stringResource(R.string.word_statistics_send), + style = SusuTheme.typography.title_xxs, + color = Gray80, + ) + } + } + } +} + +@Composable +fun OptionSlot( + modifier: Modifier = Modifier, + text: String = "", + onClick: () -> Unit = {}, +) { + Row( + modifier = modifier.background(color = Gray10, shape = RoundedCornerShape(4.dp)) + .susuClickable(onClick = onClick) + .padding( + horizontal = SusuTheme.spacing.spacing_xxs, + vertical = SusuTheme.spacing.spacing_xxxxs, + ), + verticalAlignment = Alignment.CenterVertically, + ) { + Text(text = text, color = Orange60, style = SusuTheme.typography.title_xxxs) + Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxxxs)) + Icon( + painter = painterResource(id = R.drawable.ic_statistics_arrow_down), + tint = Orange60, + contentDescription = "옵션 선택", + ) + } +} + +@Preview +@Composable +fun SusuStatisticsOptionSlotPreview() { + SusuTheme { + SusuStatisticsOptionSlot( + title = "제목", + age = "20대", + relationship = "친구", + category = "결혼식", + money = 50000, + ) + } +} diff --git a/feature/statistics/src/main/res/drawable/ic_statistics_arrow_down.xml b/feature/statistics/src/main/res/drawable/ic_statistics_arrow_down.xml new file mode 100644 index 00000000..412cfffb --- /dev/null +++ b/feature/statistics/src/main/res/drawable/ic_statistics_arrow_down.xml @@ -0,0 +1,9 @@ + + + diff --git a/feature/statistics/src/main/res/values/strings.xml b/feature/statistics/src/main/res/values/strings.xml index a6b24c05..58dd510f 100644 --- a/feature/statistics/src/main/res/values/strings.xml +++ b/feature/statistics/src/main/res/values/strings.xml @@ -16,4 +16,7 @@ 통계를 위한 정보를 알려주세요 나의 평균 거래 상황을 분석하기 위해\n필요한 정보가 있어요 정보 입력하기 + + + 을 보내고 있어요 From 5ba458d59c546ab7ea07ba73e5ffb8dc0611f456 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 25 Jan 2024 19:44:15 +0900 Subject: [PATCH 51/95] =?UTF-8?q?feat:=20=EC=88=98=EC=88=98=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 동작은 하지 않습니다 --- .../content/susu/SusuStatisticsContent.kt | 132 ++++++++++++++++++ .../content/susu/SusuStatisticsContract.kt | 19 +++ 2 files changed, 151 insertions(+) create mode 100644 feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt create mode 100644 feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt new file mode 100644 index 00000000..803ec50c --- /dev/null +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt @@ -0,0 +1,132 @@ +package com.susu.feature.statistics.content.susu + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.susu.core.designsystem.theme.Blue60 +import com.susu.core.designsystem.theme.Gray10 +import com.susu.core.designsystem.theme.Gray100 +import com.susu.core.designsystem.theme.Gray40 +import com.susu.core.designsystem.theme.SusuTheme +import com.susu.feature.statistics.R +import com.susu.feature.statistics.component.RecentSpentGraph +import com.susu.feature.statistics.component.StatisticsHorizontalItem +import com.susu.feature.statistics.component.StatisticsVerticalItem + +@Composable +fun SusuStatisticsRoute() { + SusuStatisticsScreen() +} + +@Composable +fun SusuStatisticsScreen( + modifier: Modifier = Modifier, + isBlind: Boolean = true, +) { + Box(modifier = modifier.fillMaxSize()) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), + ) { + SusuStatisticsOptionSlot() + StatisticsHorizontalItem( + title = "관계 별 퍙균 수수", + name = "", + money = 0, + isActive = !isBlind, + ) + StatisticsHorizontalItem( + title = "경조사 카테고리 별 평균 수수", + name = "", + money = 0, + isActive = !isBlind, + ) + + RecentSpentGraph( + isActive = !isBlind, + graphTitle = "올해 쓴 금액", + ) + Row( + modifier = Modifier + .fillMaxWidth() + .background(color = Gray10, shape = RoundedCornerShape(4.dp)) + .padding(SusuTheme.spacing.spacing_m), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + Text(text = stringResource(R.string.statistics_most_spent_month), style = SusuTheme.typography.title_xs, color = Gray100) + if (isBlind) { + Text( + text = stringResource(R.string.word_month_format, stringResource(id = R.string.word_unknown)), + style = SusuTheme.typography.title_xs, + color = Gray40, + ) + } else { + Text( + text = stringResource(R.string.word_month_format, ""), + style = SusuTheme.typography.title_xs, + color = Blue60, + ) + } + } + Row( + modifier = Modifier.fillMaxWidth(), + ) { + StatisticsVerticalItem( + modifier = Modifier.weight(1f), + title = stringResource(R.string.statistics_most_susu_relationship), + content = "", + count = 0, + isActive = !isBlind, + ) + Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxs)) + StatisticsVerticalItem( + modifier = Modifier.weight(1f), + title = stringResource(R.string.statistics_most_susu_event), + content = "", + count = 0, + isActive = !isBlind, + ) + } + } + +// if (uiState.isLoading) { +// LoadingScreen( +// modifier = Modifier.align(Alignment.Center), +// ) +// } + } +} + +@Preview(showBackground = true) +@Composable +fun SusuStatisticsScreenPreview() { + SusuTheme { + SusuStatisticsScreen( + isBlind = false, + ) + } +} + +@Preview(showBackground = true) +@Composable +fun SusuStatisticsScreenBlindPreview() { + SusuTheme { + SusuStatisticsScreen( + isBlind = true, + ) + } +} diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt new file mode 100644 index 00000000..41bafd98 --- /dev/null +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt @@ -0,0 +1,19 @@ +package com.susu.feature.statistics.content.susu + +import com.susu.core.ui.base.SideEffect +import com.susu.core.ui.base.UiState + +sealed interface SusuStatisticsEffect : SideEffect + +data class SusuStatisticsState( + val isLoading: Boolean = false, + val age: StatisticsAge = StatisticsAge.THIRTY, + val relationshipId: Int = 0, + val categoryId: Int = 0, + val isRelationshipSheetOpen: Boolean = false, + val isCategorySheetOpen: Boolean = false, +) : UiState + +enum class StatisticsAge { + TEN, TWENTY, THIRTY, FOURTY, FIFTY, SIXTY, SEVENTY, EIGHTY, NINETY +} From 258670c84732c862a389ff7638148fbe286fbf1b Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 26 Jan 2024 14:29:13 +0900 Subject: [PATCH 52/95] =?UTF-8?q?chore:=20=EC=88=98=EC=88=98=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EC=98=B5=EC=85=98=20=EC=84=A0=ED=83=9D=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/susu/SusuStatisticsOptionSlot.kt | 21 ++++++++++--------- .../src/main/res/values/strings.xml | 3 +++ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt index 5c9ca942..6d7f0b7e 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt @@ -1,7 +1,6 @@ package com.susu.feature.statistics.content.susu import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -54,28 +53,30 @@ fun SusuStatisticsOptionSlot( Spacer(modifier = Modifier.height(SusuTheme.spacing.spacing_xxs)) Column( modifier = Modifier.fillMaxWidth().background(color = Orange10, shape = RoundedCornerShape(4.dp)) - .padding(horizontal = 12.dp, vertical = 8.dp), - verticalArrangement = Arrangement.Bottom, + .padding(horizontal = SusuTheme.spacing.spacing_s, vertical = SusuTheme.spacing.spacing_xxs), ) { - Row { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { OptionSlot(text = age, onClick = onAgeClick) - Spacer(modifier = Modifier.width(4.dp)) + Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxxxs)) Text( text = stringResource(R.string.word_statistics_is), style = SusuTheme.typography.title_xxs, color = Gray80, ) + Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_m)) OptionSlot(text = relationship, onClick = onRelationshipClick) - Spacer(modifier = Modifier.width(4.dp)) + Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxxxs)) OptionSlot(text = category, onClick = onCategoryClick) - Spacer(modifier = Modifier.width(4.dp)) + Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxxxs)) Text( text = stringResource(R.string.word_statistics_to), style = SusuTheme.typography.title_xxs, color = Gray80, ) } - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(SusuTheme.spacing.spacing_xxs)) Row( verticalAlignment = Alignment.Bottom, ) { @@ -84,7 +85,7 @@ fun SusuStatisticsOptionSlot( style = SusuTheme.typography.title_s, color = Orange60, ) - Spacer(modifier = Modifier.width(8.dp)) + Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxs)) Text( text = stringResource(R.string.word_statistics_send), style = SusuTheme.typography.title_xxs, @@ -115,7 +116,7 @@ fun OptionSlot( Icon( painter = painterResource(id = R.drawable.ic_statistics_arrow_down), tint = Orange60, - contentDescription = "옵션 선택", + contentDescription = stringResource(R.string.word_select_option), ) } } diff --git a/feature/statistics/src/main/res/values/strings.xml b/feature/statistics/src/main/res/values/strings.xml index 58dd510f..73f6c834 100644 --- a/feature/statistics/src/main/res/values/strings.xml +++ b/feature/statistics/src/main/res/values/strings.xml @@ -19,4 +19,7 @@ 을 보내고 있어요 + + %d대 + 옵션 선택 From a7e6b9f1c6a84b58f178810db0a3fb8e32cd7d78 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 26 Jan 2024 14:29:55 +0900 Subject: [PATCH 53/95] =?UTF-8?q?feat:=20=EC=88=98=EC=88=98=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EC=98=B5=EC=85=98=20(=EB=B4=89=ED=88=AC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20config)=20=EB=B6=88=EB=9F=AC=EC=98=A4?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/StatisticsRepositoryImpl.kt | 5 +++++ .../susu/data/remote/api/StatisticsService.kt | 4 ++++ .../model/response/EnvelopeConfigResponse.kt | 15 +++++++++++++++ .../response/RelationshipConfigResponse.kt | 17 +++++++++++++++++ .../domain/repository/StatisticsRepository.kt | 2 ++ .../statistics/GetStatisticsOptionUseCase.kt | 13 +++++++++++++ 6 files changed, 56 insertions(+) create mode 100644 data/src/main/java/com/susu/data/remote/model/response/EnvelopeConfigResponse.kt create mode 100644 data/src/main/java/com/susu/data/remote/model/response/RelationshipConfigResponse.kt create mode 100644 domain/src/main/java/com/susu/domain/usecase/statistics/GetStatisticsOptionUseCase.kt diff --git a/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt index deae1020..58bec544 100644 --- a/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt @@ -2,6 +2,7 @@ package com.susu.data.data.repository import com.susu.core.model.MyStatistics import com.susu.core.model.StatisticsElement +import com.susu.core.model.StatisticsOption import com.susu.data.remote.api.StatisticsService import com.susu.data.remote.model.response.toModel import com.susu.domain.repository.StatisticsRepository @@ -26,4 +27,8 @@ class StatisticsRepositoryImpl @Inject constructor( recentMaximumSpent = originalStatistic.recentMaximumSpent, ) } + + override suspend fun getStatisticOptionConfig(): StatisticsOption { + return statisticsService.getEnvelopeConfig().getOrThrow().toModel() + } } diff --git a/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt b/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt index 9f8a0932..8f9b35a9 100644 --- a/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt +++ b/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt @@ -1,5 +1,6 @@ package com.susu.data.remote.api +import com.susu.data.remote.model.response.EnvelopeConfigResponse import com.susu.data.remote.model.response.MyStatisticsResponse import com.susu.data.remote.retrofit.ApiResult import retrofit2.http.GET @@ -7,4 +8,7 @@ import retrofit2.http.GET interface StatisticsService { @GET("statistics/mine") suspend fun getMyStatistics(): ApiResult + + @GET("envelopes/configs/create-envelopes") + suspend fun getEnvelopeConfig(): ApiResult // TODO: 봉투 생성 작업 시 삭제 } diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeConfigResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeConfigResponse.kt new file mode 100644 index 00000000..a6946f07 --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeConfigResponse.kt @@ -0,0 +1,15 @@ +package com.susu.data.remote.model.response + +import com.susu.core.model.StatisticsOption +import kotlinx.serialization.Serializable + +@Serializable +data class EnvelopeConfigResponse( + val categories: List, + val relationships: List, +) + +fun EnvelopeConfigResponse.toModel() = StatisticsOption( + categories = categories.map { it.toModel() }, + relationships = relationships.map { it.toModel() }, +) diff --git a/data/src/main/java/com/susu/data/remote/model/response/RelationshipConfigResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/RelationshipConfigResponse.kt new file mode 100644 index 00000000..4f9a7bfa --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/RelationshipConfigResponse.kt @@ -0,0 +1,17 @@ +package com.susu.data.remote.model.response + +import com.susu.core.model.Relationship +import kotlinx.serialization.Serializable + +@Serializable +data class RelationshipConfigResponse( + val id: Int, + val relation: String, + val description: String = "", +) + +fun RelationshipConfigResponse.toModel() = Relationship( + id = id, + relation = relation, + description = description, +) diff --git a/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt b/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt index 5806e125..e0614491 100644 --- a/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt @@ -1,7 +1,9 @@ package com.susu.domain.repository import com.susu.core.model.MyStatistics +import com.susu.core.model.StatisticsOption interface StatisticsRepository { suspend fun getMyStatistics(): MyStatistics + suspend fun getStatisticOptionConfig(): StatisticsOption } diff --git a/domain/src/main/java/com/susu/domain/usecase/statistics/GetStatisticsOptionUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/statistics/GetStatisticsOptionUseCase.kt new file mode 100644 index 00000000..4402b336 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/statistics/GetStatisticsOptionUseCase.kt @@ -0,0 +1,13 @@ +package com.susu.domain.usecase.statistics + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.StatisticsRepository +import javax.inject.Inject + +class GetStatisticsOptionUseCase @Inject constructor( + private val statisticsRepository: StatisticsRepository, +) { + suspend operator fun invoke() = runCatchingIgnoreCancelled { + statisticsRepository.getStatisticOptionConfig() + } +} From 72b229a6a844bd52a87fe3b672a9c2288230e4e9 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 26 Jan 2024 14:30:22 +0900 Subject: [PATCH 54/95] =?UTF-8?q?feat:=20=EB=B6=88=EB=9F=AC=EC=98=A8=20?= =?UTF-8?q?=EC=88=98=EC=88=98=20=ED=86=B5=EA=B3=84=20=EC=98=B5=EC=85=98?= =?UTF-8?q?=EC=9D=84=20StatisticOptionSlot=EC=9C=BC=EB=A1=9C=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/core/model/StatisticsOption.kt | 9 ++ .../feature/statistics/StatisticsScreen.kt | 7 +- .../content/susu/SusuStatisticsContent.kt | 110 ++++++++++++++++-- .../content/susu/SusuStatisticsContract.kt | 11 +- .../content/susu/SusuStatisticsViewModel.kt | 37 ++++++ 5 files changed, 161 insertions(+), 13 deletions(-) create mode 100644 core/model/src/main/java/com/susu/core/model/StatisticsOption.kt create mode 100644 feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt diff --git a/core/model/src/main/java/com/susu/core/model/StatisticsOption.kt b/core/model/src/main/java/com/susu/core/model/StatisticsOption.kt new file mode 100644 index 00000000..584c30e9 --- /dev/null +++ b/core/model/src/main/java/com/susu/core/model/StatisticsOption.kt @@ -0,0 +1,9 @@ +package com.susu.core.model + +import androidx.compose.runtime.Stable + +@Stable +data class StatisticsOption( + val categories: List = emptyList(), + val relationships: List = emptyList(), +) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt index 45e444c6..dbc725ab 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt @@ -27,6 +27,7 @@ import com.susu.core.ui.DialogToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.feature.statistics.component.StatisticsTab import com.susu.feature.statistics.content.my.MyStatisticsRoute +import com.susu.feature.statistics.content.susu.SusuStatisticsRoute @Composable fun StatisticsRoute( @@ -96,7 +97,11 @@ fun StatisticsScreen( handleException = handleException, ) - StatisticsTab.AVERAGE -> {} + StatisticsTab.AVERAGE -> SusuStatisticsRoute( + isBlind = uiState.isBlind, + modifier = Modifier.fillMaxSize(), + handleException = handleException, + ) } } diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt index 803ec50c..22d438e4 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt @@ -11,40 +11,105 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text 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 +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.susu.core.designsystem.component.bottomsheet.SusuSelectionBottomSheet +import com.susu.core.designsystem.component.screen.LoadingScreen import com.susu.core.designsystem.theme.Blue60 import com.susu.core.designsystem.theme.Gray10 import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray40 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.ui.extension.collectWithLifecycle import com.susu.feature.statistics.R import com.susu.feature.statistics.component.RecentSpentGraph import com.susu.feature.statistics.component.StatisticsHorizontalItem import com.susu.feature.statistics.component.StatisticsVerticalItem +import kotlinx.collections.immutable.toImmutableList @Composable -fun SusuStatisticsRoute() { - SusuStatisticsScreen() +fun SusuStatisticsRoute( + isBlind: Boolean, + modifier: Modifier = Modifier, + viewModel: SusuStatisticsViewModel = hiltViewModel(), + handleException: (Throwable, () -> Unit) -> Unit, +) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + viewModel.sideEffect.collectWithLifecycle { sideEffect -> + when (sideEffect) { + is SusuStatisticsEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) + } + } + + LaunchedEffect(key1 = Unit) { + viewModel.getStatisticsOption() + } + + SusuStatisticsScreen( + uiState = uiState, + isBlind = isBlind, + modifier = modifier, + onClickAge = viewModel::showAgeSheet, + onClickRelationship = viewModel::showRelationshipSheet, + onClickCategory = viewModel::showCategorySheet, + onSelectAge = viewModel::selectAge, + onSelectCategory = viewModel::selectCategory, + onSelectRelationship = viewModel::selectRelationship, + onDismissAge = viewModel::hideAgeSheet, + onDismissCategory = viewModel::hideCategorySheet, + onDismissRelationship = viewModel::hideRelationshipSheet, + ) } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SusuStatisticsScreen( modifier: Modifier = Modifier, + uiState: SusuStatisticsState = SusuStatisticsState(), isBlind: Boolean = true, + onClickAge: () -> Unit = {}, + onClickRelationship: () -> Unit = {}, + onClickCategory: () -> Unit = {}, + onDismissAge: () -> Unit = {}, + onDismissRelationship: () -> Unit = {}, + onDismissCategory: () -> Unit = {}, + onSelectAge: (StatisticsAge) -> Unit = {}, + onSelectRelationship: (Int) -> Unit = {}, + onSelectCategory: (Int) -> Unit = {}, ) { + val context = LocalContext.current + val ageItems = remember { StatisticsAge.entries.map { context.getString(R.string.word_age_unit, it.num) }.toImmutableList() } + Box(modifier = modifier.fillMaxSize()) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { - SusuStatisticsOptionSlot() + SusuStatisticsOptionSlot( + age = stringResource(id = R.string.word_age_unit, uiState.age.num), + relationship = uiState.statisticsOption.relationships.getOrNull(uiState.relationshipId)?.relation ?: "", + category = uiState.statisticsOption.categories.getOrNull(uiState.categoryId)?.name ?: "", + onAgeClick = onClickAge, + onCategoryClick = onClickCategory, + onRelationshipClick = onClickRelationship, + ) StatisticsHorizontalItem( - title = "관계 별 퍙균 수수", + title = "관계 별 평균 수수", name = "", money = 0, isActive = !isBlind, @@ -103,11 +168,38 @@ fun SusuStatisticsScreen( } } -// if (uiState.isLoading) { -// LoadingScreen( -// modifier = Modifier.align(Alignment.Center), -// ) -// } + if (uiState.isAgeSheetOpen) { + SusuSelectionBottomSheet( + containerHeight = 322.dp, + items = ageItems, + selectedItemPosition = uiState.age.ordinal, + onClickItem = { onSelectAge(StatisticsAge.entries[it]) }, + ) + } + + if (uiState.isRelationshipSheetOpen) { + SusuSelectionBottomSheet( + containerHeight = 322.dp, + items = uiState.statisticsOption.relationships.map { it.relation }.toImmutableList(), + selectedItemPosition = uiState.relationshipId, + onClickItem = onSelectRelationship, + ) + } + + if (uiState.isCategorySheetOpen) { + SusuSelectionBottomSheet( + containerHeight = 322.dp, + items = uiState.statisticsOption.categories.map { it.name }.toImmutableList(), + selectedItemPosition = uiState.categoryId, + onClickItem = onSelectCategory, + ) + } + + if (uiState.isLoading) { + LoadingScreen( + modifier = Modifier.align(Alignment.Center), + ) + } } } diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt index 41bafd98..5d92c358 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt @@ -1,19 +1,24 @@ package com.susu.feature.statistics.content.susu +import com.susu.core.model.StatisticsOption import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState -sealed interface SusuStatisticsEffect : SideEffect +sealed interface SusuStatisticsEffect : SideEffect { + data class HandleException(val throwable: Throwable, val retry: () -> Unit) : SusuStatisticsEffect +} data class SusuStatisticsState( val isLoading: Boolean = false, val age: StatisticsAge = StatisticsAge.THIRTY, val relationshipId: Int = 0, val categoryId: Int = 0, + val statisticsOption: StatisticsOption = StatisticsOption(), + val isAgeSheetOpen: Boolean = false, val isRelationshipSheetOpen: Boolean = false, val isCategorySheetOpen: Boolean = false, ) : UiState -enum class StatisticsAge { - TEN, TWENTY, THIRTY, FOURTY, FIFTY, SIXTY, SEVENTY, EIGHTY, NINETY +enum class StatisticsAge(val num: Int) { + TEN(10), TWENTY(20), THIRTY(30), FOURTY(40), FIFTY(50), SIXTY(60), SEVENTY(70), EIGHTY(80), NINETY(90) } diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt new file mode 100644 index 00000000..50809671 --- /dev/null +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt @@ -0,0 +1,37 @@ +package com.susu.feature.statistics.content.susu + +import androidx.lifecycle.viewModelScope +import com.susu.core.ui.base.BaseViewModel +import com.susu.domain.usecase.statistics.GetStatisticsOptionUseCase +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class SusuStatisticsViewModel @Inject constructor( + private val getStatisticsOptionUseCase: GetStatisticsOptionUseCase, +) : BaseViewModel(SusuStatisticsState()) { + + fun getStatisticsOption() { + viewModelScope.launch { + intent { copy(isLoading = true) } + getStatisticsOptionUseCase().onSuccess { + intent { copy(statisticsOption = it) } + }.onFailure { + postSideEffect(SusuStatisticsEffect.HandleException(it, ::getStatisticsOption)) + } + intent { copy(isLoading = false) } + } + } + + fun selectAge(age: StatisticsAge) = intent { copy(age = age, isAgeSheetOpen = false) } + fun selectRelationship(id: Int) = intent { copy(relationshipId = id, isRelationshipSheetOpen = false) } + fun selectCategory(id: Int) = intent { copy(categoryId = id, isCategorySheetOpen = false) } + + fun showAgeSheet() = intent { copy(isAgeSheetOpen = true) } + fun showRelationshipSheet() = intent { copy(isRelationshipSheetOpen = true) } + fun showCategorySheet() = intent { copy(isCategorySheetOpen = true) } + fun hideAgeSheet() = intent { copy(isAgeSheetOpen = false) } + fun hideRelationshipSheet() = intent { copy(isRelationshipSheetOpen = false) } + fun hideCategorySheet() = intent { copy(isCategorySheetOpen = false) } +} From d554914412caac3c603d2c9839368f5ed68520d9 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 21:59:12 +0900 Subject: [PATCH 55/95] =?UTF-8?q?refactor:=20StatisticsOption=EC=9D=84=20C?= =?UTF-8?q?ategoryConfig=EC=99=80=20RelationshipConfig=EB=A1=9C=20?= =?UTF-8?q?=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/core/model/StatisticsOption.kt | 9 ------ .../repository/StatisticsRepositoryImpl.kt | 5 --- .../susu/data/remote/api/StatisticsService.kt | 4 --- .../model/response/EnvelopeConfigResponse.kt | 15 --------- .../response/RelationshipConfigResponse.kt | 17 ---------- .../domain/repository/StatisticsRepository.kt | 2 -- .../statistics/GetStatisticsOptionUseCase.kt | 13 -------- .../content/susu/SusuStatisticsContent.kt | 17 ++++++---- .../content/susu/SusuStatisticsContract.kt | 12 ++++--- .../content/susu/SusuStatisticsOptionSlot.kt | 2 +- .../content/susu/SusuStatisticsViewModel.kt | 32 +++++++++++++------ 11 files changed, 42 insertions(+), 86 deletions(-) delete mode 100644 core/model/src/main/java/com/susu/core/model/StatisticsOption.kt delete mode 100644 data/src/main/java/com/susu/data/remote/model/response/EnvelopeConfigResponse.kt delete mode 100644 data/src/main/java/com/susu/data/remote/model/response/RelationshipConfigResponse.kt delete mode 100644 domain/src/main/java/com/susu/domain/usecase/statistics/GetStatisticsOptionUseCase.kt diff --git a/core/model/src/main/java/com/susu/core/model/StatisticsOption.kt b/core/model/src/main/java/com/susu/core/model/StatisticsOption.kt deleted file mode 100644 index 584c30e9..00000000 --- a/core/model/src/main/java/com/susu/core/model/StatisticsOption.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.susu.core.model - -import androidx.compose.runtime.Stable - -@Stable -data class StatisticsOption( - val categories: List = emptyList(), - val relationships: List = emptyList(), -) diff --git a/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt index 58bec544..deae1020 100644 --- a/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt @@ -2,7 +2,6 @@ package com.susu.data.data.repository import com.susu.core.model.MyStatistics import com.susu.core.model.StatisticsElement -import com.susu.core.model.StatisticsOption import com.susu.data.remote.api.StatisticsService import com.susu.data.remote.model.response.toModel import com.susu.domain.repository.StatisticsRepository @@ -27,8 +26,4 @@ class StatisticsRepositoryImpl @Inject constructor( recentMaximumSpent = originalStatistic.recentMaximumSpent, ) } - - override suspend fun getStatisticOptionConfig(): StatisticsOption { - return statisticsService.getEnvelopeConfig().getOrThrow().toModel() - } } diff --git a/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt b/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt index 8f9b35a9..9f8a0932 100644 --- a/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt +++ b/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt @@ -1,6 +1,5 @@ package com.susu.data.remote.api -import com.susu.data.remote.model.response.EnvelopeConfigResponse import com.susu.data.remote.model.response.MyStatisticsResponse import com.susu.data.remote.retrofit.ApiResult import retrofit2.http.GET @@ -8,7 +7,4 @@ import retrofit2.http.GET interface StatisticsService { @GET("statistics/mine") suspend fun getMyStatistics(): ApiResult - - @GET("envelopes/configs/create-envelopes") - suspend fun getEnvelopeConfig(): ApiResult // TODO: 봉투 생성 작업 시 삭제 } diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeConfigResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeConfigResponse.kt deleted file mode 100644 index a6946f07..00000000 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeConfigResponse.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.susu.data.remote.model.response - -import com.susu.core.model.StatisticsOption -import kotlinx.serialization.Serializable - -@Serializable -data class EnvelopeConfigResponse( - val categories: List, - val relationships: List, -) - -fun EnvelopeConfigResponse.toModel() = StatisticsOption( - categories = categories.map { it.toModel() }, - relationships = relationships.map { it.toModel() }, -) diff --git a/data/src/main/java/com/susu/data/remote/model/response/RelationshipConfigResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/RelationshipConfigResponse.kt deleted file mode 100644 index 4f9a7bfa..00000000 --- a/data/src/main/java/com/susu/data/remote/model/response/RelationshipConfigResponse.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.susu.data.remote.model.response - -import com.susu.core.model.Relationship -import kotlinx.serialization.Serializable - -@Serializable -data class RelationshipConfigResponse( - val id: Int, - val relation: String, - val description: String = "", -) - -fun RelationshipConfigResponse.toModel() = Relationship( - id = id, - relation = relation, - description = description, -) diff --git a/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt b/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt index e0614491..5806e125 100644 --- a/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt @@ -1,9 +1,7 @@ package com.susu.domain.repository import com.susu.core.model.MyStatistics -import com.susu.core.model.StatisticsOption interface StatisticsRepository { suspend fun getMyStatistics(): MyStatistics - suspend fun getStatisticOptionConfig(): StatisticsOption } diff --git a/domain/src/main/java/com/susu/domain/usecase/statistics/GetStatisticsOptionUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/statistics/GetStatisticsOptionUseCase.kt deleted file mode 100644 index 4402b336..00000000 --- a/domain/src/main/java/com/susu/domain/usecase/statistics/GetStatisticsOptionUseCase.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.susu.domain.usecase.statistics - -import com.susu.core.common.runCatchingIgnoreCancelled -import com.susu.domain.repository.StatisticsRepository -import javax.inject.Inject - -class GetStatisticsOptionUseCase @Inject constructor( - private val statisticsRepository: StatisticsRepository, -) { - suspend operator fun invoke() = runCatchingIgnoreCancelled { - statisticsRepository.getStatisticOptionConfig() - } -} diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt index 22d438e4..8f2de62e 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt @@ -16,7 +16,6 @@ import androidx.compose.material3.Text 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 import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -101,9 +100,10 @@ fun SusuStatisticsScreen( verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { SusuStatisticsOptionSlot( + title = "지금 평균 수수 보기", age = stringResource(id = R.string.word_age_unit, uiState.age.num), - relationship = uiState.statisticsOption.relationships.getOrNull(uiState.relationshipId)?.relation ?: "", - category = uiState.statisticsOption.categories.getOrNull(uiState.categoryId)?.name ?: "", + relationship = uiState.relationship.relation, + category = uiState.category.name, onAgeClick = onClickAge, onCategoryClick = onClickCategory, onRelationshipClick = onClickRelationship, @@ -174,24 +174,27 @@ fun SusuStatisticsScreen( items = ageItems, selectedItemPosition = uiState.age.ordinal, onClickItem = { onSelectAge(StatisticsAge.entries[it]) }, + onDismissRequest = onDismissAge, ) } if (uiState.isRelationshipSheetOpen) { SusuSelectionBottomSheet( containerHeight = 322.dp, - items = uiState.statisticsOption.relationships.map { it.relation }.toImmutableList(), - selectedItemPosition = uiState.relationshipId, + items = uiState.relationshipConfig.map { it.relation }.toImmutableList(), + selectedItemPosition = uiState.relationshipConfig.indexOf(uiState.relationship), onClickItem = onSelectRelationship, + onDismissRequest = onDismissRelationship, ) } if (uiState.isCategorySheetOpen) { SusuSelectionBottomSheet( containerHeight = 322.dp, - items = uiState.statisticsOption.categories.map { it.name }.toImmutableList(), - selectedItemPosition = uiState.categoryId, + items = uiState.categoryConfig.map { it.name }.toImmutableList(), + selectedItemPosition = uiState.categoryConfig.indexOf(uiState.category), onClickItem = onSelectCategory, + onDismissRequest = onDismissCategory, ) } diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt index 5d92c358..4a500cd6 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt @@ -1,8 +1,11 @@ package com.susu.feature.statistics.content.susu -import com.susu.core.model.StatisticsOption +import com.susu.core.model.Category +import com.susu.core.model.Relationship import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf sealed interface SusuStatisticsEffect : SideEffect { data class HandleException(val throwable: Throwable, val retry: () -> Unit) : SusuStatisticsEffect @@ -11,9 +14,10 @@ sealed interface SusuStatisticsEffect : SideEffect { data class SusuStatisticsState( val isLoading: Boolean = false, val age: StatisticsAge = StatisticsAge.THIRTY, - val relationshipId: Int = 0, - val categoryId: Int = 0, - val statisticsOption: StatisticsOption = StatisticsOption(), + val relationship: Relationship = Relationship(), + val category: Category = Category(), + val categoryConfig: PersistentList = persistentListOf(), + val relationshipConfig: PersistentList = persistentListOf(), val isAgeSheetOpen: Boolean = false, val isRelationshipSheetOpen: Boolean = false, val isCategorySheetOpen: Boolean = false, diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt index 6d7f0b7e..5ae1a502 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt @@ -47,7 +47,7 @@ fun SusuStatisticsOptionSlot( ) { Text( text = title, - style = SusuTheme.typography.title_xs, + style = SusuTheme.typography.title_xxs, color = Gray50, ) Spacer(modifier = Modifier.height(SusuTheme.spacing.spacing_xxs)) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt index 50809671..e4fd3d97 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt @@ -1,33 +1,47 @@ package com.susu.feature.statistics.content.susu import androidx.lifecycle.viewModelScope +import com.susu.core.model.Category +import com.susu.core.model.Relationship import com.susu.core.ui.base.BaseViewModel -import com.susu.domain.usecase.statistics.GetStatisticsOptionUseCase +import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase +import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class SusuStatisticsViewModel @Inject constructor( - private val getStatisticsOptionUseCase: GetStatisticsOptionUseCase, + private val getCategoryConfigUseCase: GetCategoryConfigUseCase, + private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, ) : BaseViewModel(SusuStatisticsState()) { fun getStatisticsOption() { viewModelScope.launch { intent { copy(isLoading = true) } - getStatisticsOptionUseCase().onSuccess { - intent { copy(statisticsOption = it) } - }.onFailure { - postSideEffect(SusuStatisticsEffect.HandleException(it, ::getStatisticsOption)) + val categoryConfigResult = getCategoryConfigUseCase() + val relationshipConfigResult = getRelationShipConfigListUseCase() + + if (categoryConfigResult.isSuccess && relationshipConfigResult.isSuccess) { + val categoryConfig = categoryConfigResult.getOrDefault(emptyList()).toPersistentList() + val relationshipConfig = relationshipConfigResult.getOrDefault(emptyList()).toPersistentList() + intent { + copy( + categoryConfig = categoryConfig, + relationshipConfig = relationshipConfig, + category = categoryConfig.firstOrNull() ?: Category(), + relationship = relationshipConfig.firstOrNull() ?: Relationship(), + ) + } } intent { copy(isLoading = false) } } } fun selectAge(age: StatisticsAge) = intent { copy(age = age, isAgeSheetOpen = false) } - fun selectRelationship(id: Int) = intent { copy(relationshipId = id, isRelationshipSheetOpen = false) } - fun selectCategory(id: Int) = intent { copy(categoryId = id, isCategorySheetOpen = false) } - + fun selectRelationship(index: Int) = intent { copy(relationship = relationshipConfig[index], isRelationshipSheetOpen = false) } + fun selectCategory(index: Int) = intent { copy(category = categoryConfig[index], isCategorySheetOpen = false) } fun showAgeSheet() = intent { copy(isAgeSheetOpen = true) } fun showRelationshipSheet() = intent { copy(isRelationshipSheetOpen = true) } fun showCategorySheet() = intent { copy(isCategorySheetOpen = true) } From 28202a1380cf875f8b5aafb0a1997f72b4818b30 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 23:18:16 +0900 Subject: [PATCH 56/95] =?UTF-8?q?feat:=20=EC=88=98=EC=88=98=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20api=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/core/model/SusuStatistics.kt | 16 +++++ .../repository/StatisticsRepositoryImpl.kt | 23 +++++++ .../susu/data/remote/api/StatisticsService.kt | 9 +++ ...sticsResponse.kt => StatisticsResponse.kt} | 28 +++++++- .../domain/repository/StatisticsRepository.kt | 2 + .../statistics/GetSusuStatisticsUseCase.kt | 13 ++++ .../statistics/component/RecentSpentGraph.kt | 64 ++++++++++--------- .../content/susu/SusuStatisticsContent.kt | 27 +++++--- .../content/susu/SusuStatisticsContract.kt | 4 +- .../content/susu/SusuStatisticsViewModel.kt | 21 ++++++ 10 files changed, 166 insertions(+), 41 deletions(-) create mode 100644 core/model/src/main/java/com/susu/core/model/SusuStatistics.kt rename data/src/main/java/com/susu/data/remote/model/response/{MyStatisticsResponse.kt => StatisticsResponse.kt} (50%) create mode 100644 domain/src/main/java/com/susu/domain/usecase/statistics/GetSusuStatisticsUseCase.kt diff --git a/core/model/src/main/java/com/susu/core/model/SusuStatistics.kt b/core/model/src/main/java/com/susu/core/model/SusuStatistics.kt new file mode 100644 index 00000000..83851128 --- /dev/null +++ b/core/model/src/main/java/com/susu/core/model/SusuStatistics.kt @@ -0,0 +1,16 @@ +package com.susu.core.model + +import androidx.compose.runtime.Stable + +@Stable +data class SusuStatistics( + val averageSent: Int = 0, + val averageRelationship: StatisticsElement = StatisticsElement(), + val averageCategory: StatisticsElement = StatisticsElement(), + val recentSpent: List = emptyList(), + val recentTotalSpent: Int = 0, + val recentMaximumSpent: Int = 0, + val mostSpentMonth: Int = 0, + val mostRelationship: StatisticsElement = StatisticsElement(), + val mostCategory: StatisticsElement = StatisticsElement(), +) diff --git a/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt index deae1020..f84b693f 100644 --- a/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/StatisticsRepositoryImpl.kt @@ -2,6 +2,7 @@ package com.susu.data.data.repository import com.susu.core.model.MyStatistics import com.susu.core.model.StatisticsElement +import com.susu.core.model.SusuStatistics import com.susu.data.remote.api.StatisticsService import com.susu.data.remote.model.response.toModel import com.susu.domain.repository.StatisticsRepository @@ -26,4 +27,26 @@ class StatisticsRepositoryImpl @Inject constructor( recentMaximumSpent = originalStatistic.recentMaximumSpent, ) } + + override suspend fun getSusuStatistics(age: String, relationshipId: Int, categoryId: Int): SusuStatistics { + val originalStatistic = statisticsService.getSusuStatistics( + age = age, + relationshipId = relationshipId, + categoryId = categoryId, + ).getOrThrow().toModel() + val sortedRecentSpent = originalStatistic.recentSpent.sortedBy { it.title.toInt() } + .map { StatisticsElement(title = it.title.substring(it.title.length - 2).toInt().toString(), value = it.value) } + + return SusuStatistics( + averageSent = originalStatistic.averageSent, + averageRelationship = originalStatistic.averageRelationship, + averageCategory = originalStatistic.averageCategory, + recentSpent = sortedRecentSpent, + mostSpentMonth = originalStatistic.mostSpentMonth % 100, + mostRelationship = originalStatistic.mostRelationship, + mostCategory = originalStatistic.mostCategory, + recentTotalSpent = originalStatistic.recentTotalSpent, + recentMaximumSpent = originalStatistic.recentMaximumSpent, + ) + } } diff --git a/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt b/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt index 9f8a0932..a3ab5df7 100644 --- a/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt +++ b/data/src/main/java/com/susu/data/remote/api/StatisticsService.kt @@ -1,10 +1,19 @@ package com.susu.data.remote.api import com.susu.data.remote.model.response.MyStatisticsResponse +import com.susu.data.remote.model.response.SusuStatisticsResponse import com.susu.data.remote.retrofit.ApiResult import retrofit2.http.GET +import retrofit2.http.Query interface StatisticsService { @GET("statistics/mine") suspend fun getMyStatistics(): ApiResult + + @GET("statistics/susu") + suspend fun getSusuStatistics( + @Query("age") age: String, + @Query("relationshipId") relationshipId: Int, + @Query("categoryId") categoryId: Int, + ): ApiResult } diff --git a/data/src/main/java/com/susu/data/remote/model/response/MyStatisticsResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/StatisticsResponse.kt similarity index 50% rename from data/src/main/java/com/susu/data/remote/model/response/MyStatisticsResponse.kt rename to data/src/main/java/com/susu/data/remote/model/response/StatisticsResponse.kt index e9be8886..7483ff3e 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/MyStatisticsResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/StatisticsResponse.kt @@ -1,6 +1,7 @@ package com.susu.data.remote.model.response import com.susu.core.model.MyStatistics +import com.susu.core.model.SusuStatistics import kotlinx.serialization.Serializable @Serializable @@ -15,8 +16,19 @@ data class MyStatisticsResponse( @Serializable data class StatisticsElement( - val title: String, - val value: Int, + val title: String = "", + val value: Int = 0, +) + +@Serializable +data class SusuStatisticsResponse( + val averageSent: Int = 0, + val averageRelationship: StatisticsElement = StatisticsElement(), + val averageCategory: StatisticsElement = StatisticsElement(), + val recentSpent: List = emptyList(), + val mostSpentMonth: Int = 0, + val mostRelationship: StatisticsElement = StatisticsElement(), + val mostCategory: StatisticsElement = StatisticsElement(), ) fun MyStatisticsResponse.toModel() = MyStatistics( @@ -34,3 +46,15 @@ fun StatisticsElement.toModel() = com.susu.core.model.StatisticsElement( title = title, value = value, ) + +fun SusuStatisticsResponse.toModel() = SusuStatistics( + averageSent = averageSent, + averageRelationship = averageRelationship.toModel(), + averageCategory = averageCategory.toModel(), + recentSpent = recentSpent.map { it.toModel() }, + mostSpentMonth = mostSpentMonth, + mostRelationship = mostRelationship.toModel(), + mostCategory = mostCategory.toModel(), + recentMaximumSpent = recentSpent.maxOfOrNull { it.value } ?: 0, + recentTotalSpent = recentSpent.sumOf { it.value }, +) diff --git a/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt b/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt index 5806e125..ec7daf76 100644 --- a/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/StatisticsRepository.kt @@ -1,7 +1,9 @@ package com.susu.domain.repository import com.susu.core.model.MyStatistics +import com.susu.core.model.SusuStatistics interface StatisticsRepository { suspend fun getMyStatistics(): MyStatistics + suspend fun getSusuStatistics(age: String, relationshipId: Int, categoryId: Int): SusuStatistics } diff --git a/domain/src/main/java/com/susu/domain/usecase/statistics/GetSusuStatisticsUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/statistics/GetSusuStatisticsUseCase.kt new file mode 100644 index 00000000..620388a6 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/statistics/GetSusuStatisticsUseCase.kt @@ -0,0 +1,13 @@ +package com.susu.domain.usecase.statistics + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.StatisticsRepository +import javax.inject.Inject + +class GetSusuStatisticsUseCase @Inject constructor( + private val statisticsRepository: StatisticsRepository, +) { + suspend operator fun invoke(age: String, relationshipId: Int, categoryId: Int) = runCatchingIgnoreCancelled { + statisticsRepository.getSusuStatistics(age, relationshipId, categoryId) + } +} diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt index 6ca70edc..68157f3b 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -108,38 +109,43 @@ fun RecentSpentGraph( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically, ) { - spentData.forEachIndexed { i, data -> - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - StickGraph( - ratio = data.value.toFloat() / maximumAmount, - color = if (isActive) { - if (i == spentData.lastIndex) { - Orange60 + if (maximumAmount > 0) { + spentData.forEachIndexed { i, data -> + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + StickGraph( + ratio = data.value.toFloat() / maximumAmount, + color = if (isActive) { + if (i == spentData.lastIndex) { + Orange60 + } else { + Orange30 + } } else { - Orange30 - } - } else { - if (i == spentData.lastIndex) { - Gray60 + if (i == spentData.lastIndex) { + Gray60 + } else { + Gray30 + } + }, + ) + Spacer(modifier = Modifier.height(SusuTheme.spacing.spacing_xxxxs)) + Text( + text = stringResource(id = R.string.word_month_format, data.title), + style = SusuTheme.typography.title_xxxs, + color = if (i == spentData.lastIndex) { + Gray90 } else { - Gray30 - } - }, - ) - Spacer(modifier = Modifier.height(SusuTheme.spacing.spacing_xxxxs)) - Text( - text = stringResource(id = R.string.word_month_format, data.title), - style = SusuTheme.typography.title_xxxs, - color = if (i == spentData.lastIndex) { - Gray90 - } else { - Gray40 - }, - ) + Gray40 + }, + ) + } + Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_s)) } - Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_s)) + } else { + // TODO: 데이터가 없을 땐? + Spacer(modifier = Modifier.fillMaxSize()) } } } diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt index 8f2de62e..2e55f7e7 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt @@ -39,6 +39,7 @@ import com.susu.feature.statistics.component.RecentSpentGraph import com.susu.feature.statistics.component.StatisticsHorizontalItem import com.susu.feature.statistics.component.StatisticsVerticalItem import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toPersistentList @Composable fun SusuStatisticsRoute( @@ -59,6 +60,10 @@ fun SusuStatisticsRoute( viewModel.getStatisticsOption() } + LaunchedEffect(key1 = uiState.age, key2 = uiState.category, key3 = uiState.relationship) { + viewModel.getSusuStatistics() + } + SusuStatisticsScreen( uiState = uiState, isBlind = isBlind, @@ -102,6 +107,7 @@ fun SusuStatisticsScreen( SusuStatisticsOptionSlot( title = "지금 평균 수수 보기", age = stringResource(id = R.string.word_age_unit, uiState.age.num), + money = uiState.susuStatistics.averageSent, relationship = uiState.relationship.relation, category = uiState.category.name, onAgeClick = onClickAge, @@ -110,20 +116,23 @@ fun SusuStatisticsScreen( ) StatisticsHorizontalItem( title = "관계 별 평균 수수", - name = "", - money = 0, + name = uiState.susuStatistics.averageRelationship.title, + money = uiState.susuStatistics.averageRelationship.value, isActive = !isBlind, ) StatisticsHorizontalItem( title = "경조사 카테고리 별 평균 수수", - name = "", - money = 0, + name = uiState.susuStatistics.averageCategory.title, + money = uiState.susuStatistics.averageCategory.value, isActive = !isBlind, ) RecentSpentGraph( isActive = !isBlind, graphTitle = "올해 쓴 금액", + spentData = uiState.susuStatistics.recentSpent.toPersistentList(), + maximumAmount = uiState.susuStatistics.recentMaximumSpent, + totalAmount = uiState.susuStatistics.recentTotalSpent, ) Row( modifier = Modifier @@ -141,7 +150,7 @@ fun SusuStatisticsScreen( ) } else { Text( - text = stringResource(R.string.word_month_format, ""), + text = stringResource(R.string.word_month_format, uiState.susuStatistics.mostSpentMonth.toString()), style = SusuTheme.typography.title_xs, color = Blue60, ) @@ -153,16 +162,16 @@ fun SusuStatisticsScreen( StatisticsVerticalItem( modifier = Modifier.weight(1f), title = stringResource(R.string.statistics_most_susu_relationship), - content = "", - count = 0, + content = uiState.susuStatistics.mostRelationship.title, + count = uiState.susuStatistics.mostRelationship.value, isActive = !isBlind, ) Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxs)) StatisticsVerticalItem( modifier = Modifier.weight(1f), title = stringResource(R.string.statistics_most_susu_event), - content = "", - count = 0, + content = uiState.susuStatistics.mostCategory.title, + count = uiState.susuStatistics.mostCategory.value, isActive = !isBlind, ) } diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt index 4a500cd6..b1c78e20 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContract.kt @@ -2,6 +2,7 @@ package com.susu.feature.statistics.content.susu import com.susu.core.model.Category import com.susu.core.model.Relationship +import com.susu.core.model.SusuStatistics import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState import kotlinx.collections.immutable.PersistentList @@ -13,7 +14,7 @@ sealed interface SusuStatisticsEffect : SideEffect { data class SusuStatisticsState( val isLoading: Boolean = false, - val age: StatisticsAge = StatisticsAge.THIRTY, + val age: StatisticsAge = StatisticsAge.TWENTY, val relationship: Relationship = Relationship(), val category: Category = Category(), val categoryConfig: PersistentList = persistentListOf(), @@ -21,6 +22,7 @@ data class SusuStatisticsState( val isAgeSheetOpen: Boolean = false, val isRelationshipSheetOpen: Boolean = false, val isCategorySheetOpen: Boolean = false, + val susuStatistics: SusuStatistics = SusuStatistics(), ) : UiState enum class StatisticsAge(val num: Int) { diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt index e4fd3d97..4a50cf16 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt @@ -6,6 +6,7 @@ import com.susu.core.model.Relationship import com.susu.core.ui.base.BaseViewModel import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase +import com.susu.domain.usecase.statistics.GetSusuStatisticsUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch @@ -15,6 +16,7 @@ import javax.inject.Inject class SusuStatisticsViewModel @Inject constructor( private val getCategoryConfigUseCase: GetCategoryConfigUseCase, private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, + private val getSusuStatisticsUseCase: GetSusuStatisticsUseCase, ) : BaseViewModel(SusuStatisticsState()) { fun getStatisticsOption() { @@ -39,6 +41,25 @@ class SusuStatisticsViewModel @Inject constructor( } } + fun getSusuStatistics() { + if (currentState.relationship !in currentState.relationshipConfig && + currentState.category !in currentState.categoryConfig + ) return + + viewModelScope.launch { + getSusuStatisticsUseCase( + age = currentState.age.name, + relationshipId = currentState.relationship.id.toInt(), + categoryId = currentState.category.id, + ).onSuccess { + println(it) + intent { copy(susuStatistics = it) } + }.onFailure { + postSideEffect(SusuStatisticsEffect.HandleException(it, ::getSusuStatistics)) + } + } + } + fun selectAge(age: StatisticsAge) = intent { copy(age = age, isAgeSheetOpen = false) } fun selectRelationship(index: Int) = intent { copy(relationship = relationshipConfig[index], isRelationshipSheetOpen = false) } fun selectCategory(index: Int) = intent { copy(category = categoryConfig[index], isCategorySheetOpen = false) } From befc99a963acee4b5c8fda96cb4df2e88b7536c1 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 00:21:06 +0900 Subject: [PATCH 57/95] =?UTF-8?q?fix:=20=ED=86=B5=EA=B3=84=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=20=EC=9E=98=EB=AA=BB=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=EB=90=9C=20=ED=8C=A8=EB=94=A9,=20=EC=8A=A4=ED=81=AC=EB=A1=A4?= =?UTF-8?q?=20=EA=B3=A0=EC=B9=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/susu/feature/navigator/MainScreen.kt | 1 + .../java/com/susu/feature/statistics/StatisticsScreen.kt | 9 +++++---- .../feature/statistics/content/my/MyStatisticsContent.kt | 4 +++- .../statistics/content/susu/SusuStatisticsContent.kt | 5 +++-- .../statistics/navigation/StatisticsNavigation.kt | 3 +++ 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt index 29b30a9b..58c06331 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt @@ -139,6 +139,7 @@ internal fun MainScreen( ) statisticsNavGraph( + padding = innerPadding, navigateToMyInfo = navigator::navigateMyPageInfo, onShowDialog = viewModel::onShowDialog, handleException = viewModel::handleException, diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt index dbc725ab..918da16f 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/StatisticsScreen.kt @@ -3,11 +3,10 @@ package com.susu.feature.statistics import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -31,6 +30,7 @@ import com.susu.feature.statistics.content.susu.SusuStatisticsRoute @Composable fun StatisticsRoute( + padding: PaddingValues, viewModel: StatisticsViewModel = hiltViewModel(), navigateToMyInfo: () -> Unit, onShowDialog: (DialogToken) -> Unit, @@ -59,6 +59,7 @@ fun StatisticsRoute( } StatisticsScreen( + padding = padding, uiState = uiState, onTabSelected = viewModel::selectStatisticsTab, handleException = handleException, @@ -67,13 +68,13 @@ fun StatisticsRoute( @Composable fun StatisticsScreen( + padding: PaddingValues = PaddingValues(), uiState: StatisticsState = StatisticsState(), onTabSelected: (StatisticsTab) -> Unit = {}, handleException: (Throwable, () -> Unit) -> Unit = { _, _ -> }, ) { Box( - modifier = Modifier.fillMaxSize() - .verticalScroll(rememberScrollState()), + modifier = Modifier.fillMaxSize().padding(padding), ) { Column( modifier = Modifier.fillMaxSize().padding(horizontal = SusuTheme.spacing.spacing_m), diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt index 017cc1dc..1316ec97 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt @@ -10,7 +10,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -68,7 +70,7 @@ fun MyStatisticsContent( ) { Box(modifier = modifier.fillMaxSize()) { Column( - modifier = Modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { RecentSpentGraph( diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt index 2e55f7e7..5f429688 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt @@ -10,14 +10,15 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -101,7 +102,7 @@ fun SusuStatisticsScreen( Box(modifier = modifier.fillMaxSize()) { Column( - modifier = Modifier.fillMaxSize(), + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { SusuStatisticsOptionSlot( diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/navigation/StatisticsNavigation.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/navigation/StatisticsNavigation.kt index 5a4f4afe..8548173d 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/navigation/StatisticsNavigation.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/navigation/StatisticsNavigation.kt @@ -1,5 +1,6 @@ package com.susu.feature.statistics.navigation +import androidx.compose.foundation.layout.PaddingValues import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions @@ -12,12 +13,14 @@ fun NavController.navigateStatistics(navOptions: NavOptions) { } fun NavGraphBuilder.statisticsNavGraph( + padding: PaddingValues, navigateToMyInfo: () -> Unit, onShowDialog: (DialogToken) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, ) { composable(route = StatisticsRoute.route) { StatisticsRoute( + padding = padding, navigateToMyInfo = navigateToMyInfo, onShowDialog = onShowDialog, handleException = handleException, From cb42da1ca0fe4aa6e36b93078dfc5c04d7664c4c Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 00:21:35 +0900 Subject: [PATCH 58/95] =?UTF-8?q?feat:=20=EC=88=AB=EC=9E=90=EA=B0=80=20?= =?UTF-8?q?=EB=B0=94=EB=80=8C=EB=8A=94=20=ED=9A=A8=EA=B3=BC=EA=B0=80=20?= =?UTF-8?q?=EC=9E=88=EB=8A=94=20Text=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/text/AnimatedCounterText.kt | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 core/designsystem/src/main/java/com/susu/core/designsystem/component/text/AnimatedCounterText.kt diff --git a/core/designsystem/src/main/java/com/susu/core/designsystem/component/text/AnimatedCounterText.kt b/core/designsystem/src/main/java/com/susu/core/designsystem/component/text/AnimatedCounterText.kt new file mode 100644 index 00000000..fe5b4fb3 --- /dev/null +++ b/core/designsystem/src/main/java/com/susu/core/designsystem/component/text/AnimatedCounterText.kt @@ -0,0 +1,98 @@ +package com.susu.core.designsystem.component.text + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.tooling.preview.Preview +import com.susu.core.designsystem.theme.Gray100 +import com.susu.core.designsystem.theme.SusuTheme +import java.text.DecimalFormat + +private val upward = slideInVertically { it } togetherWith slideOutVertically { -it } +private val downward = slideInVertically { -it } togetherWith slideOutVertically { it } + +@Composable +fun AnimatedCounterText( + number: Int, + modifier: Modifier = Modifier, + prefix: String? = null, + postfix: String? = null, + style: TextStyle = SusuTheme.typography.title_xs, + color: Color = Gray100, +) { + val moneyFormat = remember { DecimalFormat("#,###") } + val currentString = moneyFormat.format(number) + + Row( + modifier = modifier, + horizontalArrangement = Arrangement.End, + ) { + prefix?.let { + Text( + text = it, + style = style, + color = color, + ) + } + for (i in currentString.indices) { + val char = currentString[i] + + AnimatedContent( + targetState = char, + transitionSpec = { + when { + initialState.isDigit() && targetState.isDigit() && + initialState.digitToInt() < targetState.digitToInt() -> upward + + initialState.isDigit() && targetState.isDigit() && + initialState.digitToInt() > targetState.digitToInt() -> downward + + else -> upward + } + }, + label = "animatedCounterChar$i", + ) { + Text( + text = it.toString(), + style = style, + color = color, + ) + } + } + postfix?.let { + Text( + text = it, + style = style, + color = color, + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun AnimatedCounterTextPreview() { + SusuTheme { + var number by remember { mutableStateOf(10000) } + Column { + Button(onClick = { number += 6000 }) { + Text(text = "증가") + } + AnimatedCounterText(number = number) + } + } +} From 7c86191d9f36fd99a612f289d36a36c0f52c0909 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 01:07:34 +0900 Subject: [PATCH 59/95] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A1=9C=EA=B7=B8=20=EC=B6=9C=EB=A0=A5=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/statistics/content/susu/SusuStatisticsViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt index 4a50cf16..a0d0c09d 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt @@ -52,7 +52,6 @@ class SusuStatisticsViewModel @Inject constructor( relationshipId = currentState.relationship.id.toInt(), categoryId = currentState.category.id, ).onSuccess { - println(it) intent { copy(susuStatistics = it) } }.onFailure { postSideEffect(SusuStatisticsEffect.HandleException(it, ::getSusuStatistics)) From e375e168d3aec2f0d601b0a42e889a6dad6372fe Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 01:07:55 +0900 Subject: [PATCH 60/95] =?UTF-8?q?feat:=20=ED=86=B5=EA=B3=84=EC=97=90=20?= =?UTF-8?q?=EC=88=AB=EC=9E=90=20=EB=B3=80=EA=B2=BD=20=EC=95=A0=EB=8B=88?= =?UTF-8?q?=EB=A9=94=EC=9D=B4=EC=85=98=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../susu/feature/statistics/component/RecentSpentGraph.kt | 7 +++++-- .../susu/feature/statistics/component/StatisticsItem.kt | 6 ++++-- .../statistics/content/susu/SusuStatisticsOptionSlot.kt | 7 ++++--- feature/statistics/src/main/res/values/strings.xml | 2 ++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt index 68157f3b..06f0d64a 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/component/RecentSpentGraph.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.susu.core.designsystem.component.text.AnimatedCounterText import com.susu.core.designsystem.theme.Blue60 import com.susu.core.designsystem.theme.Gray10 import com.susu.core.designsystem.theme.Gray100 @@ -81,10 +82,12 @@ fun RecentSpentGraph( color = Gray100, ) if (isActive) { - Text( - text = stringResource(R.string.statistics_total_man_format, totalAmount.toString()), + AnimatedCounterText( + number = totalAmount, style = SusuTheme.typography.title_xs, color = Blue60, + prefix = stringResource(id = R.string.statistics_total_man_prefix), + postfix = stringResource(id = R.string.statistics_total_man_postfix), ) } else { Text( diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/component/StatisticsItem.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/component/StatisticsItem.kt index 80a4495c..d516f0b7 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/component/StatisticsItem.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/component/StatisticsItem.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.susu.core.designsystem.component.text.AnimatedCounterText import com.susu.core.designsystem.theme.Gray10 import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray40 @@ -104,10 +105,11 @@ fun StatisticsHorizontalItem( ) { if (isActive) { Text(text = name, style = SusuTheme.typography.title_s, color = Gray80) - Text( - text = stringResource(id = com.susu.core.ui.R.string.money_unit_format, money.toMoneyFormat()), + AnimatedCounterText( + number = money, style = SusuTheme.typography.title_s, color = Gray100, + postfix = stringResource(id = com.susu.core.designsystem.R.string.money_unit), ) } else { Text(text = stringResource(id = R.string.word_unknown), style = SusuTheme.typography.title_s, color = Gray40) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt index 5ae1a502..05d9e12f 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsOptionSlot.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.susu.core.designsystem.component.text.AnimatedCounterText import com.susu.core.designsystem.theme.Gray10 import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Gray80 @@ -25,7 +26,6 @@ import com.susu.core.designsystem.theme.Orange10 import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.extension.susuClickable -import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.statistics.R @Composable @@ -80,10 +80,11 @@ fun SusuStatisticsOptionSlot( Row( verticalAlignment = Alignment.Bottom, ) { - Text( - text = stringResource(id = com.susu.core.ui.R.string.money_unit_format, money.toMoneyFormat()), + AnimatedCounterText( + number = money, style = SusuTheme.typography.title_s, color = Orange60, + postfix = stringResource(id = com.susu.core.designsystem.R.string.money_unit), ) Spacer(modifier = Modifier.width(SusuTheme.spacing.spacing_xxs)) Text( diff --git a/feature/statistics/src/main/res/values/strings.xml b/feature/statistics/src/main/res/values/strings.xml index 73f6c834..5f20a0d9 100644 --- a/feature/statistics/src/main/res/values/strings.xml +++ b/feature/statistics/src/main/res/values/strings.xml @@ -2,6 +2,8 @@ 최근 8개월 간 쓴 금액 총 %s만원 + + 만원 나의 수수 수수 평균 \? From 1aead52c07efd0deaedc0655c33f3f56eba36f5c Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 01:12:54 +0900 Subject: [PATCH 61/95] =?UTF-8?q?feat:=20=EC=88=98=EC=88=98=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20=EB=B3=80=ED=99=94=ED=95=98=EB=8A=94=20=ED=95=AD?= =?UTF-8?q?=EB=AA=A9=EC=97=90=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/component/StatisticsItem.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/component/StatisticsItem.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/component/StatisticsItem.kt index d516f0b7..ba18fd77 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/component/StatisticsItem.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/component/StatisticsItem.kt @@ -1,5 +1,9 @@ package com.susu.feature.statistics.component +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -25,7 +29,6 @@ import com.susu.core.designsystem.theme.Gray60 import com.susu.core.designsystem.theme.Gray80 import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme -import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.statistics.R @Composable @@ -104,7 +107,15 @@ fun StatisticsHorizontalItem( horizontalArrangement = Arrangement.SpaceBetween, ) { if (isActive) { - Text(text = name, style = SusuTheme.typography.title_s, color = Gray80) + AnimatedContent( + targetState = name, + transitionSpec = { + slideInVertically { -it } togetherWith slideOutVertically { it } + }, + label = "StatisticsHorizontalItemName", + ) { + Text(text = it, style = SusuTheme.typography.title_s, color = Gray80) + } AnimatedCounterText( number = money, style = SusuTheme.typography.title_s, From b43d2e032ca15bab4c0457b431266e05dfe52eb4 Mon Sep 17 00:00:00 2001 From: syb8200 Date: Fri, 2 Feb 2024 04:53:58 +0900 Subject: [PATCH 62/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B4=EC=9A=94=20?= =?UTF-8?q?=EB=B4=89=ED=88=AC=20=ED=99=94=EB=A9=B4=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?(NavDeepLink=20=EC=9D=B4=EC=8A=88)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/EnvelopesRepositoryImpl.kt | 4 +- .../susu/data/remote/api/EnvelopesService.kt | 2 +- .../domain/repository/EnvelopesRepository.kt | 2 +- .../envelope/GetEnvelopesListUseCase.kt | 2 +- .../susu/feature/navigator/MainNavigator.kt | 6 +-- .../feature/envelope/SentEnvelopeContract.kt | 7 +++ .../feature/envelope/SentEnvelopeScreen.kt | 27 +++++++--- .../feature/envelope/SentEnvelopeViewModel.kt | 53 ++++++++++++++++++- .../com/susu/feature/sent/SentContract.kt | 2 +- .../java/com/susu/feature/sent/SentScreen.kt | 7 +-- .../com/susu/feature/sent/SentViewModel.kt | 3 +- .../susu/feature/sent/component/SentCard.kt | 7 +-- .../feature/sent/component/SentHistoryCard.kt | 5 +- .../feature/sent/navigation/SentNavigation.kt | 19 +++++-- 14 files changed, 111 insertions(+), 35 deletions(-) diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index a195cd98..14a60b76 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -1,11 +1,9 @@ package com.susu.data.data.repository -import com.susu.core.model.Category import com.susu.core.model.FriendStatistics import com.susu.data.remote.api.EnvelopesService import com.susu.core.model.Envelope import com.susu.core.model.EnvelopeSearch -import com.susu.core.model.Friend import com.susu.core.model.Relationship import com.susu.data.remote.model.request.CategoryRequest import com.susu.data.remote.model.request.EnvelopeRequest @@ -17,7 +15,7 @@ class EnvelopesRepositoryImpl @Inject constructor( private val envelopesService: EnvelopesService, ) : EnvelopesRepository { override suspend fun getEnvelopesList( - friendIds: List?, + friendIds: List?, fromTotalAmounts: Int?, toTotalAmounts: Int?, page: Int?, diff --git a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt index 70bb7c5f..eb730fbf 100644 --- a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt +++ b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt @@ -14,7 +14,7 @@ import retrofit2.http.Query interface EnvelopesService { @GET("envelopes/friend-statistics") suspend fun getEnvelopesList( - @Query("friendIds") friendIds: List?, + @Query("friendIds") friendIds: List?, @Query("fromTotalAmounts") fromTotalAmounts: Int?, @Query("toTotalAmounts") toTotalAmounts: Int?, @Query("page") page: Int?, diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index 5ecb946e..b523afe9 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -10,7 +10,7 @@ import kotlinx.datetime.LocalDateTime interface EnvelopesRepository { suspend fun getEnvelopesList( - friendIds: List?, + friendIds: List?, fromTotalAmounts: Int?, toTotalAmounts: Int?, page: Int?, diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesListUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesListUseCase.kt index ae47170b..f9ce7911 100644 --- a/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesListUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopesListUseCase.kt @@ -21,7 +21,7 @@ class GetEnvelopesListUseCase @Inject constructor( } data class Param( - val friendIds: List? = emptyList(), + val friendIds: List? = emptyList(), val fromTotalAmounts: Int? = null, val toTotalAmounts: Int? = null, val page: Int? = null, diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt index c9dac278..fd68e085 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt @@ -58,7 +58,7 @@ internal class MainNavigator( ReceivedRoute.ledgerFilterRoute("{${ReceivedRoute.FILTER_ARGUMENT_NAME}}"), ReceivedRoute.envelopeDetailRoute, ReceivedRoute.envelopeEditRoute, - SentRoute.sentEnvelopeRoute, + SentRoute.sentEnvelopeRoute("${SentRoute.FRIEND_ID_ARGUMENT_NAME}"), SentRoute.sentEnvelopeDetailRoute, SentRoute.sentEnvelopeEditRoute, CommunityRoute.route, @@ -95,8 +95,8 @@ internal class MainNavigator( } } - fun navigateSentEnvelope() { - navController.navigateSentEnvelope() + fun navigateSentEnvelope(id: Long) { + navController.navigateSentEnvelope(id) } fun navigateSentEnvelopeDetail() { diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt index 6913cc1f..272fdb93 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt @@ -1,12 +1,19 @@ package com.susu.feature.envelope +import com.susu.core.model.EnvelopeSearch +import com.susu.core.model.FriendStatistics import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf data class SentEnvelopeState( val isLoading: Boolean = false, + val envelopeInfo: PersistentList = persistentListOf(), + val envelopeHistoryList: PersistentList = persistentListOf(), ) : UiState sealed interface SentEnvelopeSideEffect : SideEffect { + data object NavigateEnvelopeDetail : SentEnvelopeSideEffect data object PopBackStack : SentEnvelopeSideEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt index 76566496..396d131c 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt @@ -16,12 +16,14 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon import com.susu.core.designsystem.component.appbar.icon.NotificationIcon @@ -36,22 +38,31 @@ import com.susu.core.designsystem.theme.Gray90 import com.susu.core.designsystem.theme.Orange20 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.extension.collectWithLifecycle +import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.envelope.component.EnvelopeHistoryItem import com.susu.feature.sent.R @Composable fun SentEnvelopeRoute( viewModel: SentEnvelopeViewModel = hiltViewModel(), + friendId: Long, popBackStack: () -> Unit, navigateSentEnvelopeDetail: () -> Unit, ) { + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeSideEffect.PopBackStack -> popBackStack() + else -> {} } } + LaunchedEffect(key1 = Unit) { + viewModel.initData(friendId) + } + SentEnvelopeScreen( + uiState = uiState, onClickBackIcon = viewModel::popBackStack, onClickEnvelopeDetail = navigateSentEnvelopeDetail, ) @@ -59,6 +70,7 @@ fun SentEnvelopeRoute( @Composable fun SentEnvelopeScreen( + uiState: SentEnvelopeState = SentEnvelopeState(), modifier: Modifier = Modifier, onClickBackIcon: () -> Unit = {}, onClickSearchIcon: () -> Unit = {}, @@ -75,14 +87,13 @@ fun SentEnvelopeScreen( leftIcon = { BackIcon(onClickBackIcon) }, - title = "김철수", + title = uiState.envelopeInfo[0].friend.name, actions = { SearchIcon(onClickSearchIcon) NotificationIcon(onClickNotificationIcon) }, ) - // TODO: text 변경하기 Column( modifier = modifier .padding( @@ -91,14 +102,16 @@ fun SentEnvelopeScreen( ), ) { Text( - text = "전체 100,000원", + text = stringResource(R.string.sent_envelope_card_monee_total) + uiState.envelopeInfo[0].totalAmounts.toMoneyFormat() + + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_m, color = Gray100, ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_xxs)) SusuBadge( color = BadgeColor.Gray30, - text = "-40,000원", + text = "${uiState.envelopeInfo[0].sentAmounts.toFloat() + uiState.envelopeInfo[0].receivedAmounts}" + + stringResource(R.string.sent_envelope_card_money_won), padding = BadgeStyle.smallBadge, ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_xl)) @@ -118,7 +131,7 @@ fun SentEnvelopeScreen( ) } LinearProgressIndicator( - progress = { 0.7f }, + progress = { uiState.envelopeInfo[0].sentAmounts.toFloat() / uiState.envelopeInfo[0].totalAmounts }, color = SusuTheme.colorScheme.primary, trackColor = Orange20, strokeCap = StrokeCap.Round, @@ -131,12 +144,12 @@ fun SentEnvelopeScreen( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = "70,000원", + text = uiState.envelopeInfo[0].sentAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxxxs, color = Gray90, ) Text( - text = "30,000원", + text = uiState.envelopeInfo[0].receivedAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxxxs, color = Gray60, ) diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt index 85119a6a..cc5c7928 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt @@ -1,12 +1,63 @@ package com.susu.feature.envelope +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope import com.susu.core.ui.base.BaseViewModel +import com.susu.domain.usecase.envelope.GetEnvelopesHistoryListUseCase +import com.susu.domain.usecase.envelope.GetEnvelopesListUseCase +import com.susu.feature.sent.navigation.SentRoute import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class SentEnvelopeViewModel @Inject constructor() : BaseViewModel( +class SentEnvelopeViewModel @Inject constructor( + private val getEnvelopesListUseCase: GetEnvelopesListUseCase, + private val getEnvelopesHistoryListUseCase: GetEnvelopesHistoryListUseCase, + savedStateHandle: SavedStateHandle, +) : BaseViewModel( SentEnvelopeState(), ) { + // private val argument = savedStateHandle.get(SentRoute.FRIEND_ID_ARGUMENT_NAME)!! + + fun initData(id: Long) { + getEnvelopeInfo(id) + getEnvelopeDetailHistoryList(id) + } + + fun getEnvelopeInfo(id: Long) = viewModelScope.launch { + val friendsList: List = listOf(id) + + getEnvelopesListUseCase( + GetEnvelopesListUseCase.Param(friendIds = friendsList), + ).onSuccess { envelope -> + val envelopeInfo = currentState.envelopeInfo.plus(envelope).toPersistentList() + intent { + copy( + envelopeInfo = envelopeInfo + ) + } + } + } + + fun getEnvelopeDetailHistoryList(id: Long) = viewModelScope.launch { + val friendsList: List = listOf(id) + val includeList = listOf("CATEGORY") + + getEnvelopesHistoryListUseCase( + GetEnvelopesHistoryListUseCase.Param(friendIds = friendsList, include = includeList), + ).onSuccess { history -> + val envelopeHistoryList = history.toPersistentList() + intent { + copy( + envelopeHistoryList = envelopeHistoryList, + ) + } + } + } + + // TODO: 봉투 id 값 넘겨주기 + fun navigateSentEnvelopeDetail() = postSideEffect(SentEnvelopeSideEffect.NavigateEnvelopeDetail) fun popBackStack() = postSideEffect(SentEnvelopeSideEffect.PopBackStack) } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt index 9ef3232a..072b4608 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentContract.kt @@ -31,6 +31,6 @@ internal fun FriendStatistics.toState() = FriendStatisticsState( ) sealed interface SentEffect : SideEffect { - data object NavigateEnvelope : SentEffect + data class NavigateEnvelope(val id: Long) : SentEffect data object NavigateEnvelopeAdd : SentEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt index 592456ac..10c1a22d 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt @@ -45,7 +45,7 @@ import com.susu.feature.sent.component.SentCard fun SentRoute( viewModel: SentViewModel = hiltViewModel(), padding: PaddingValues, - navigateSentEnvelope: () -> Unit, + navigateSentEnvelope: (Long) -> Unit, navigateSentEnvelopeAdd: () -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value @@ -54,7 +54,8 @@ fun SentRoute( viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEffect.NavigateEnvelopeAdd -> navigateSentEnvelopeAdd() - is SentEffect.NavigateEnvelope -> navigateSentEnvelope() + is SentEffect.NavigateEnvelope -> navigateSentEnvelope(sideEffect.id) + else -> {} } } @@ -83,7 +84,7 @@ fun SentScreen( onClickSearchIcon: () -> Unit = {}, onClickNotificationIcon: () -> Unit = {}, onClickHistory: (Long) -> Unit = {}, - onClickHistoryShowAll: () -> Unit = {}, + onClickHistoryShowAll: (Long) -> Unit = {}, onClickAddEnvelope: () -> Unit = {}, ) { Box( diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt index 1408fcaa..853c7666 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt @@ -8,7 +8,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import javax.inject.Inject -import kotlin.system.exitProcess @HiltViewModel class SentViewModel @Inject constructor( @@ -56,6 +55,6 @@ class SentViewModel @Inject constructor( } } - fun navigateSentEnvelope() = postSideEffect(SentEffect.NavigateEnvelope) + fun navigateSentEnvelope(id: Long) = postSideEffect(SentEffect.NavigateEnvelope(id = id)) fun navigateSentAdd() = postSideEffect(SentEffect.NavigateEnvelopeAdd) } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt index eeff3946..14a78da7 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt @@ -23,9 +23,6 @@ import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -48,7 +45,6 @@ import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.sent.FriendStatisticsState import com.susu.feature.sent.R -import com.susu.feature.sent.SentState @Composable fun SentCard( @@ -59,7 +55,7 @@ fun SentCard( sentAmounts: Int = 0, receivedAmounts: Int = 0, onClickHistory: (Long) -> Unit = {}, - onClickHistoryShowAll: () -> Unit = {}, + onClickHistoryShowAll: (Long) -> Unit = {}, ) { val degrees by animateFloatAsState(if (uiState.expand) 180f else 0f, label = "") @@ -161,6 +157,7 @@ fun SentCard( ) { SentHistoryCard( envelopeHistoryList = uiState.envelopesHistoryList, + friendId = friend.id, onClickHistoryShowAll = onClickHistoryShowAll, ) } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt index 2bf09123..509d3450 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryCard.kt @@ -27,7 +27,8 @@ import kotlinx.collections.immutable.PersistentList fun SentHistoryCard( modifier: Modifier = Modifier, envelopeHistoryList: PersistentList, - onClickHistoryShowAll: () -> Unit = {}, + friendId: Long, + onClickHistoryShowAll: (Long) -> Unit = {}, ) { Card( modifier = modifier @@ -68,7 +69,7 @@ fun SentHistoryCard( text = stringResource(R.string.sent_screen_envelope_history_show_all), modifier = modifier .fillMaxWidth(), - onClick = onClickHistoryShowAll, + onClick = { onClickHistoryShowAll(friendId) }, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt index 564803c0..8d365d06 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt @@ -15,8 +15,9 @@ fun NavController.navigateSent(navOptions: NavOptions) { navigate(SentRoute.route, navOptions) } -fun NavController.navigateSentEnvelope() { - navigate(SentRoute.sentEnvelopeRoute) +// TODO: 수정? +fun NavController.navigateSentEnvelope(id: Long) { + navigate(SentRoute.sentEnvelopeRoute(id = id.toString())) } fun NavController.navigateSentEnvelopeDetail() { @@ -34,7 +35,7 @@ fun NavController.navigateSentEnvelopeAdd() { fun NavGraphBuilder.sentNavGraph( padding: PaddingValues, popBackStack: () -> Unit, - navigateSentEnvelope: () -> Unit, + navigateSentEnvelope: (Long) -> Unit, navigateSentEnvelopeDetail: () -> Unit, navigateSentEnvelopeEdit: () -> Unit, navigateSentEnvelopeAdd: () -> Unit, @@ -47,8 +48,13 @@ fun NavGraphBuilder.sentNavGraph( ) } - composable(route = SentRoute.sentEnvelopeRoute) { + // TODO: 수정 필요 + composable( + route = SentRoute.sentEnvelopeRoute("${SentRoute.FRIEND_ID_ARGUMENT_NAME}"), + ) { navBackStackEntry -> + val friendId = navBackStackEntry.savedStateHandle.get(SentRoute.FRIEND_ID_ARGUMENT_NAME) ?: -1 SentEnvelopeRoute( + friendId = friendId, popBackStack = popBackStack, navigateSentEnvelopeDetail = navigateSentEnvelopeDetail, ) @@ -77,8 +83,11 @@ fun NavGraphBuilder.sentNavGraph( object SentRoute { const val route = "sent" - const val sentEnvelopeRoute = "sent-envelope" const val sentEnvelopeDetailRoute = "sent-envelope-detail" const val sentEnvelopeEditRoute = "sent-envelope-edit" const val sentEnvelopeAddRoute = "sent-envelope-add" + + // TODO: 수정 필요 + fun sentEnvelopeRoute(id: String) = "sent-envelope/$id" + const val FRIEND_ID_ARGUMENT_NAME = "sent-envelope-id" } From 2bb8627df5869753ac8c02908083df7faf4f80a3 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Fri, 2 Feb 2024 09:01:07 +0900 Subject: [PATCH 63/95] =?UTF-8?q?feat:=20=EC=9E=90=EA=B8=B0=20=EC=9E=90?= =?UTF-8?q?=EC=8B=A0=EC=9D=84=20=EC=B0=A8=EB=8B=A8=ED=95=9C=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=EC=9D=98=20=EC=98=88=EC=99=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/model/exception/SusuExceptions.kt | 28 +++++++++++++++++-- .../community/community/CommunityContract.kt | 1 + .../community/community/CommunityScreen.kt | 4 +++ .../community/community/CommunityViewModel.kt | 6 +++- .../navigation/CommunityNavigation.kt | 1 + 5 files changed, 37 insertions(+), 3 deletions(-) diff --git a/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt b/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt index dbec590e..de3c300e 100644 --- a/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt +++ b/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt @@ -57,32 +57,40 @@ enum class SusuServerError(val exception: Exception) { /** Term Error Code */ NOT_FOUND_TERM_ERROR(NotFoundTermException()), - /** Vote History Error Code */ + /** Vote Error Code */ ALREADY_VOTED_POST(AlreadyVotedPostException()), + CANNOT_BLOCK_MYSELF(CannotBlockMyself()) } /** Common Exception Code */ class BadRequestException( override val message: String = "bad request", ) : RuntimeException() + class InvalidInputValueException( override val message: String = "input is invalid value", ) : RuntimeException() + class InvalidTypeValueException( override val message: String = "invalid type value", ) : RuntimeException() + class MethodNotAllowedException( override val message: String = "Method type is invalid", ) : RuntimeException() + class InvalidMediaTypeException( override val message: String = "invalid media type", ) : RuntimeException() + class QueryDslNotExistsException( override val message: String = "not found query dsl", ) : RuntimeException() + class CoroutineCancellationException( override val message: String = "coroutine cancellation Exception", ) : RuntimeException() + class NoAuthorityException( override val message: String = "수정 권한이 없습니다.", ) : RuntimeException() @@ -91,9 +99,11 @@ class NoAuthorityException( class FailToVerifyTokenException( override val message: String = "fail to verify token", ) : RuntimeException() + class NotAccessTokenException( override val message: String = "엑세스 토큰이 아닙니다.", ) : RuntimeException() + class NotRefreshTokenException( override val message: String = "리프레시 토큰이 아닙니다.", ) : RuntimeException() @@ -102,9 +112,11 @@ class NotRefreshTokenException( class UserNotFoundException( override val message: String = "유저 정보를 찾을 수 없습니다.", ) : RuntimeException() + class AlreadyRegisteredUserException( override val message: String = "이미 가입된 유저입니다.", ) : RuntimeException() + class FailToCreateUserException( override val message: String = "유저 생성을 실패했습니다.", ) : RuntimeException() @@ -113,9 +125,11 @@ class FailToCreateUserException( class LedgerInvalidDueDateException( override val message: String = "잘못된 일정 등록 요청입니다.", ) : RuntimeException() + class FailToCreateLedgerException( override val message: String = "장부 생성을 실패했습니다.", ) : RuntimeException() + class NotFoundLedgerException( override val message: String = "장부 정보가 없습니다.", ) : RuntimeException() @@ -143,6 +157,7 @@ class NotFoundFriendException( class AlreadyRegisteredFriendPhoneNumberException( override val message: String = "이미 등록된 전화번호 입니다.", ) : RuntimeException() + class FailToCreateFriendException( override val message: String = "친구 생성을 실패했습니다.", ) : RuntimeException() @@ -151,6 +166,7 @@ class FailToCreateFriendException( class FailToCreateEnvelopeException( override val message: String = "봉투 생성을 실패했습니다.", ) : RuntimeException() + class NotFoundEnvelopeException( override val message: String = "봉투 정보를 찾을 수 없습니다.", ) : RuntimeException() @@ -159,15 +175,19 @@ class NotFoundEnvelopeException( class NotFoundPostException( override val message: String = "게시글 정보를 찾을 수 없습니다.", ) : RuntimeException() + class InvalidVoteOptionSequenceException( override val message: String = "투표 옵션 순서가 잘못되었습니다.", ) : RuntimeException() + class FailToCreatePostException( override val message: String = "게시글 생성을 실패했습니다.", ) : RuntimeException() + class NotFoundVoteException( override val message: String = "투표 정보를 찾을 수 없습니다.", ) : RuntimeException() + class DuplicatedVoteException( override val message: String = "중복 투표를 할 수 없습니다.", ) : RuntimeException() @@ -182,7 +202,11 @@ class NotFoundTermException( override val message: String = "약관 정보를 찾을 수 없습니다.", ) : RuntimeException() -/** Vote History Exception Code */ +/** Vote Exception Code */ class AlreadyVotedPostException( override val message: String = "이미 진행된 투표입니다.", ) : RuntimeException() + +class CannotBlockMyself( + override val message: String = "본인을 차단할 수 없습니다.", +) : RuntimeException() diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt index a64e0f80..40afd3e0 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt @@ -19,6 +19,7 @@ data class CommunityState( sealed interface CommunitySideEffect : SideEffect { data class HandleException(val throwable: Throwable, val retry: () -> Unit) : CommunitySideEffect + data class ShowSnackbar(val message: String) : CommunitySideEffect data object NavigateVoteAdd : CommunitySideEffect data object NavigateVoteSearch : CommunitySideEffect data class NavigateVoteDetail(val voteId: Long) : CommunitySideEffect diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt index 36e885b7..47eb540f 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt @@ -54,6 +54,7 @@ import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.Category import com.susu.core.model.Vote import com.susu.core.ui.DialogToken +import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.OnBottomReached import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable @@ -75,6 +76,7 @@ fun CommunityRoute( navigateVoteDetail: (Long) -> Unit, onShowDialog: (DialogToken) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, + onShowSnackbar: (SnackbarToken) -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value val context = LocalContext.current @@ -95,6 +97,8 @@ fun CommunityRoute( onCheckedAction = sideEffect.onCheckedAction, ), ) + + is CommunitySideEffect.ShowSnackbar -> onShowSnackbar(SnackbarToken(message = sideEffect.message)) } } diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt index 08fe13f0..10dff79e 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt @@ -3,6 +3,7 @@ package com.susu.feature.community.community import androidx.lifecycle.viewModelScope import com.susu.core.model.Category import com.susu.core.model.Vote +import com.susu.core.model.exception.CannotBlockMyself import com.susu.core.model.exception.NotFoundLedgerException import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri @@ -208,7 +209,10 @@ class CommunityViewModel @Inject constructor( getVoteList(true) } .onFailure { throwable -> - postSideEffect(CommunitySideEffect.HandleException(throwable = throwable, retry = { blockUser(uid) })) + when (throwable) { + is CannotBlockMyself -> postSideEffect(CommunitySideEffect.ShowSnackbar(throwable.message)) + else -> postSideEffect(CommunitySideEffect.HandleException(throwable = throwable, retry = { blockUser(uid) })) + } } } } diff --git a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt index d5683c3a..41f38640 100644 --- a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt +++ b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt @@ -66,6 +66,7 @@ fun NavGraphBuilder.communityNavGraph( navigateVoteSearch = navigateVoteSearch, navigateVoteDetail = navigateVoteDetail, onShowDialog = onShowDialog, + onShowSnackbar = onShowSnackbar, handleException = handleException, ) } From 5507f86b7ca3e7ddb0e54397f8096353d54c0f7f Mon Sep 17 00:00:00 2001 From: jinukeu Date: Fri, 2 Feb 2024 09:17:52 +0900 Subject: [PATCH 64/95] =?UTF-8?q?feat:=20SentEnvelope=20Navigate=20?= =?UTF-8?q?=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../susu/feature/envelope/SentEnvelopeContract.kt | 2 +- .../com/susu/feature/envelope/SentEnvelopeScreen.kt | 3 +-- .../susu/feature/envelope/SentEnvelopeViewModel.kt | 8 ++++---- .../susu/feature/sent/navigation/SentNavigation.kt | 13 +++++++++---- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt index 272fdb93..10f04596 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.persistentListOf data class SentEnvelopeState( val isLoading: Boolean = false, - val envelopeInfo: PersistentList = persistentListOf(), + val envelopeInfo: PersistentList = persistentListOf(FriendStatistics()), val envelopeHistoryList: PersistentList = persistentListOf(), ) : UiState diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt index 396d131c..1aa1cd77 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt @@ -45,7 +45,6 @@ import com.susu.feature.sent.R @Composable fun SentEnvelopeRoute( viewModel: SentEnvelopeViewModel = hiltViewModel(), - friendId: Long, popBackStack: () -> Unit, navigateSentEnvelopeDetail: () -> Unit, ) { @@ -58,7 +57,7 @@ fun SentEnvelopeRoute( } LaunchedEffect(key1 = Unit) { - viewModel.initData(friendId) + viewModel.initData() } SentEnvelopeScreen( diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt index cc5c7928..b21e6c58 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt @@ -19,11 +19,11 @@ class SentEnvelopeViewModel @Inject constructor( ) : BaseViewModel( SentEnvelopeState(), ) { - // private val argument = savedStateHandle.get(SentRoute.FRIEND_ID_ARGUMENT_NAME)!! + private val friendId = savedStateHandle.get(SentRoute.FRIEND_ID_ARGUMENT_NAME)!! - fun initData(id: Long) { - getEnvelopeInfo(id) - getEnvelopeDetailHistoryList(id) + fun initData() { + getEnvelopeInfo(friendId) + getEnvelopeDetailHistoryList(friendId) } fun getEnvelopeInfo(id: Long) = viewModelScope.launch { diff --git a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt index 8d365d06..8d2c3c12 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt @@ -4,7 +4,9 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions +import androidx.navigation.NavType import androidx.navigation.compose.composable +import androidx.navigation.navArgument import com.susu.feature.envelope.SentEnvelopeRoute import com.susu.feature.envelopeadd.SentEnvelopeAddRoute import com.susu.feature.envelopedetail.SentEnvelopeDetailRoute @@ -50,11 +52,14 @@ fun NavGraphBuilder.sentNavGraph( // TODO: 수정 필요 composable( - route = SentRoute.sentEnvelopeRoute("${SentRoute.FRIEND_ID_ARGUMENT_NAME}"), - ) { navBackStackEntry -> - val friendId = navBackStackEntry.savedStateHandle.get(SentRoute.FRIEND_ID_ARGUMENT_NAME) ?: -1 + route = SentRoute.sentEnvelopeRoute("{${SentRoute.FRIEND_ID_ARGUMENT_NAME}}"), + arguments = listOf( + navArgument(SentRoute.FRIEND_ID_ARGUMENT_NAME) { + type = NavType.LongType + }, + ), + ) { SentEnvelopeRoute( - friendId = friendId, popBackStack = popBackStack, navigateSentEnvelopeDetail = navigateSentEnvelopeDetail, ) From 42dec0a0433cdf5ee62550d5df9b3c969171c83d Mon Sep 17 00:00:00 2001 From: jinukeu Date: Fri, 2 Feb 2024 09:38:55 +0900 Subject: [PATCH 65/95] =?UTF-8?q?feat:=20=ED=88=AC=ED=91=9C=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20-=20=EC=8B=A0=EA=B3=A0,=20=EC=B0=A8=EB=8B=A8=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../community/community/CommunityContract.kt | 2 +- .../community/community/CommunityScreen.kt | 4 ++- .../community/community/CommunityViewModel.kt | 9 +++-- .../navigation/CommunityNavigation.kt | 6 ++++ .../votedetail/VoteDetailContract.kt | 4 +++ .../community/votedetail/VoteDetailScreen.kt | 27 ++++++++++++--- .../votedetail/VoteDetailViewModel.kt | 34 +++++++++++++++++++ .../com/susu/feature/navigator/MainScreen.kt | 7 ++++ 8 files changed, 85 insertions(+), 8 deletions(-) diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt index 40afd3e0..ae9b1846 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityContract.kt @@ -23,5 +23,5 @@ sealed interface CommunitySideEffect : SideEffect { data object NavigateVoteAdd : CommunitySideEffect data object NavigateVoteSearch : CommunitySideEffect data class NavigateVoteDetail(val voteId: Long) : CommunitySideEffect - data class ShowDeleteDialog(val onConfirmRequest: () -> Unit, val onCheckedAction: () -> Unit) : CommunitySideEffect + data class ShowReportDialog(val onConfirmRequest: () -> Unit, val onCheckedAction: () -> Unit) : CommunitySideEffect } diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt index 47eb540f..fba56c4f 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityScreen.kt @@ -68,6 +68,7 @@ import java.time.LocalDateTime fun CommunityRoute( padding: PaddingValues, vote: String?, + needRefresh: Boolean, toDeleteVoteId: Long?, toUpdateVote: String?, viewModel: CommunityViewModel = hiltViewModel(), @@ -86,7 +87,7 @@ fun CommunityRoute( CommunitySideEffect.NavigateVoteAdd -> navigateVoteAdd() is CommunitySideEffect.NavigateVoteDetail -> navigateVoteDetail(sideEffect.voteId) CommunitySideEffect.NavigateVoteSearch -> navigateVoteSearch() - is CommunitySideEffect.ShowDeleteDialog -> onShowDialog( + is CommunitySideEffect.ShowReportDialog -> onShowDialog( DialogToken( title = context.getString(R.string.dialog_report_title), text = context.getString(R.string.dialog_report_body), @@ -122,6 +123,7 @@ fun CommunityRoute( viewModel.addVoteIfNeed(vote) viewModel.updateVoteIfNeed(toUpdateVote) viewModel.deleteVoteIfNeed(toDeleteVoteId) + viewModel.needRefreshIfNeed(needRefresh) } voteListState.OnBottomReached(minItemsCount = 4) { diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt index 10dff79e..8e02c43c 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.viewModelScope import com.susu.core.model.Category import com.susu.core.model.Vote import com.susu.core.model.exception.CannotBlockMyself -import com.susu.core.model.exception.NotFoundLedgerException import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri import com.susu.domain.usecase.block.BlockUserUseCase @@ -97,6 +96,12 @@ class CommunityViewModel @Inject constructor( } } + fun needRefreshIfNeed(needRefresh: Boolean) { + if (needRefresh.not()) return + + getVoteList(true) + } + fun initData() { if (isFirstVisit.not()) return getVoteList() @@ -190,7 +195,7 @@ class CommunityViewModel @Inject constructor( fun navigateVoteSearch() = postSideEffect(CommunitySideEffect.NavigateVoteSearch) fun showReportDialog(vote: Vote) = postSideEffect( - CommunitySideEffect.ShowDeleteDialog( + CommunitySideEffect.ShowReportDialog( onConfirmRequest = { reportVote(vote.id) }, onCheckedAction = { blockUser(vote.uid) }, ), diff --git a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt index 41f38640..f1412547 100644 --- a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt +++ b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt @@ -48,6 +48,7 @@ fun NavGraphBuilder.communityNavGraph( popBackStackWithVote: (String) -> Unit, popBackStackWithToUpdateVote: (String) -> Unit, popBackStackWithDeleteVoteId: (Long) -> Unit, + popBackStackWithNeedRefresh: (Boolean) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, onShowDialog: (DialogToken) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, @@ -56,10 +57,13 @@ fun NavGraphBuilder.communityNavGraph( val vote = navBackStackEntry.savedStateHandle.get(CommunityRoute.VOTE_ARGUMENT_NAME) val toUpdateVote = navBackStackEntry.savedStateHandle.get(CommunityRoute.TO_UPDATE_VOTE_ARGUMENT_NAME) val toDeleteVoteId = navBackStackEntry.savedStateHandle.get(CommunityRoute.VOTE_ID_ARGUMENT_NAME) + val needRefresh = navBackStackEntry.savedStateHandle.get(CommunityRoute.NEED_REFRESH_ARGUMENT_NAME) ?: false + navBackStackEntry.savedStateHandle[CommunityRoute.NEED_REFRESH_ARGUMENT_NAME] = false CommunityRoute( padding = padding, vote = vote, + needRefresh = needRefresh, toDeleteVoteId = toDeleteVoteId, toUpdateVote = toUpdateVote, navigateVoteAdd = navigateVoteAdd, @@ -92,6 +96,7 @@ fun NavGraphBuilder.communityNavGraph( VoteDetailRoute( popBackStackWithToUpdateVote = popBackStackWithToUpdateVote, popBackStackWithDeleteVoteId = popBackStackWithDeleteVoteId, + popBackStackWithNeedRefresh = popBackStackWithNeedRefresh, navigateVoteEdit = navigateVoteEdit, onShowDialog = onShowDialog, onShowSnackbar = onShowSnackbar, @@ -127,6 +132,7 @@ object CommunityRoute { const val VOTE_ARGUMENT_NAME = "vote" const val VOTE_ID_ARGUMENT_NAME = "vote-id" const val TO_UPDATE_VOTE_ARGUMENT_NAME = "to-update-vote" + const val NEED_REFRESH_ARGUMENT_NAME = "need-refresh" fun voteDetailRoute(voteId: String) = "vote-detail/$voteId" fun voteEditRoute(vote: String) = "vote-edit/$vote" diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt index 22c639d5..d082058b 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt @@ -3,6 +3,7 @@ package com.susu.feature.community.votedetail import com.susu.core.model.Vote import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState +import com.susu.feature.community.community.CommunitySideEffect data class VoteDetailState( val vote: Vote = Vote(), @@ -10,8 +11,11 @@ data class VoteDetailState( ) : UiState sealed interface VoteDetailSideEffect : SideEffect { + data class ShowSnackbar(val message: String) : VoteDetailSideEffect data class PopBackStackWithDeleteVoteId(val voteId: Long) : VoteDetailSideEffect + data object PopBackStackWithNeedRefresh : VoteDetailSideEffect data class ShowDeleteDialog(val onConfirmRequest: () -> Unit) : VoteDetailSideEffect + data class ShowReportDialog(val onConfirmRequest: () -> Unit, val onCheckedAction: () -> Unit) : VoteDetailSideEffect data object ShowDeleteSuccessSnackbar : VoteDetailSideEffect data class PopBackStackWithToUpdateVote(val vote: String) : VoteDetailSideEffect data class NavigateVoteEdit(val vote: Vote) : VoteDetailSideEffect diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt index 09b915c9..5d24c821 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt @@ -65,6 +65,7 @@ fun VoteDetailRoute( viewModel: VoteDetailViewModel = hiltViewModel(), popBackStackWithDeleteVoteId: (Long) -> Unit, popBackStackWithToUpdateVote: (String) -> Unit, + popBackStackWithNeedRefresh: (Boolean) -> Unit, navigateVoteEdit: (Vote) -> Unit, onShowSnackbar: (SnackbarToken) -> Unit, onShowDialog: (DialogToken) -> Unit, @@ -89,6 +90,7 @@ fun VoteDetailRoute( ), ) } + VoteDetailSideEffect.ShowDeleteSuccessSnackbar -> { onShowSnackbar( SnackbarToken( @@ -96,6 +98,21 @@ fun VoteDetailRoute( ), ) } + + VoteDetailSideEffect.PopBackStackWithNeedRefresh -> popBackStackWithNeedRefresh(true) + is VoteDetailSideEffect.ShowReportDialog -> onShowDialog( + DialogToken( + title = context.getString(R.string.dialog_report_title), + text = context.getString(R.string.dialog_report_body), + confirmText = context.getString(R.string.dialog_report_confirm_text), + dismissText = context.getString(R.string.dialog_report_dismiss_text), + checkboxText = context.getString(R.string.dialog_report_checkbox_text), + onConfirmRequest = sideEffect.onConfirmRequest, + onCheckedAction = sideEffect.onCheckedAction, + ), + ) + + is VoteDetailSideEffect.ShowSnackbar -> onShowSnackbar(SnackbarToken(message = sideEffect.message)) } } @@ -125,6 +142,7 @@ fun VoteDetailRoute( onClickEdit = viewModel::navigateVoteEdit, onClickDelete = viewModel::showDeleteDialog, onClickOption = viewModel::vote, + onClickReport = viewModel::showReportDialog, ) } @@ -184,10 +202,11 @@ fun VoteDetailScreen( ) { // border를 사용하지 않은 이유 see -> https://stackoverflow.com/questions/75964726/jetpack-compose-circle-shape-border-not-being-applied-as-expected Box { - Box(modifier = Modifier - .size(20.dp) - .clip(CircleShape) - .background(Gray15) + Box( + modifier = Modifier + .size(20.dp) + .clip(CircleShape) + .background(Gray15), ) Image( modifier = Modifier diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt index b1ce6818..471035f4 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt @@ -3,14 +3,19 @@ package com.susu.feature.community.votedetail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.model.Vote +import com.susu.core.model.exception.CannotBlockMyself import com.susu.core.model.exception.NotFoundLedgerException import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.encodeToUri +import com.susu.domain.usecase.block.BlockUserUseCase +import com.susu.domain.usecase.report.ReportVoteUseCase import com.susu.domain.usecase.vote.DeleteVoteUseCase import com.susu.domain.usecase.vote.GetVoteDetailUseCase import com.susu.domain.usecase.vote.VoteUseCase +import com.susu.feature.community.community.CommunitySideEffect import com.susu.feature.community.navigation.CommunityRoute import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.serialization.json.Json import javax.inject.Inject @@ -20,6 +25,8 @@ class VoteDetailViewModel @Inject constructor( private val deleteVoteUseCase: DeleteVoteUseCase, private val getVoteDetailUseCase: GetVoteDetailUseCase, private val voteUseCase: VoteUseCase, + private val reportVoteUseCase: ReportVoteUseCase, + private val blockUserUseCase: BlockUserUseCase, savedStateHandle: SavedStateHandle, ) : BaseViewModel( VoteDetailState(), @@ -117,4 +124,31 @@ class VoteDetailViewModel @Inject constructor( fun navigateVoteEdit() = postSideEffect(VoteDetailSideEffect.NavigateVoteEdit(vote)) fun popBackStack() = postSideEffect(VoteDetailSideEffect.PopBackStackWithToUpdateVote(Json.encodeToUri(currentState.vote))) + + fun showReportDialog() = postSideEffect( + VoteDetailSideEffect.ShowReportDialog( + onConfirmRequest = { reportVote(vote.id) }, + onCheckedAction = { blockUser(vote.uid) }, + ), + ) + + private fun reportVote(voteId: Long): Job = viewModelScope.launch { + reportVoteUseCase(voteId) + .onFailure { throwable -> + postSideEffect(VoteDetailSideEffect.HandleException(throwable = throwable, retry = { reportVote(voteId) })) + } + } + + private fun blockUser(uid: Long): Job = viewModelScope.launch { + blockUserUseCase(uid) + .onSuccess { + postSideEffect(VoteDetailSideEffect.PopBackStackWithNeedRefresh) + } + .onFailure { throwable -> + when (throwable) { + is CannotBlockMyself -> postSideEffect(VoteDetailSideEffect.ShowSnackbar(throwable.message)) + else -> postSideEffect(VoteDetailSideEffect.HandleException(throwable = throwable, retry = { blockUser(uid) })) + } + } + } } diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt index a7a8c49f..5ad39f50 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt @@ -172,6 +172,13 @@ internal fun MainScreen( ) navigator.popBackStackIfNotHome() }, + popBackStackWithNeedRefresh = { needRefresh -> + navigator.navController.previousBackStackEntry?.savedStateHandle?.set( + CommunityRoute.NEED_REFRESH_ARGUMENT_NAME, + needRefresh, + ) + navigator.popBackStackIfNotHome() + }, onShowSnackbar = viewModel::onShowSnackbar, onShowDialog = viewModel::onShowDialog, handleException = viewModel::handleException, From 746e3f3573a36f294701bdbfa5e174fb8bc411d2 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Fri, 2 Feb 2024 09:39:46 +0900 Subject: [PATCH 66/95] chore: ktlint --- .../susu/core/model/exception/SusuExceptions.kt | 2 +- .../data/data/repository/BlockRepositoryImpl.kt | 10 ---------- .../data/data/repository/ReportRepositoryImpl.kt | 13 ------------- .../data/data/repository/VoteRepositoryImpl.kt | 2 +- .../java/com/susu/data/remote/api/BlockService.kt | 14 -------------- .../java/com/susu/data/remote/api/ReportService.kt | 13 ------------- .../susu/domain/usecase/block/BlockUserUseCase.kt | 1 - .../susu/domain/usecase/vote/DeleteVoteUseCase.kt | 6 +++--- .../community/navigation/CommunityNavigation.kt | 2 +- .../community/votedetail/VoteDetailContract.kt | 1 - .../community/votedetail/VoteDetailScreen.kt | 3 --- .../community/votedetail/VoteDetailViewModel.kt | 2 -- .../feature/community/voteedit/VoteEditScreen.kt | 4 ++-- .../community/voteedit/VoteEditViewModel.kt | 1 - .../community/votesearch/VoteSearchScreen.kt | 2 -- .../received/ledgerdetail/LedgerDetailViewModel.kt | 13 ++++++++----- 16 files changed, 16 insertions(+), 73 deletions(-) diff --git a/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt b/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt index de3c300e..789b6ae7 100644 --- a/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt +++ b/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt @@ -59,7 +59,7 @@ enum class SusuServerError(val exception: Exception) { /** Vote Error Code */ ALREADY_VOTED_POST(AlreadyVotedPostException()), - CANNOT_BLOCK_MYSELF(CannotBlockMyself()) + CANNOT_BLOCK_MYSELF(CannotBlockMyself()), } /** Common Exception Code */ diff --git a/data/src/main/java/com/susu/data/data/repository/BlockRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/BlockRepositoryImpl.kt index 14d728fa..af53834e 100644 --- a/data/src/main/java/com/susu/data/data/repository/BlockRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/BlockRepositoryImpl.kt @@ -1,18 +1,8 @@ package com.susu.data.data.repository -import com.susu.core.model.Envelope -import com.susu.core.model.EnvelopeStatics -import com.susu.core.model.Relationship -import com.susu.core.model.SearchEnvelope import com.susu.data.remote.api.BlockService -import com.susu.data.remote.api.EnvelopesService import com.susu.data.remote.model.request.BlockUserRequest -import com.susu.data.remote.model.request.CategoryRequest -import com.susu.data.remote.model.request.EnvelopeRequest -import com.susu.data.remote.model.response.toModel import com.susu.domain.repository.BlockRepository -import com.susu.domain.repository.EnvelopesRepository -import kotlinx.datetime.LocalDateTime import javax.inject.Inject class BlockRepositoryImpl @Inject constructor( diff --git a/data/src/main/java/com/susu/data/data/repository/ReportRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/ReportRepositoryImpl.kt index 34a4299b..6c90625b 100644 --- a/data/src/main/java/com/susu/data/data/repository/ReportRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/ReportRepositoryImpl.kt @@ -1,21 +1,8 @@ package com.susu.data.data.repository -import com.susu.core.model.Envelope -import com.susu.core.model.EnvelopeStatics -import com.susu.core.model.Relationship -import com.susu.core.model.SearchEnvelope -import com.susu.data.remote.api.BlockService -import com.susu.data.remote.api.EnvelopesService import com.susu.data.remote.api.ReportService -import com.susu.data.remote.model.request.BlockUserRequest -import com.susu.data.remote.model.request.CategoryRequest -import com.susu.data.remote.model.request.EnvelopeRequest import com.susu.data.remote.model.request.ReportVoteRequest -import com.susu.data.remote.model.response.toModel -import com.susu.domain.repository.BlockRepository -import com.susu.domain.repository.EnvelopesRepository import com.susu.domain.repository.ReportRepository -import kotlinx.datetime.LocalDateTime import javax.inject.Inject class ReportRepositoryImpl @Inject constructor( diff --git a/data/src/main/java/com/susu/data/data/repository/VoteRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/VoteRepositoryImpl.kt index aa884d06..22ce01b0 100644 --- a/data/src/main/java/com/susu/data/data/repository/VoteRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/VoteRepositoryImpl.kt @@ -76,7 +76,7 @@ class VoteRepositoryImpl @Inject constructor( EditVoteRequest( boardId = boardId, content = content, - ) + ), ).getOrThrow().toModel() override suspend fun deleteVote(id: Long) = api.deleteVote(id).getOrThrow() diff --git a/data/src/main/java/com/susu/data/remote/api/BlockService.kt b/data/src/main/java/com/susu/data/remote/api/BlockService.kt index b0e0bc97..838db7da 100644 --- a/data/src/main/java/com/susu/data/remote/api/BlockService.kt +++ b/data/src/main/java/com/susu/data/remote/api/BlockService.kt @@ -1,23 +1,9 @@ package com.susu.data.remote.api import com.susu.data.remote.model.request.BlockUserRequest -import com.susu.data.remote.model.request.CreateVoteRequest -import com.susu.data.remote.model.request.EditVoteRequest -import com.susu.data.remote.model.request.ReportVoteRequest -import com.susu.data.remote.model.request.VoteRequest -import com.susu.data.remote.model.response.PopularVoteResponse -import com.susu.data.remote.model.response.PostCategoryConfig -import com.susu.data.remote.model.response.VoteDetailResponse -import com.susu.data.remote.model.response.VoteListResponse -import com.susu.data.remote.model.response.VoteResponse import com.susu.data.remote.retrofit.ApiResult import retrofit2.http.Body -import retrofit2.http.DELETE -import retrofit2.http.GET -import retrofit2.http.PATCH import retrofit2.http.POST -import retrofit2.http.Path -import retrofit2.http.Query interface BlockService { diff --git a/data/src/main/java/com/susu/data/remote/api/ReportService.kt b/data/src/main/java/com/susu/data/remote/api/ReportService.kt index 8864e297..9f035626 100644 --- a/data/src/main/java/com/susu/data/remote/api/ReportService.kt +++ b/data/src/main/java/com/susu/data/remote/api/ReportService.kt @@ -1,22 +1,9 @@ package com.susu.data.remote.api -import com.susu.data.remote.model.request.CreateVoteRequest -import com.susu.data.remote.model.request.EditVoteRequest import com.susu.data.remote.model.request.ReportVoteRequest -import com.susu.data.remote.model.request.VoteRequest -import com.susu.data.remote.model.response.PopularVoteResponse -import com.susu.data.remote.model.response.PostCategoryConfig -import com.susu.data.remote.model.response.VoteDetailResponse -import com.susu.data.remote.model.response.VoteListResponse -import com.susu.data.remote.model.response.VoteResponse import com.susu.data.remote.retrofit.ApiResult import retrofit2.http.Body -import retrofit2.http.DELETE -import retrofit2.http.GET -import retrofit2.http.PATCH import retrofit2.http.POST -import retrofit2.http.Path -import retrofit2.http.Query interface ReportService { diff --git a/domain/src/main/java/com/susu/domain/usecase/block/BlockUserUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/block/BlockUserUseCase.kt index aeb0b0d5..86058b64 100644 --- a/domain/src/main/java/com/susu/domain/usecase/block/BlockUserUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/block/BlockUserUseCase.kt @@ -2,7 +2,6 @@ package com.susu.domain.usecase.block import com.susu.core.common.runCatchingIgnoreCancelled import com.susu.domain.repository.BlockRepository -import com.susu.domain.repository.EnvelopesRepository import javax.inject.Inject class BlockUserUseCase @Inject constructor( diff --git a/domain/src/main/java/com/susu/domain/usecase/vote/DeleteVoteUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/vote/DeleteVoteUseCase.kt index a2e97342..dd912b2d 100644 --- a/domain/src/main/java/com/susu/domain/usecase/vote/DeleteVoteUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/vote/DeleteVoteUseCase.kt @@ -8,8 +8,8 @@ class DeleteVoteUseCase @Inject constructor( private val voteRepository: VoteRepository, ) { suspend operator fun invoke(id: Long) = runCatchingIgnoreCancelled { - voteRepository.deleteVote( - id = id, - ) + voteRepository.deleteVote( + id = id, + ) } } diff --git a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt index f1412547..0abd1965 100644 --- a/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt +++ b/feature/community/src/main/java/com/susu/feature/community/navigation/CommunityNavigation.kt @@ -114,7 +114,7 @@ fun NavGraphBuilder.communityNavGraph( } composable( - route = CommunityRoute.voteEditRoute("{${CommunityRoute.VOTE_ARGUMENT_NAME}}") + route = CommunityRoute.voteEditRoute("{${CommunityRoute.VOTE_ARGUMENT_NAME}}"), ) { VoteEditRoute( popBackStack = popBackStack, diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt index d082058b..b3900d3e 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailContract.kt @@ -3,7 +3,6 @@ package com.susu.feature.community.votedetail import com.susu.core.model.Vote import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState -import com.susu.feature.community.community.CommunitySideEffect data class VoteDetailState( val vote: Vote = Vote(), diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt index 5d24c821..5a5b20fd 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailScreen.kt @@ -3,7 +3,6 @@ package com.susu.feature.community.votedetail import androidx.activity.compose.BackHandler import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -42,7 +41,6 @@ import com.susu.core.designsystem.component.badge.BadgeColor import com.susu.core.designsystem.component.badge.BadgeStyle import com.susu.core.designsystem.component.badge.SusuBadge import com.susu.core.designsystem.component.screen.LoadingScreen -import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray15 import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Orange60 @@ -218,7 +216,6 @@ fun VoteDetailScreen( ) } - Text(text = stringResource(R.string.word_anonymous_susu), style = SusuTheme.typography.title_xxxs) if (uiState.vote.isMine) { diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt index 471035f4..c75627ff 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.model.Vote import com.susu.core.model.exception.CannotBlockMyself -import com.susu.core.model.exception.NotFoundLedgerException import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.encodeToUri import com.susu.domain.usecase.block.BlockUserUseCase @@ -12,7 +11,6 @@ import com.susu.domain.usecase.report.ReportVoteUseCase import com.susu.domain.usecase.vote.DeleteVoteUseCase import com.susu.domain.usecase.vote.GetVoteDetailUseCase import com.susu.domain.usecase.vote.VoteUseCase -import com.susu.feature.community.community.CommunitySideEffect import com.susu.feature.community.navigation.CommunityRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job diff --git a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt index 7efa2ea6..8f93caeb 100644 --- a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditScreen.kt @@ -51,7 +51,7 @@ fun VoteEditRoute( is VoteEditSideEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) VoteEditSideEffect.PopBackStack -> popBackStack() VoteEditSideEffect.ShowCanNotChangeOptionSnackbar -> onShowSnackbar( - SnackbarToken(message = context.getString(R.string.snackbar_can_not_change_option)) + SnackbarToken(message = context.getString(R.string.snackbar_can_not_change_option)), ) } } @@ -67,7 +67,7 @@ fun VoteEditRoute( onClickRegister = viewModel::editVote, onClickCategoryButton = viewModel::selectCategory, onTextChangeContent = viewModel::updateContent, - onClickOption = viewModel::showCannotChangeOptionSnackbar + onClickOption = viewModel::showCannotChangeOptionSnackbar, ) } diff --git a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt index 319c90b2..93fb5c3e 100644 --- a/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/voteedit/VoteEditViewModel.kt @@ -6,7 +6,6 @@ import com.susu.core.model.Category import com.susu.core.model.Vote import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri -import com.susu.domain.usecase.vote.CreateVoteUseCase import com.susu.domain.usecase.vote.EditVoteUseCase import com.susu.domain.usecase.vote.GetPostCategoryConfigUseCase import com.susu.feature.community.navigation.CommunityRoute diff --git a/feature/community/src/main/java/com/susu/feature/community/votesearch/VoteSearchScreen.kt b/feature/community/src/main/java/com/susu/feature/community/votesearch/VoteSearchScreen.kt index cbdd54ba..703f9499 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votesearch/VoteSearchScreen.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votesearch/VoteSearchScreen.kt @@ -219,7 +219,6 @@ private fun SearchResultColumn( ) } - voteList.forEach { vote -> SusuRecentSearchContainer( typeIconId = R.drawable.ic_vote, @@ -228,7 +227,6 @@ private fun SearchResultColumn( ) } } - } @Preview diff --git a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt index 9748126a..129f66dc 100644 --- a/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt +++ b/feature/received/src/main/java/com/susu/feature/received/ledgerdetail/LedgerDetailViewModel.kt @@ -76,11 +76,14 @@ class LedgerDetailViewModel @Inject constructor( it } }.run { - if (friend.id == searchEnvelope.friend.id) copy( - friend = searchEnvelope.friend, - relation = searchEnvelope.relation, - ) - else this + if (friend.id == searchEnvelope.friend.id) { + copy( + friend = searchEnvelope.friend, + relation = searchEnvelope.relation, + ) + } else { + this + } } }.toPersistentList(), ) From 7a609266f5915ccaf26e0872ae4d37c4da204941 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Fri, 2 Feb 2024 09:46:26 +0900 Subject: [PATCH 67/95] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=20=EC=8B=A0?= =?UTF-8?q?=EA=B3=A0=ED=95=9C=20=EC=83=81=ED=83=9C=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/core/model/exception/SusuExceptions.kt | 12 ++++++++++-- .../community/community/CommunityViewModel.kt | 10 +++++++--- .../community/votedetail/VoteDetailViewModel.kt | 10 +++++++--- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt b/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt index 789b6ae7..478936b7 100644 --- a/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt +++ b/core/model/src/main/java/com/susu/core/model/exception/SusuExceptions.kt @@ -59,7 +59,10 @@ enum class SusuServerError(val exception: Exception) { /** Vote Error Code */ ALREADY_VOTED_POST(AlreadyVotedPostException()), - CANNOT_BLOCK_MYSELF(CannotBlockMyself()), + CANNOT_BLOCK_MYSELF(CannotBlockMyselfException()), + + /** Report Error Code */ + ALREADY_EXISTS_REPORT_HISTORY_ERROR(AlreadyExistsReportHistoryException()), } /** Common Exception Code */ @@ -207,6 +210,11 @@ class AlreadyVotedPostException( override val message: String = "이미 진행된 투표입니다.", ) : RuntimeException() -class CannotBlockMyself( +class CannotBlockMyselfException( override val message: String = "본인을 차단할 수 없습니다.", ) : RuntimeException() + +/** Report Error Code */ +class AlreadyExistsReportHistoryException( + override val message: String = "이미 신고한 상태입니다.", +) : RuntimeException() diff --git a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt index 8e02c43c..9f0b7e3b 100644 --- a/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/community/CommunityViewModel.kt @@ -3,7 +3,8 @@ package com.susu.feature.community.community import androidx.lifecycle.viewModelScope import com.susu.core.model.Category import com.susu.core.model.Vote -import com.susu.core.model.exception.CannotBlockMyself +import com.susu.core.model.exception.AlreadyExistsReportHistoryException +import com.susu.core.model.exception.CannotBlockMyselfException import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.decodeFromUri import com.susu.domain.usecase.block.BlockUserUseCase @@ -204,7 +205,10 @@ class CommunityViewModel @Inject constructor( private fun reportVote(voteId: Long): Job = viewModelScope.launch { reportVoteUseCase(voteId) .onFailure { throwable -> - postSideEffect(CommunitySideEffect.HandleException(throwable = throwable, retry = { reportVote(voteId) })) + when (throwable) { + is AlreadyExistsReportHistoryException -> postSideEffect(CommunitySideEffect.ShowSnackbar(throwable.message)) + else -> postSideEffect(CommunitySideEffect.HandleException(throwable = throwable, retry = { reportVote(voteId) })) + } } } @@ -215,7 +219,7 @@ class CommunityViewModel @Inject constructor( } .onFailure { throwable -> when (throwable) { - is CannotBlockMyself -> postSideEffect(CommunitySideEffect.ShowSnackbar(throwable.message)) + is CannotBlockMyselfException -> postSideEffect(CommunitySideEffect.ShowSnackbar(throwable.message)) else -> postSideEffect(CommunitySideEffect.HandleException(throwable = throwable, retry = { blockUser(uid) })) } } diff --git a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt index c75627ff..3a1b9cb3 100644 --- a/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt +++ b/feature/community/src/main/java/com/susu/feature/community/votedetail/VoteDetailViewModel.kt @@ -3,7 +3,8 @@ package com.susu.feature.community.votedetail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.model.Vote -import com.susu.core.model.exception.CannotBlockMyself +import com.susu.core.model.exception.AlreadyExistsReportHistoryException +import com.susu.core.model.exception.CannotBlockMyselfException import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.extension.encodeToUri import com.susu.domain.usecase.block.BlockUserUseCase @@ -133,7 +134,10 @@ class VoteDetailViewModel @Inject constructor( private fun reportVote(voteId: Long): Job = viewModelScope.launch { reportVoteUseCase(voteId) .onFailure { throwable -> - postSideEffect(VoteDetailSideEffect.HandleException(throwable = throwable, retry = { reportVote(voteId) })) + when (throwable) { + is AlreadyExistsReportHistoryException -> postSideEffect(VoteDetailSideEffect.ShowSnackbar(throwable.message)) + else -> postSideEffect(VoteDetailSideEffect.HandleException(throwable = throwable, retry = { reportVote(voteId) })) + } } } @@ -144,7 +148,7 @@ class VoteDetailViewModel @Inject constructor( } .onFailure { throwable -> when (throwable) { - is CannotBlockMyself -> postSideEffect(VoteDetailSideEffect.ShowSnackbar(throwable.message)) + is CannotBlockMyselfException -> postSideEffect(VoteDetailSideEffect.ShowSnackbar(throwable.message)) else -> postSideEffect(VoteDetailSideEffect.HandleException(throwable = throwable, retry = { blockUser(uid) })) } } From 2230e768e55f5721ccef4dca3166c260da01a061 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 14:59:56 +0900 Subject: [PATCH 68/95] =?UTF-8?q?chore:=20stringRes=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/content/susu/SusuStatisticsContent.kt | 8 ++++---- feature/statistics/src/main/res/values/strings.xml | 5 ++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt index 5f429688..74c6a66e 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt @@ -106,7 +106,7 @@ fun SusuStatisticsScreen( verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_xxs), ) { SusuStatisticsOptionSlot( - title = "지금 평균 수수 보기", + title = stringResource(R.string.statistics_susu_average_title), age = stringResource(id = R.string.word_age_unit, uiState.age.num), money = uiState.susuStatistics.averageSent, relationship = uiState.relationship.relation, @@ -116,13 +116,13 @@ fun SusuStatisticsScreen( onRelationshipClick = onClickRelationship, ) StatisticsHorizontalItem( - title = "관계 별 평균 수수", + title = stringResource(R.string.statistics_susu_relationship_average), name = uiState.susuStatistics.averageRelationship.title, money = uiState.susuStatistics.averageRelationship.value, isActive = !isBlind, ) StatisticsHorizontalItem( - title = "경조사 카테고리 별 평균 수수", + title = stringResource(R.string.statistics_susu_category_average), name = uiState.susuStatistics.averageCategory.title, money = uiState.susuStatistics.averageCategory.value, isActive = !isBlind, @@ -130,7 +130,7 @@ fun SusuStatisticsScreen( RecentSpentGraph( isActive = !isBlind, - graphTitle = "올해 쓴 금액", + graphTitle = stringResource(R.string.statistics_susu_this_year_spent), spentData = uiState.susuStatistics.recentSpent.toPersistentList(), maximumAmount = uiState.susuStatistics.recentMaximumSpent, totalAmount = uiState.susuStatistics.recentTotalSpent, diff --git a/feature/statistics/src/main/res/values/strings.xml b/feature/statistics/src/main/res/values/strings.xml index 5f20a0d9..11e6c45d 100644 --- a/feature/statistics/src/main/res/values/strings.xml +++ b/feature/statistics/src/main/res/values/strings.xml @@ -21,7 +21,10 @@ 을 보내고 있어요 - %d대 옵션 선택 + 지금 평균 수수 보기 + 관계 별 평균 수수 + 경조사 카테고리 별 평균 수수 + 올해 쓴 금액 From 156f0f09522fe35f39f3c363d49d1791773d3c3b Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 15:08:14 +0900 Subject: [PATCH 69/95] =?UTF-8?q?feat:=20=ED=86=B5=EA=B3=84=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=EC=9D=84=20=EB=B6=88=EB=9F=AC=EC=98=A4=EC=A7=80=20?= =?UTF-8?q?=EB=AA=BB=ED=96=88=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/content/susu/SusuStatisticsViewModel.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt index a0d0c09d..5d782319 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt @@ -36,6 +36,11 @@ class SusuStatisticsViewModel @Inject constructor( relationship = relationshipConfig.firstOrNull() ?: Relationship(), ) } + } else { + val exception = categoryConfigResult.exceptionOrNull() + ?: relationshipConfigResult.exceptionOrNull() + ?: Throwable() + postSideEffect(SusuStatisticsEffect.HandleException(exception, ::getStatisticsOption)) } intent { copy(isLoading = false) } } From fc39f50dd59f550c5e13a37a154fc1a62fc8b656 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 15:09:25 +0900 Subject: [PATCH 70/95] chore: ktlint, detekt check --- .../susu/feature/statistics/content/my/MyStatisticsContent.kt | 2 +- .../statistics/content/susu/SusuStatisticsViewModel.kt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt index 1316ec97..5ce22d53 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/my/MyStatisticsContent.kt @@ -78,7 +78,7 @@ fun MyStatisticsContent( spentData = uiState.statistics.recentSpent.toPersistentList(), maximumAmount = uiState.statistics.recentMaximumSpent, totalAmount = uiState.statistics.recentTotalSpent, - graphTitle = stringResource(R.string.statistics_recent_8_total_money) + graphTitle = stringResource(R.string.statistics_recent_8_total_money), ) Row( modifier = Modifier diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt index 5d782319..7a27a052 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt @@ -49,7 +49,9 @@ class SusuStatisticsViewModel @Inject constructor( fun getSusuStatistics() { if (currentState.relationship !in currentState.relationshipConfig && currentState.category !in currentState.categoryConfig - ) return + ) { + return + } viewModelScope.launch { getSusuStatisticsUseCase( From d646198ebea37ff2e6a3209bb6c2491d3d0cc9eb Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 15:17:55 +0900 Subject: [PATCH 71/95] =?UTF-8?q?chore:=20=ED=86=B5=EA=B3=84=20=EC=98=B5?= =?UTF-8?q?=EC=85=98=20=EB=B6=88=EB=9F=AC=EC=98=A4=EA=B8=B0=20=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=20=EC=8B=9C=20Exception=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/statistics/content/susu/SusuStatisticsViewModel.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt index 7a27a052..d72549d7 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsViewModel.kt @@ -3,6 +3,7 @@ package com.susu.feature.statistics.content.susu import androidx.lifecycle.viewModelScope import com.susu.core.model.Category import com.susu.core.model.Relationship +import com.susu.core.model.exception.UnknownException import com.susu.core.ui.base.BaseViewModel import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase @@ -39,7 +40,7 @@ class SusuStatisticsViewModel @Inject constructor( } else { val exception = categoryConfigResult.exceptionOrNull() ?: relationshipConfigResult.exceptionOrNull() - ?: Throwable() + ?: UnknownException() postSideEffect(SusuStatisticsEffect.HandleException(exception, ::getStatisticsOption)) } intent { copy(isLoading = false) } From 5c20ed70d3076f0062ac4aca5dead4c63b9c5797 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Fri, 2 Feb 2024 15:18:07 +0900 Subject: [PATCH 72/95] chore: detekt check --- .../feature/statistics/content/susu/SusuStatisticsContent.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt index 74c6a66e..dea25448 100644 --- a/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt +++ b/feature/statistics/src/main/java/com/susu/feature/statistics/content/susu/SusuStatisticsContent.kt @@ -98,7 +98,9 @@ fun SusuStatisticsScreen( onSelectCategory: (Int) -> Unit = {}, ) { val context = LocalContext.current - val ageItems = remember { StatisticsAge.entries.map { context.getString(R.string.word_age_unit, it.num) }.toImmutableList() } + val ageItems = remember { + StatisticsAge.entries.map { context.getString(R.string.word_age_unit, it.num) }.toImmutableList() + } Box(modifier = modifier.fillMaxSize()) { Column( From b06fc20f995723c5dc40fcb30221e3026542878d Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sat, 3 Feb 2024 00:02:22 +0900 Subject: [PATCH 73/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B4=EC=9A=94=20?= =?UTF-8?q?=EB=B4=89=ED=88=AC=20=ED=99=94=EB=A9=B4=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=ED=86=B5=EC=8B=A0=20=EC=84=B8=ED=8C=85=20=EB=B0=8F=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/envelope/SentEnvelopeScreen.kt | 41 ++++++++++++----- .../feature/envelope/SentEnvelopeViewModel.kt | 12 ++--- .../envelope/component/EnvelopeHistoryItem.kt | 44 ++++++++++++++----- .../susu/feature/sent/component/SentCard.kt | 3 +- 4 files changed, 71 insertions(+), 29 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt index 1aa1cd77..988b490b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt @@ -12,6 +12,9 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.Text @@ -37,6 +40,7 @@ import com.susu.core.designsystem.theme.Gray60 import com.susu.core.designsystem.theme.Gray90 import com.susu.core.designsystem.theme.Orange20 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.ui.extension.OnBottomReached import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.envelope.component.EnvelopeHistoryItem @@ -49,6 +53,8 @@ fun SentEnvelopeRoute( navigateSentEnvelopeDetail: () -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val historyListState = rememberLazyListState() + viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeSideEffect.PopBackStack -> popBackStack() @@ -60,6 +66,10 @@ fun SentEnvelopeRoute( viewModel.initData() } + historyListState.OnBottomReached { + viewModel.getEnvelopeHistoryList() + } + SentEnvelopeScreen( uiState = uiState, onClickBackIcon = viewModel::popBackStack, @@ -71,6 +81,7 @@ fun SentEnvelopeRoute( fun SentEnvelopeScreen( uiState: SentEnvelopeState = SentEnvelopeState(), modifier: Modifier = Modifier, + historyListState: LazyListState = rememberLazyListState(), onClickBackIcon: () -> Unit = {}, onClickSearchIcon: () -> Unit = {}, onClickNotificationIcon: () -> Unit = {}, @@ -86,7 +97,7 @@ fun SentEnvelopeScreen( leftIcon = { BackIcon(onClickBackIcon) }, - title = uiState.envelopeInfo[0].friend.name, + title = uiState.envelopeInfo.last().friend.name, actions = { SearchIcon(onClickSearchIcon) NotificationIcon(onClickNotificationIcon) @@ -101,7 +112,7 @@ fun SentEnvelopeScreen( ), ) { Text( - text = stringResource(R.string.sent_envelope_card_monee_total) + uiState.envelopeInfo[0].totalAmounts.toMoneyFormat() + + text = stringResource(R.string.sent_envelope_card_monee_total) + uiState.envelopeInfo.last().totalAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_m, color = Gray100, @@ -109,7 +120,7 @@ fun SentEnvelopeScreen( Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_xxs)) SusuBadge( color = BadgeColor.Gray30, - text = "${uiState.envelopeInfo[0].sentAmounts.toFloat() + uiState.envelopeInfo[0].receivedAmounts}" + + text = (uiState.envelopeInfo.last().receivedAmounts - uiState.envelopeInfo.last().sentAmounts).toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), padding = BadgeStyle.smallBadge, ) @@ -130,7 +141,7 @@ fun SentEnvelopeScreen( ) } LinearProgressIndicator( - progress = { uiState.envelopeInfo[0].sentAmounts.toFloat() / uiState.envelopeInfo[0].totalAmounts }, + progress = { uiState.envelopeInfo.last().sentAmounts.toFloat() / uiState.envelopeInfo.last().totalAmounts }, color = SusuTheme.colorScheme.primary, trackColor = Orange20, strokeCap = StrokeCap.Round, @@ -143,12 +154,12 @@ fun SentEnvelopeScreen( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = uiState.envelopeInfo[0].sentAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), + text = uiState.envelopeInfo.last().sentAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxxxs, color = Gray90, ) Text( - text = uiState.envelopeInfo[0].receivedAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), + text = uiState.envelopeInfo.last().receivedAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxxxs, color = Gray60, ) @@ -160,15 +171,21 @@ fun SentEnvelopeScreen( ) LazyColumn( + state = historyListState, contentPadding = PaddingValues(vertical = SusuTheme.spacing.spacing_m), verticalArrangement = Arrangement.spacedBy(SusuTheme.spacing.spacing_m), ) { - repeat(10) { - item { - EnvelopeHistoryItem( - onClick = onClickEnvelopeDetail, - ) - } + items( + items = uiState.envelopeHistoryList, + key = { it.envelope.id } + ) { + EnvelopeHistoryItem( + type = it.envelope.type, + event = it.category!!.category, + date = it.envelope.handedOverAt!!, + money = it.envelope.amount, + onClick = onClickEnvelopeDetail, + ) } } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt index b21e6c58..a64b6816 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt @@ -19,14 +19,14 @@ class SentEnvelopeViewModel @Inject constructor( ) : BaseViewModel( SentEnvelopeState(), ) { - private val friendId = savedStateHandle.get(SentRoute.FRIEND_ID_ARGUMENT_NAME)!! + private val friendId = savedStateHandle.get(SentRoute.FRIEND_ID_ARGUMENT_NAME)!! fun initData() { - getEnvelopeInfo(friendId) - getEnvelopeDetailHistoryList(friendId) + getEnvelopeInfo() + getEnvelopeHistoryList() } - fun getEnvelopeInfo(id: Long) = viewModelScope.launch { + fun getEnvelopeInfo(id: Long = friendId) = viewModelScope.launch { val friendsList: List = listOf(id) getEnvelopesListUseCase( @@ -35,13 +35,13 @@ class SentEnvelopeViewModel @Inject constructor( val envelopeInfo = currentState.envelopeInfo.plus(envelope).toPersistentList() intent { copy( - envelopeInfo = envelopeInfo + envelopeInfo = envelopeInfo, ) } } } - fun getEnvelopeDetailHistoryList(id: Long) = viewModelScope.launch { + fun getEnvelopeHistoryList(id: Long = friendId) = viewModelScope.launch { val friendsList: List = listOf(id) val includeList = listOf("CATEGORY") diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/component/EnvelopeHistoryItem.kt b/feature/sent/src/main/java/com/susu/feature/envelope/component/EnvelopeHistoryItem.kt index 2ea71b28..eed401d8 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/component/EnvelopeHistoryItem.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/component/EnvelopeHistoryItem.kt @@ -11,6 +11,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import com.susu.core.designsystem.component.badge.BadgeColor import com.susu.core.designsystem.component.badge.BadgeStyle import com.susu.core.designsystem.component.badge.SusuBadge @@ -19,12 +21,22 @@ import com.susu.core.designsystem.theme.Gray50 import com.susu.core.designsystem.theme.Orange60 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.extension.susuClickable +import com.susu.core.ui.extension.toMoneyFormat +import com.susu.core.ui.util.to_yyyy_dot_MM_dot_dd import com.susu.feature.sent.R +import java.time.LocalDateTime + +enum class EnvelopeType { + SENT, RECEIVED +} @Composable fun EnvelopeHistoryItem( modifier: Modifier = Modifier, - isSent: Boolean = true, + type: String = "", + event: String = "", + date: LocalDateTime = LocalDateTime.now(), + money: Long = 0, onClick: () -> Unit = {}, ) { Row( @@ -39,35 +51,47 @@ fun EnvelopeHistoryItem( ), verticalAlignment = Alignment.CenterVertically, ) { - // TODO: text 변경하기 Icon( painter = painterResource( - id = if (isSent) { + id = if (type == EnvelopeType.SENT.name) { R.drawable.ic_round_arrow_sent } else { R.drawable.ic_round_arrow_received }, ), contentDescription = null, - tint = if (isSent) Orange60 else Gray50, + tint = if (type == EnvelopeType.SENT.name) Orange60 else Gray50, ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_s)) SusuBadge( - color = if (isSent) BadgeColor.Gray90 else BadgeColor.Gray40, - text = "결혼식", + color = if (type == EnvelopeType.SENT.name) BadgeColor.Gray90 else BadgeColor.Gray40, + text = event, padding = BadgeStyle.smallBadge, ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_s)) Text( - text = "22.07.18", + text = date.to_yyyy_dot_MM_dot_dd().substring(2), style = SusuTheme.typography.title_xxs, - color = if (isSent) Gray100 else Gray50, + color = if (type == EnvelopeType.SENT.name) Gray100 else Gray50, ) Spacer(modifier = modifier.weight(1f)) Text( - text = " 100,000원", + text = money.toInt().toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xs, - color = if (isSent) Gray100 else Gray50, + color = if (type == EnvelopeType.SENT.name) Gray100 else Gray50, + ) + } +} + +@Preview +@Composable +fun EnvelopeHistoryItem() { + SusuTheme { + EnvelopeHistoryItem( + type = "SENT", + event = "돌잔치", + date = LocalDateTime.now(), + money = 50000, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt index 14a78da7..61f1913e 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt @@ -86,7 +86,8 @@ fun SentCard( Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_s)) SusuBadge( color = BadgeColor.Gray20, - text = stringResource(R.string.sent_envelope_card_monee_total) + totalAmounts.toMoneyFormat(), + text = stringResource(R.string.sent_envelope_card_monee_total) + totalAmounts.toMoneyFormat() + + stringResource(R.string.sent_envelope_card_money_won), padding = BadgeStyle.smallBadge, ) Spacer(modifier = modifier.weight(1f)) From b2d706e93a8b62e0d3235215bcf21680e41f8fbb Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sat, 3 Feb 2024 00:03:07 +0900 Subject: [PATCH 74/95] =?UTF-8?q?chore:=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20string=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/susu/feature/navigator/MainNavigator.kt | 2 +- feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt | 1 - .../java/com/susu/feature/sent/component/SentHistoryItem.kt | 3 ++- .../java/com/susu/feature/sent/navigation/SentNavigation.kt | 3 --- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt index fd68e085..bf14cadf 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt @@ -58,7 +58,7 @@ internal class MainNavigator( ReceivedRoute.ledgerFilterRoute("{${ReceivedRoute.FILTER_ARGUMENT_NAME}}"), ReceivedRoute.envelopeDetailRoute, ReceivedRoute.envelopeEditRoute, - SentRoute.sentEnvelopeRoute("${SentRoute.FRIEND_ID_ARGUMENT_NAME}"), + SentRoute.sentEnvelopeRoute("{${SentRoute.FRIEND_ID_ARGUMENT_NAME}}"), SentRoute.sentEnvelopeDetailRoute, SentRoute.sentEnvelopeEditRoute, CommunityRoute.route, diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt index 10c1a22d..4f5ad535 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt @@ -55,7 +55,6 @@ fun SentRoute( when (sideEffect) { SentEffect.NavigateEnvelopeAdd -> navigateSentEnvelopeAdd() is SentEffect.NavigateEnvelope -> navigateSentEnvelope(sideEffect.id) - else -> {} } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt index ccabe186..938e05e6 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentHistoryItem.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.susu.core.designsystem.component.badge.BadgeColor import com.susu.core.designsystem.component.badge.BadgeStyle @@ -67,7 +68,7 @@ fun SentHistoryItem( ) Spacer(modifier = modifier.weight(1f)) Text( - text = "${money.toInt().toMoneyFormat()}원", + text = money.toInt().toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxs, color = if (type == EnvelopeType.SENT.name) Gray100 else Gray50, ) diff --git a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt index 8d2c3c12..9c9445b4 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt @@ -17,7 +17,6 @@ fun NavController.navigateSent(navOptions: NavOptions) { navigate(SentRoute.route, navOptions) } -// TODO: 수정? fun NavController.navigateSentEnvelope(id: Long) { navigate(SentRoute.sentEnvelopeRoute(id = id.toString())) } @@ -50,7 +49,6 @@ fun NavGraphBuilder.sentNavGraph( ) } - // TODO: 수정 필요 composable( route = SentRoute.sentEnvelopeRoute("{${SentRoute.FRIEND_ID_ARGUMENT_NAME}}"), arguments = listOf( @@ -92,7 +90,6 @@ object SentRoute { const val sentEnvelopeEditRoute = "sent-envelope-edit" const val sentEnvelopeAddRoute = "sent-envelope-add" - // TODO: 수정 필요 fun sentEnvelopeRoute(id: String) = "sent-envelope/$id" const val FRIEND_ID_ARGUMENT_NAME = "sent-envelope-id" } From 67ac6a702d9f00669c59c6526ed1139ec25f3653 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Sat, 3 Feb 2024 16:09:07 +0900 Subject: [PATCH 75/95] =?UTF-8?q?refactor:=20SusuDialog=EB=A5=BC=20Dialog?= =?UTF-8?q?=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/dialog/SusuCheckedDialog.kt | 20 +++++++------------ .../component/dialog/SusuDialog.kt | 20 ++++--------------- .../main/java/com/susu/core/ui/DialogToken.kt | 1 - .../com/susu/feature/navigator/MainScreen.kt | 2 -- 4 files changed, 11 insertions(+), 32 deletions(-) diff --git a/core/designsystem/src/main/java/com/susu/core/designsystem/component/dialog/SusuCheckedDialog.kt b/core/designsystem/src/main/java/com/susu/core/designsystem/component/dialog/SusuCheckedDialog.kt index 06f36607..c80a22af 100644 --- a/core/designsystem/src/main/java/com/susu/core/designsystem/component/dialog/SusuCheckedDialog.kt +++ b/core/designsystem/src/main/java/com/susu/core/designsystem/component/dialog/SusuCheckedDialog.kt @@ -1,11 +1,9 @@ package com.susu.core.designsystem.component.dialog import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -19,10 +17,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.GhostButtonColor import com.susu.core.designsystem.component.button.SmallButtonStyle @@ -34,34 +32,26 @@ import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray40 import com.susu.core.designsystem.theme.Gray80 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.ui.extension.susuClickable @Composable fun SusuCheckedDialog( - modifier: Modifier = Modifier, title: String? = null, text: String? = null, defaultChecked: Boolean = false, checkboxText: String = "", confirmText: String = "", dismissText: String? = null, - isDimmed: Boolean = true, textAlign: TextAlign = TextAlign.Center, onConfirmRequest: (isChecked: Boolean) -> Unit = {}, onDismissRequest: () -> Unit = {}, ) { - val rootModifier = modifier - .fillMaxSize() - .background(color = if (isDimmed) Color.Black.copy(alpha = 0.16f) else Color.Transparent) - .padding(horizontal = SusuTheme.spacing.spacing_xl) var isChecked by remember { mutableStateOf(defaultChecked) } - Box( - modifier = rootModifier, - ) { + Dialog(onDismissRequest = onDismissRequest) { Column( modifier = Modifier .fillMaxWidth() - .align(Alignment.Center) .background(color = Gray10, shape = RoundedCornerShape(8.dp)) .padding(SusuTheme.spacing.spacing_xl), horizontalAlignment = Alignment.CenterHorizontally, @@ -97,6 +87,10 @@ fun SusuCheckedDialog( text = checkboxText, style = SusuTheme.typography.title_xxs, color = if (isChecked) Gray100 else Gray40, + modifier = Modifier.susuClickable( + onClick = { isChecked = !isChecked }, + rippleEnabled = false, + ), ) } Spacer(modifier = Modifier.height(SusuTheme.spacing.spacing_xl)) diff --git a/core/designsystem/src/main/java/com/susu/core/designsystem/component/dialog/SusuDialog.kt b/core/designsystem/src/main/java/com/susu/core/designsystem/component/dialog/SusuDialog.kt index abe6cbf1..9f19b1b8 100644 --- a/core/designsystem/src/main/java/com/susu/core/designsystem/component/dialog/SusuDialog.kt +++ b/core/designsystem/src/main/java/com/susu/core/designsystem/component/dialog/SusuDialog.kt @@ -1,11 +1,9 @@ package com.susu.core.designsystem.component.dialog import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -14,10 +12,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.GhostButtonColor import com.susu.core.designsystem.component.button.SmallButtonStyle @@ -30,28 +28,18 @@ import com.susu.core.designsystem.theme.SusuTheme @Composable fun SusuDialog( - modifier: Modifier = Modifier, title: String? = null, text: String? = null, confirmText: String = "", dismissText: String? = null, - isDimmed: Boolean = true, textAlign: TextAlign = TextAlign.Center, onConfirmRequest: () -> Unit = {}, onDismissRequest: () -> Unit = {}, ) { - val rootModifier = modifier - .fillMaxSize() - .background(color = if (isDimmed) Color.Black.copy(alpha = 0.16f) else Color.Transparent) - .padding(horizontal = SusuTheme.spacing.spacing_xl) - - Box( - modifier = rootModifier, - ) { + Dialog(onDismissRequest = onDismissRequest) { Column( modifier = Modifier .fillMaxWidth() - .align(Alignment.Center) .background(color = Gray10, shape = RoundedCornerShape(8.dp)) .padding(SusuTheme.spacing.spacing_xl), horizontalAlignment = Alignment.CenterHorizontally, @@ -100,7 +88,7 @@ fun SusuDialog( } } -@Preview +@Preview(showBackground = true) @Composable fun SusuDialogPreview() { SusuTheme { @@ -112,7 +100,7 @@ fun SusuDialogPreview() { } } -@Preview +@Preview(showBackground = true) @Composable fun SusuDialogLongTitlePreview() { SusuTheme { diff --git a/core/ui/src/main/java/com/susu/core/ui/DialogToken.kt b/core/ui/src/main/java/com/susu/core/ui/DialogToken.kt index 39ba8af1..e694b35b 100644 --- a/core/ui/src/main/java/com/susu/core/ui/DialogToken.kt +++ b/core/ui/src/main/java/com/susu/core/ui/DialogToken.kt @@ -14,7 +14,6 @@ data class DialogToken( val dismissText: String? = null, val checkboxText: String? = null, val defaultChecked: Boolean = false, - val isDimmed: Boolean = true, val textAlign: TextAlign = TextAlign.Center, val onConfirmRequest: () -> Unit = {}, val onCheckedAction: () -> Unit = {}, diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt index f2d96de5..427e36ce 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt @@ -218,7 +218,6 @@ internal fun MainScreen( text = text, confirmText = confirmText, dismissText = dismissText, - isDimmed = isDimmed, textAlign = textAlign, onConfirmRequest = { onConfirmRequest() @@ -236,7 +235,6 @@ internal fun MainScreen( confirmText = confirmText, dismissText = dismissText, checkboxText = checkboxText!!, - isDimmed = isDimmed, defaultChecked = defaultChecked, textAlign = textAlign, onConfirmRequest = { checked -> From 8f4b66b63f4d635d9042dc8374c34ac854f795b7 Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sat, 3 Feb 2024 23:19:58 +0900 Subject: [PATCH 76/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B4=EC=9A=94=20?= =?UTF-8?q?=EB=B4=89=ED=88=AC=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=20=ED=86=B5=EC=8B=A0=20=EC=84=B8=ED=8C=85=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/core/model/EnvelopeDetail.kt | 9 +++ .../com/susu/core/model/FriendRelationship.kt | 7 ++ .../repository/EnvelopesRepositoryImpl.kt | 7 ++ .../susu/data/remote/api/EnvelopesService.kt | 7 ++ .../model/response/EnvelopeDetailResponse.kt | 35 +++++++++ .../data/remote/model/response/FriendInfo.kt | 17 ----- .../response/FriendStatisticsResponse.kt | 14 ++++ .../domain/repository/EnvelopesRepository.kt | 7 +- .../envelope/GetEnvelopeDetailUseCase.kt | 13 ++++ .../susu/feature/navigator/MainNavigator.kt | 6 +- .../feature/envelope/SentEnvelopeContract.kt | 2 +- .../feature/envelope/SentEnvelopeScreen.kt | 10 +-- .../feature/envelope/SentEnvelopeViewModel.kt | 3 +- .../envelope/component/EnvelopeHistoryItem.kt | 2 +- .../SentEnvelopeDetailContract.kt | 14 +++- .../SentEnvelopeDetailScreen.kt | 71 +++++++++++-------- .../SentEnvelopeDetailViewModel.kt | 42 ++++++++++- .../envelopeedit/SentEnvelopeEditContract.kt | 5 +- .../envelopeedit/SentEnvelopeEditScreen.kt | 9 +-- .../envelopeedit/SentEnvelopeEditViewModel.kt | 4 +- .../feature/sent/navigation/SentNavigation.kt | 19 +++-- feature/sent/src/main/res/values/strings.xml | 10 +++ 22 files changed, 237 insertions(+), 76 deletions(-) create mode 100644 core/model/src/main/java/com/susu/core/model/EnvelopeDetail.kt create mode 100644 core/model/src/main/java/com/susu/core/model/FriendRelationship.kt create mode 100644 data/src/main/java/com/susu/data/remote/model/response/EnvelopeDetailResponse.kt delete mode 100644 data/src/main/java/com/susu/data/remote/model/response/FriendInfo.kt create mode 100644 domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopeDetailUseCase.kt diff --git a/core/model/src/main/java/com/susu/core/model/EnvelopeDetail.kt b/core/model/src/main/java/com/susu/core/model/EnvelopeDetail.kt new file mode 100644 index 00000000..f1e9bfee --- /dev/null +++ b/core/model/src/main/java/com/susu/core/model/EnvelopeDetail.kt @@ -0,0 +1,9 @@ +package com.susu.core.model + +data class EnvelopeDetail( + val envelope: Envelope, + val category: Category, + val relationship: Relationship, + val friendRelationship: FriendRelationship, + val friend: Friend, +) diff --git a/core/model/src/main/java/com/susu/core/model/FriendRelationship.kt b/core/model/src/main/java/com/susu/core/model/FriendRelationship.kt new file mode 100644 index 00000000..d81e15a4 --- /dev/null +++ b/core/model/src/main/java/com/susu/core/model/FriendRelationship.kt @@ -0,0 +1,7 @@ +package com.susu.core.model + +data class FriendRelationship( + val id: Long = 0, + val friendId: Long = 0, + val relationshipId: Long = 0, +) diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index 14a60b76..bd78f906 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -3,6 +3,7 @@ package com.susu.data.data.repository import com.susu.core.model.FriendStatistics import com.susu.data.remote.api.EnvelopesService import com.susu.core.model.Envelope +import com.susu.core.model.EnvelopeDetail import com.susu.core.model.EnvelopeSearch import com.susu.core.model.Relationship import com.susu.data.remote.model.request.CategoryRequest @@ -88,4 +89,10 @@ class EnvelopesRepositoryImpl @Inject constructor( size = size, sort = sort, ).getOrThrow().toModel() + + override suspend fun getEnvelopeDetail( + id: Long + ): EnvelopeDetail = envelopesService.getEnvelopeDetail( + id = id + ).getOrThrow().toModel() } diff --git a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt index eb730fbf..a1ee90ca 100644 --- a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt +++ b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt @@ -3,12 +3,14 @@ package com.susu.data.remote.api import com.susu.data.remote.model.response.EnvelopesListResponse import com.susu.data.remote.retrofit.ApiResult import com.susu.data.remote.model.request.EnvelopeRequest +import com.susu.data.remote.model.response.EnvelopeDetailResponse import com.susu.data.remote.model.response.EnvelopeResponse import com.susu.data.remote.model.response.EnvelopesHistoryListResponse import com.susu.data.remote.model.response.RelationShipListResponse import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST +import retrofit2.http.Path import retrofit2.http.Query interface EnvelopesService { @@ -42,4 +44,9 @@ interface EnvelopesService { @Query("size") size: Int?, @Query("sort") sort: String?, ): ApiResult + + @GET("envelopes/{id}") + suspend fun getEnvelopeDetail( + @Path("id") id: Long, + ): ApiResult } diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeDetailResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeDetailResponse.kt new file mode 100644 index 00000000..3ce344a3 --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeDetailResponse.kt @@ -0,0 +1,35 @@ +package com.susu.data.remote.model.response + +import com.susu.core.model.EnvelopeDetail +import com.susu.core.model.FriendRelationship +import kotlinx.serialization.Serializable + +@Serializable +data class EnvelopeDetailResponse( + val envelope: EnvelopeInfo, + val category: CategoryInfo, + val relationship: RelationshipInfo, + val friendRelationship: FriendRelationshipInfo, + val friend: FriendInfo, +) + +@Serializable +data class FriendRelationshipInfo( + val id: Long, + val friendId: Long, + val relationshipId: Long, +) + +internal fun FriendRelationshipInfo.toModel() = FriendRelationship( + id = id, + friendId = friendId, + relationshipId = relationshipId, +) + +internal fun EnvelopeDetailResponse.toModel() = EnvelopeDetail( + envelope = envelope.toModel(), + category = category.toModel(), + relationship = relationship.toModel(), + friendRelationship = friendRelationship.toModel(), + friend = friend.toModel(), +) diff --git a/data/src/main/java/com/susu/data/remote/model/response/FriendInfo.kt b/data/src/main/java/com/susu/data/remote/model/response/FriendInfo.kt deleted file mode 100644 index f75f465e..00000000 --- a/data/src/main/java/com/susu/data/remote/model/response/FriendInfo.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.susu.data.remote.model.response - -import com.susu.core.model.Friend -import kotlinx.serialization.Serializable - -@Serializable -data class FriendInfo( - val id: Long, - val name: String, - val phoneNumber: String = "", -) - -internal fun FriendInfo.toModel() = Friend( - id = id, - name = name, - phoneNumber = phoneNumber, -) diff --git a/data/src/main/java/com/susu/data/remote/model/response/FriendStatisticsResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/FriendStatisticsResponse.kt index c757c548..da61149b 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/FriendStatisticsResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/FriendStatisticsResponse.kt @@ -1,5 +1,6 @@ package com.susu.data.remote.model.response +import com.susu.core.model.Friend import com.susu.core.model.FriendStatistics import kotlinx.serialization.Serializable @@ -11,6 +12,19 @@ data class FriendStatisticsResponse( val totalAmounts: Int, ) +@Serializable +data class FriendInfo( + val id: Long, + val name: String, + val phoneNumber: String = "", +) + +internal fun FriendInfo.toModel() = Friend( + id = id, + name = name, + phoneNumber = phoneNumber, +) + internal fun FriendStatisticsResponse.toModel() = FriendStatistics( friend = friend.toModel(), receivedAmounts = receivedAmounts, diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index b523afe9..a472d3ed 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -1,10 +1,9 @@ package com.susu.domain.repository -import com.susu.core.model.Category import com.susu.core.model.FriendStatistics import com.susu.core.model.Envelope +import com.susu.core.model.EnvelopeDetail import com.susu.core.model.EnvelopeSearch -import com.susu.core.model.Friend import com.susu.core.model.Relationship import kotlinx.datetime.LocalDateTime @@ -44,4 +43,8 @@ interface EnvelopesRepository { size: Int?, sort: String?, ): List + + suspend fun getEnvelopeDetail( + id: Long, + ): EnvelopeDetail } diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopeDetailUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopeDetailUseCase.kt new file mode 100644 index 00000000..51d807cd --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/GetEnvelopeDetailUseCase.kt @@ -0,0 +1,13 @@ +package com.susu.domain.usecase.envelope + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.domain.repository.EnvelopesRepository +import javax.inject.Inject + +class GetEnvelopeDetailUseCase @Inject constructor( + private val envelopesRepository: EnvelopesRepository, +) { + suspend operator fun invoke(id: Long) = runCatchingIgnoreCancelled { + envelopesRepository.getEnvelopeDetail(id = id) + } +} diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt index bf14cadf..b3f52a29 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt @@ -59,7 +59,7 @@ internal class MainNavigator( ReceivedRoute.envelopeDetailRoute, ReceivedRoute.envelopeEditRoute, SentRoute.sentEnvelopeRoute("{${SentRoute.FRIEND_ID_ARGUMENT_NAME}}"), - SentRoute.sentEnvelopeDetailRoute, + SentRoute.sentEnvelopeDetailRoute("{${SentRoute.ENVELOPE_ID_ARGUMENT_NAME}}"), SentRoute.sentEnvelopeEditRoute, CommunityRoute.route, CommunityRoute.voteAddRoute, @@ -99,8 +99,8 @@ internal class MainNavigator( navController.navigateSentEnvelope(id) } - fun navigateSentEnvelopeDetail() { - navController.navigateSentEnvelopeDetail() + fun navigateSentEnvelopeDetail(id: Long) { + navController.navigateSentEnvelopeDetail(id) } fun navigateSentEnvelopeEdit() { diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt index 10f04596..04cf497b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt @@ -14,6 +14,6 @@ data class SentEnvelopeState( ) : UiState sealed interface SentEnvelopeSideEffect : SideEffect { - data object NavigateEnvelopeDetail : SentEnvelopeSideEffect + data class NavigateEnvelopeDetail(val id: Long) : SentEnvelopeSideEffect data object PopBackStack : SentEnvelopeSideEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt index 988b490b..ea5bc98a 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt @@ -50,7 +50,7 @@ import com.susu.feature.sent.R fun SentEnvelopeRoute( viewModel: SentEnvelopeViewModel = hiltViewModel(), popBackStack: () -> Unit, - navigateSentEnvelopeDetail: () -> Unit, + navigateSentEnvelopeDetail: (Long) -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value val historyListState = rememberLazyListState() @@ -58,7 +58,7 @@ fun SentEnvelopeRoute( viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeSideEffect.PopBackStack -> popBackStack() - else -> {} + is SentEnvelopeSideEffect.NavigateEnvelopeDetail -> navigateSentEnvelopeDetail(sideEffect.id) } } @@ -73,7 +73,7 @@ fun SentEnvelopeRoute( SentEnvelopeScreen( uiState = uiState, onClickBackIcon = viewModel::popBackStack, - onClickEnvelopeDetail = navigateSentEnvelopeDetail, + onClickEnvelopeDetail = viewModel::navigateSentEnvelopeDetail, ) } @@ -85,7 +85,7 @@ fun SentEnvelopeScreen( onClickBackIcon: () -> Unit = {}, onClickSearchIcon: () -> Unit = {}, onClickNotificationIcon: () -> Unit = {}, - onClickEnvelopeDetail: () -> Unit = {}, + onClickEnvelopeDetail: (Long) -> Unit = {}, ) { Box( modifier = modifier @@ -184,7 +184,7 @@ fun SentEnvelopeScreen( event = it.category!!.category, date = it.envelope.handedOverAt!!, money = it.envelope.amount, - onClick = onClickEnvelopeDetail, + onClick = { onClickEnvelopeDetail(it.envelope.id) }, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt index a64b6816..3234835c 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt @@ -57,7 +57,6 @@ class SentEnvelopeViewModel @Inject constructor( } } - // TODO: 봉투 id 값 넘겨주기 - fun navigateSentEnvelopeDetail() = postSideEffect(SentEnvelopeSideEffect.NavigateEnvelopeDetail) + fun navigateSentEnvelopeDetail(id: Long) = postSideEffect(SentEnvelopeSideEffect.NavigateEnvelopeDetail(id = id)) fun popBackStack() = postSideEffect(SentEnvelopeSideEffect.PopBackStack) } diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/component/EnvelopeHistoryItem.kt b/feature/sent/src/main/java/com/susu/feature/envelope/component/EnvelopeHistoryItem.kt index eed401d8..6600aa2a 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/component/EnvelopeHistoryItem.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/component/EnvelopeHistoryItem.kt @@ -83,7 +83,7 @@ fun EnvelopeHistoryItem( } } -@Preview +@Preview(showBackground = true, backgroundColor = 0xffffff) @Composable fun EnvelopeHistoryItem() { SusuTheme { diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt index 3ba25034..585ca58d 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt @@ -5,8 +5,18 @@ import com.susu.core.ui.base.UiState data class SentEnvelopeDetailState( val isLoading: Boolean = false, + val money: Int = 0, + val event: String = "", + val name: String = "", + val relationship: String = "", + val date: String = "", + val visited: Boolean? = null, + val gift: String = "", + val phoneNumber: String = "", + val memo: String = "", ) : UiState -sealed interface SentEnvelopeDetailSideEffect : SideEffect { - data object PopBackStack : SentEnvelopeDetailSideEffect +sealed interface SentEnvelopeDetailEffect : SideEffect { + data object NavigateEnvelopeEdit : SentEnvelopeDetailEffect + data object PopBackStack : SentEnvelopeDetailEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt index c068cfea..9cd9e531 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt @@ -11,9 +11,12 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon import com.susu.core.designsystem.component.appbar.icon.DeleteText @@ -21,7 +24,9 @@ import com.susu.core.designsystem.component.appbar.icon.EditText import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.extension.collectWithLifecycle +import com.susu.core.ui.extension.toMoneyFormat import com.susu.feature.envelopedetail.component.DetailItem +import com.susu.feature.sent.R @Composable fun SentEnvelopeDetailRoute( @@ -29,20 +34,29 @@ fun SentEnvelopeDetailRoute( popBackStack: () -> Unit, navigateSentEnvelopeEdit: () -> Unit, ) { + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { - SentEnvelopeDetailSideEffect.PopBackStack -> popBackStack() + SentEnvelopeDetailEffect.PopBackStack -> popBackStack() + is SentEnvelopeDetailEffect.NavigateEnvelopeEdit -> navigateSentEnvelopeEdit } } + LaunchedEffect(key1 = Unit) { + viewModel.getEnvelopeDetail() + } + SentEnvelopeDetailScreen( + uiState = uiState, onClickBackIcon = viewModel::popBackStack, - onClickEdit = navigateSentEnvelopeEdit, + onClickEdit = viewModel::navigateSentEnvelopeEdit, ) } @Composable fun SentEnvelopeDetailScreen( + uiState: SentEnvelopeDetailState = SentEnvelopeDetailState(), modifier: Modifier = Modifier, onClickBackIcon: () -> Unit = {}, onClickEdit: () -> Unit = {}, @@ -72,7 +86,7 @@ fun SentEnvelopeDetailScreen( Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_m)) }, ) - // TODO: text 수정 + Column( modifier = modifier .fillMaxSize() @@ -84,51 +98,52 @@ fun SentEnvelopeDetailScreen( .verticalScroll(scrollState), ) { Text( - text = "150,000원", + text = uiState.money.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxl, color = Gray100, ) Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_m)) Column { DetailItem( - categoryText = "경조사", - contentText = "결혼식", - isEmptyContent = false, + categoryText = stringResource(R.string.sent_envelope_detail_category_event), + contentText = uiState.event, + isEmptyContent = uiState.event.isEmpty(), ) DetailItem( - categoryText = "이름", - contentText = "김철수", - isEmptyContent = false, + categoryText = stringResource(R.string.sent_envelope_detail_category_name), + contentText = uiState.name, + isEmptyContent = uiState.name.isEmpty(), ) DetailItem( - categoryText = "나와의 관계", - contentText = "친구", - isEmptyContent = false, + categoryText = stringResource(R.string.sent_envelope_detail_category_relationship), + contentText = uiState.relationship, + isEmptyContent = uiState.relationship.isEmpty(), ) DetailItem( - categoryText = "날짜", - contentText = "2023년 11월 25일", - isEmptyContent = false, + categoryText = stringResource(R.string.sent_envelope_detail_category_date), + contentText = uiState.date, + isEmptyContent = uiState.date.isEmpty(), ) DetailItem( - categoryText = "방문 여부", - contentText = "예", - isEmptyContent = false, + categoryText = stringResource(R.string.sent_envelope_detail_category_visited), + contentText = if (uiState.visited == true) stringResource(R.string.sent_envelope_detail_category_visited_yes) + else stringResource(R.string.sent_envelope_detail_category_visited_no), + isEmptyContent = uiState.visited == null, ) DetailItem( - categoryText = "선물", - contentText = "한끼 식사", - isEmptyContent = true, + categoryText = stringResource(R.string.sent_envelope_detail_category_gift), + contentText = uiState.gift, + isEmptyContent = uiState.gift.isEmpty(), ) DetailItem( - categoryText = "연락처", - contentText = "01012345678", - isEmptyContent = true, + categoryText = stringResource(R.string.sent_envelope_detail_category_phone), + contentText = uiState.phoneNumber, + isEmptyContent = uiState.phoneNumber.isEmpty(), ) DetailItem( - categoryText = "메모", - contentText = "가나다라마바사아자차카타파하가나다라마바사아자차카타파하가나다라마바사아자차카타파하가나다라마바사아자차카타파하가나다라마바사아자차카타파하", - isEmptyContent = true, + categoryText = stringResource(R.string.sent_envelope_detail_category_memo), + contentText = uiState.memo, + isEmptyContent = uiState.memo.isEmpty(), ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt index 8636ef99..2e216bda 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt @@ -1,12 +1,50 @@ package com.susu.feature.envelopedetail +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope import com.susu.core.ui.base.BaseViewModel +import com.susu.core.ui.util.to_yyyy_dot_MM_dot_dd +import com.susu.domain.usecase.envelope.GetEnvelopeDetailUseCase +import com.susu.feature.sent.navigation.SentRoute import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class SentEnvelopeDetailViewModel @Inject constructor() : BaseViewModel( +class SentEnvelopeDetailViewModel @Inject constructor( + private val getEnvelopeDetailUseCase: GetEnvelopeDetailUseCase, + savedStateHandle: SavedStateHandle, +) : BaseViewModel( SentEnvelopeDetailState(), ) { - fun popBackStack() = postSideEffect(SentEnvelopeDetailSideEffect.PopBackStack) + private val envelopeId = savedStateHandle.get(SentRoute.ENVELOPE_ID_ARGUMENT_NAME)!! + fun getEnvelopeDetail(id: Long = envelopeId) = viewModelScope.launch { + getEnvelopeDetailUseCase(id).onSuccess { envelopeDetail -> + val date = envelopeDetail.envelope.handedOverAt!!.to_yyyy_dot_MM_dot_dd().split(".") + + intent { + copy( + money = envelopeDetail.envelope.amount.toInt(), + event = envelopeDetail.category.category, + name = envelopeDetail.friend.name, + relationship = envelopeDetail.relationship.relation, + date = date.joinToString(" ") { section -> + when (date.indexOf(section)) { + 0 -> "${section}년" + 1 -> "${section}월" + 2 -> "${section}일" + else -> "" + } + }, + visited = envelopeDetail.envelope.hasVisited, + gift = envelopeDetail.envelope.gift!!, + phoneNumber = envelopeDetail.friend.phoneNumber, + memo = envelopeDetail.envelope.memo!!, + ) + } + } + } + + fun navigateSentEnvelopeEdit() = postSideEffect(SentEnvelopeDetailEffect.NavigateEnvelopeEdit) + fun popBackStack() = postSideEffect(SentEnvelopeDetailEffect.PopBackStack) } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt index 966772fa..6a97855d 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt @@ -7,6 +7,7 @@ data class SentEnvelopeEditState( val isLoading: Boolean = false, ) : UiState -sealed interface SentEnvelopeEditSideEffect : SideEffect { - data object PopBackStack : SentEnvelopeEditSideEffect +sealed interface SentEnvelopeEditEffect : SideEffect { +// data class NavigateEnvelope(val id: Long) : SentEnvelopeEditEffect + data object PopBackStack : SentEnvelopeEditEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 7c6e24a9..f0751c7a 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -51,11 +51,12 @@ import com.susu.feature.sent.R fun SentEnvelopeEditRoute( viewModel: SentEnvelopeEditViewModel = hiltViewModel(), popBackStack: () -> Unit, - navigateSentEnvelopeDetail: () -> Unit, + navigateSentEnvelopeDetail: (Long) -> Unit, ) { viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { - SentEnvelopeEditSideEffect.PopBackStack -> popBackStack() + SentEnvelopeEditEffect.PopBackStack -> popBackStack() + else -> {} } } @@ -70,7 +71,7 @@ fun SentEnvelopeEditRoute( fun SentEnvelopeEditScreen( modifier: Modifier = Modifier, onClickBackIcon: () -> Unit = {}, - onClickSave: () -> Unit = {}, + onClickSave: (Long) -> Unit = {}, ) { // TODO: 수정 필요 var money by remember { mutableStateOf(150000) } @@ -287,7 +288,7 @@ fun SentEnvelopeEditScreen( style = MediumButtonStyle.height60, shape = RectangleShape, text = stringResource(R.string.sent_envelope_edit_save), - onClick = onClickSave, +// onClick = { onClickSave() }, // TODO: 친구 id 값 넣어주기 ) } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index 63d1dbe8..4f3c76ae 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -5,8 +5,8 @@ import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject @HiltViewModel -class SentEnvelopeEditViewModel @Inject constructor() : BaseViewModel( +class SentEnvelopeEditViewModel @Inject constructor() : BaseViewModel( SentEnvelopeEditState(), ) { - fun popBackStack() = postSideEffect(SentEnvelopeEditSideEffect.PopBackStack) + fun popBackStack() = postSideEffect(SentEnvelopeEditEffect.PopBackStack) } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt index 9c9445b4..fb661f4a 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt @@ -21,8 +21,8 @@ fun NavController.navigateSentEnvelope(id: Long) { navigate(SentRoute.sentEnvelopeRoute(id = id.toString())) } -fun NavController.navigateSentEnvelopeDetail() { - navigate(SentRoute.sentEnvelopeDetailRoute) +fun NavController.navigateSentEnvelopeDetail(id: Long) { + navigate(SentRoute.sentEnvelopeDetailRoute(id = id.toString())) } fun NavController.navigateSentEnvelopeEdit() { @@ -37,7 +37,7 @@ fun NavGraphBuilder.sentNavGraph( padding: PaddingValues, popBackStack: () -> Unit, navigateSentEnvelope: (Long) -> Unit, - navigateSentEnvelopeDetail: () -> Unit, + navigateSentEnvelopeDetail: (Long) -> Unit, navigateSentEnvelopeEdit: () -> Unit, navigateSentEnvelopeAdd: () -> Unit, ) { @@ -63,7 +63,14 @@ fun NavGraphBuilder.sentNavGraph( ) } - composable(route = SentRoute.sentEnvelopeDetailRoute) { + composable( + route = SentRoute.sentEnvelopeDetailRoute("{${SentRoute.ENVELOPE_ID_ARGUMENT_NAME}}"), + arguments = listOf( + navArgument(SentRoute.ENVELOPE_ID_ARGUMENT_NAME) { + type = NavType.LongType + } + ) + ) { SentEnvelopeDetailRoute( popBackStack = popBackStack, navigateSentEnvelopeEdit = navigateSentEnvelopeEdit, @@ -86,10 +93,12 @@ fun NavGraphBuilder.sentNavGraph( object SentRoute { const val route = "sent" - const val sentEnvelopeDetailRoute = "sent-envelope-detail" const val sentEnvelopeEditRoute = "sent-envelope-edit" const val sentEnvelopeAddRoute = "sent-envelope-add" fun sentEnvelopeRoute(id: String) = "sent-envelope/$id" const val FRIEND_ID_ARGUMENT_NAME = "sent-envelope-id" + + fun sentEnvelopeDetailRoute(id: String) = "sent-envelope-detail/$id" + const val ENVELOPE_ID_ARGUMENT_NAME = "sent-envelope-detail-id" } diff --git a/feature/sent/src/main/res/values/strings.xml b/feature/sent/src/main/res/values/strings.xml index 2e7bbdcf..9e2d2b51 100644 --- a/feature/sent/src/main/res/values/strings.xml +++ b/feature/sent/src/main/res/values/strings.xml @@ -55,4 +55,14 @@ "을 " "전체 " + 경조사 + 이름 + 나와의 관계 + 날짜 + 방문 여부 + 선물 + 연락처 + 메모 + + 아니요 From 0ef42fac45601e323ce141f7a9fd2b1dfb6c27fa Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sun, 4 Feb 2024 04:32:24 +0900 Subject: [PATCH 77/95] =?UTF-8?q?chore:=20response=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/response/EnvelopeHistoryResponse.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 data/src/main/java/com/susu/data/remote/model/response/EnvelopeHistoryResponse.kt diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeHistoryResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeHistoryResponse.kt new file mode 100644 index 00000000..b160dd44 --- /dev/null +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeHistoryResponse.kt @@ -0,0 +1,25 @@ +package com.susu.data.remote.model.response + +import com.susu.core.model.EnvelopeSearch +import com.susu.core.model.Relationship +import kotlinx.serialization.Serializable + +@Serializable +data class EnvelopeHistoryResponse( + val envelope: EnvelopeInfo, + val category: CategoryInfo? = null, + val friend: FriendInfo? = null, + val relation: RelationshipInfo? = null, +) + +internal fun RelationshipInfo.toModel() = Relationship( + id = id, + relation = relation, +) + +internal fun EnvelopeHistoryResponse.toModel() = EnvelopeSearch( + envelope = envelope.toModel(), + category = category?.toModel(), + friend = friend?.toModel(), + relationship = relation?.toModel(), +) From c826800510cc5f72fe9c4e726d4f765008c88497 Mon Sep 17 00:00:00 2001 From: syb8200 Date: Sun, 4 Feb 2024 13:08:50 +0900 Subject: [PATCH 78/95] =?UTF-8?q?chore:=20conflicts=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote/model/response/EnvelopeDetailResponse.kt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeDetailResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeDetailResponse.kt index 3ce344a3..4fb9fd84 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopeDetailResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopeDetailResponse.kt @@ -9,18 +9,11 @@ data class EnvelopeDetailResponse( val envelope: EnvelopeInfo, val category: CategoryInfo, val relationship: RelationshipInfo, - val friendRelationship: FriendRelationshipInfo, + val friendRelationship: FriendRelationShipInfo, val friend: FriendInfo, ) -@Serializable -data class FriendRelationshipInfo( - val id: Long, - val friendId: Long, - val relationshipId: Long, -) - -internal fun FriendRelationshipInfo.toModel() = FriendRelationship( +internal fun FriendRelationShipInfo.toModel() = FriendRelationship( id = id, friendId = friendId, relationshipId = relationshipId, From 72a4cc59d671168f74aad6c671afc5ac27707b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A7=84=EC=9A=B1?= Date: Sun, 4 Feb 2024 23:33:05 +0900 Subject: [PATCH 79/95] Update feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 양수진 <69582122+yangsooplus@users.noreply.github.com> --- .../com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt index 9cd9e531..6deddf1e 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt @@ -39,7 +39,7 @@ fun SentEnvelopeDetailRoute( viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeDetailEffect.PopBackStack -> popBackStack() - is SentEnvelopeDetailEffect.NavigateEnvelopeEdit -> navigateSentEnvelopeEdit + is SentEnvelopeDetailEffect.NavigateEnvelopeEdit -> navigateSentEnvelopeEdit() } } From 0eb2ca8628419670af5c4e92d4c9666f7b21915a Mon Sep 17 00:00:00 2001 From: jinukeu Date: Sun, 4 Feb 2024 23:35:36 +0900 Subject: [PATCH 80/95] =?UTF-8?q?feat:=20getEnvelopesListUseCase=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=80=20=EB=B4=89=ED=88=AC=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B2=BD=EC=9A=B0=EC=9D=98=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../susu/feature/envelope/SentEnvelopeContract.kt | 2 +- .../susu/feature/envelope/SentEnvelopeScreen.kt | 14 +++++++------- .../susu/feature/envelope/SentEnvelopeViewModel.kt | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt index 04cf497b..54ae4e94 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeContract.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.persistentListOf data class SentEnvelopeState( val isLoading: Boolean = false, - val envelopeInfo: PersistentList = persistentListOf(FriendStatistics()), + val envelopeInfo: FriendStatistics = FriendStatistics(), val envelopeHistoryList: PersistentList = persistentListOf(), ) : UiState diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt index b7358c74..cec09a51 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt @@ -80,8 +80,8 @@ fun SentEnvelopeRoute( @Composable fun SentEnvelopeScreen( - uiState: SentEnvelopeState = SentEnvelopeState(), modifier: Modifier = Modifier, + uiState: SentEnvelopeState = SentEnvelopeState(), historyListState: LazyListState = rememberLazyListState(), onClickBackIcon: () -> Unit = {}, onClickSearchIcon: () -> Unit = {}, @@ -98,7 +98,7 @@ fun SentEnvelopeScreen( leftIcon = { BackIcon(onClickBackIcon) }, - title = uiState.envelopeInfo.last().friend.name, + title = uiState.envelopeInfo.friend.name, actions = { SearchIcon(onClickSearchIcon) NotificationIcon(onClickNotificationIcon) @@ -113,7 +113,7 @@ fun SentEnvelopeScreen( ), ) { Text( - text = stringResource(R.string.sent_envelope_card_monee_total) + uiState.envelopeInfo.last().totalAmounts.toMoneyFormat() + + text = stringResource(R.string.sent_envelope_card_monee_total) + uiState.envelopeInfo.totalAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_m, color = Gray100, @@ -121,7 +121,7 @@ fun SentEnvelopeScreen( Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_xxs)) SusuBadge( color = BadgeColor.Gray30, - text = (uiState.envelopeInfo.last().receivedAmounts - uiState.envelopeInfo.last().sentAmounts).toMoneyFormat() + + text = (uiState.envelopeInfo.receivedAmounts - uiState.envelopeInfo.sentAmounts).toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), padding = BadgeStyle.smallBadge, ) @@ -142,7 +142,7 @@ fun SentEnvelopeScreen( ) } LinearProgressIndicator( - progress = { uiState.envelopeInfo.last().sentAmounts.toFloat() / uiState.envelopeInfo.last().totalAmounts }, + progress = { uiState.envelopeInfo.sentAmounts.toFloat() / uiState.envelopeInfo.totalAmounts }, color = SusuTheme.colorScheme.primary, trackColor = Orange20, strokeCap = StrokeCap.Round, @@ -155,12 +155,12 @@ fun SentEnvelopeScreen( horizontalArrangement = Arrangement.SpaceBetween, ) { Text( - text = uiState.envelopeInfo.last().sentAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), + text = uiState.envelopeInfo.sentAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxxxs, color = Gray90, ) Text( - text = uiState.envelopeInfo.last().receivedAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), + text = uiState.envelopeInfo.receivedAmounts.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), style = SusuTheme.typography.title_xxxxs, color = Gray60, ) diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt index 3234835c..50a07776 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeViewModel.kt @@ -26,13 +26,13 @@ class SentEnvelopeViewModel @Inject constructor( getEnvelopeHistoryList() } - fun getEnvelopeInfo(id: Long = friendId) = viewModelScope.launch { + private fun getEnvelopeInfo(id: Long = friendId) = viewModelScope.launch { val friendsList: List = listOf(id) getEnvelopesListUseCase( GetEnvelopesListUseCase.Param(friendIds = friendsList), ).onSuccess { envelope -> - val envelopeInfo = currentState.envelopeInfo.plus(envelope).toPersistentList() + val envelopeInfo = envelope.getOrNull(0) ?: return@launch intent { copy( envelopeInfo = envelopeInfo, From 02e0d96378a4080f5ae49eed190ae2521dcc2f35 Mon Sep 17 00:00:00 2001 From: jinukeu Date: Sun, 4 Feb 2024 23:37:51 +0900 Subject: [PATCH 81/95] chore: ktlint, detekt --- .../data/data/repository/EnvelopesRepositoryImpl.kt | 4 ++-- .../com/susu/data/remote/api/EnvelopesService.kt | 4 ++-- .../model/response/EnvelopesHistoryListResponse.kt | 2 +- .../remote/model/response/EnvelopesListResponse.kt | 2 +- .../remote/model/response/SearchEnvelopeResponse.kt | 2 +- .../susu/domain/repository/EnvelopesRepository.kt | 2 +- .../com/susu/feature/envelope/SentEnvelopeScreen.kt | 2 +- .../envelopedetail/SentEnvelopeDetailScreen.kt | 7 +++++-- .../feature/envelopeedit/SentEnvelopeEditScreen.kt | 1 + .../main/java/com/susu/feature/sent/SentScreen.kt | 2 +- .../main/java/com/susu/feature/sent/SentViewModel.kt | 12 ++++++++---- .../java/com/susu/feature/sent/component/SentCard.kt | 2 +- .../susu/feature/sent/navigation/SentNavigation.kt | 4 ++-- 13 files changed, 27 insertions(+), 19 deletions(-) diff --git a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt index a8939922..3ba030dc 100644 --- a/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt +++ b/data/src/main/java/com/susu/data/data/repository/EnvelopesRepositoryImpl.kt @@ -1,12 +1,12 @@ package com.susu.data.data.repository -import com.susu.core.model.FriendStatistics -import com.susu.data.remote.api.EnvelopesService import com.susu.core.model.Envelope import com.susu.core.model.EnvelopeDetail import com.susu.core.model.EnvelopeSearch +import com.susu.core.model.FriendStatistics import com.susu.core.model.Relationship import com.susu.core.model.SearchEnvelope +import com.susu.data.remote.api.EnvelopesService import com.susu.data.remote.model.request.CategoryRequest import com.susu.data.remote.model.request.EnvelopeRequest import com.susu.data.remote.model.response.toModel diff --git a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt index 869accbb..26b32247 100644 --- a/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt +++ b/data/src/main/java/com/susu/data/remote/api/EnvelopesService.kt @@ -1,13 +1,13 @@ package com.susu.data.remote.api -import com.susu.data.remote.model.response.EnvelopesListResponse -import com.susu.data.remote.retrofit.ApiResult import com.susu.data.remote.model.request.EnvelopeRequest import com.susu.data.remote.model.response.EnvelopeDetailResponse import com.susu.data.remote.model.response.EnvelopeResponse import com.susu.data.remote.model.response.EnvelopesHistoryListResponse +import com.susu.data.remote.model.response.EnvelopesListResponse import com.susu.data.remote.model.response.RelationShipListResponse import com.susu.data.remote.model.response.SearchEnvelopeResponse +import com.susu.data.remote.retrofit.ApiResult import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesHistoryListResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesHistoryListResponse.kt index f27e22d2..22b59969 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesHistoryListResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesHistoryListResponse.kt @@ -11,7 +11,7 @@ data class EnvelopesHistoryListResponse( val size: Int, val sort: Sort, val totalCount: Int, - val totalPage: Int + val totalPage: Int, ) internal fun EnvelopesHistoryListResponse.toModel() = this.envelopesHistoryList.map { envelopesHistory -> diff --git a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt index 8d49e1f8..d6654e12 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/EnvelopesListResponse.kt @@ -18,7 +18,7 @@ data class EnvelopesListResponse( data class Sort( val empty: Boolean, val sorted: Boolean, - val unsorted: Boolean + val unsorted: Boolean, ) internal fun EnvelopesListResponse.toModel() = this.envelopesList.map { envelopes -> diff --git a/data/src/main/java/com/susu/data/remote/model/response/SearchEnvelopeResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/SearchEnvelopeResponse.kt index 2f2d1919..95470437 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/SearchEnvelopeResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/SearchEnvelopeResponse.kt @@ -1,8 +1,8 @@ package com.susu.data.remote.model.response import com.susu.core.model.Relationship -import kotlinx.datetime.LocalDateTime import com.susu.core.model.SearchEnvelope +import kotlinx.datetime.LocalDateTime import kotlinx.serialization.Serializable @Serializable diff --git a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt index d2c5678b..929992c2 100644 --- a/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt +++ b/domain/src/main/java/com/susu/domain/repository/EnvelopesRepository.kt @@ -1,9 +1,9 @@ package com.susu.domain.repository -import com.susu.core.model.FriendStatistics import com.susu.core.model.Envelope import com.susu.core.model.EnvelopeDetail import com.susu.core.model.EnvelopeSearch +import com.susu.core.model.FriendStatistics import com.susu.core.model.Relationship import com.susu.core.model.SearchEnvelope import kotlinx.datetime.LocalDateTime diff --git a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt index cec09a51..06839aba 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelope/SentEnvelopeScreen.kt @@ -178,7 +178,7 @@ fun SentEnvelopeScreen( ) { items( items = uiState.envelopeHistoryList, - key = { it.envelope.id } + key = { it.envelope.id }, ) { EnvelopeHistoryItem( type = it.envelope.type, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt index 6deddf1e..45a14c51 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt @@ -126,8 +126,11 @@ fun SentEnvelopeDetailScreen( ) DetailItem( categoryText = stringResource(R.string.sent_envelope_detail_category_visited), - contentText = if (uiState.visited == true) stringResource(R.string.sent_envelope_detail_category_visited_yes) - else stringResource(R.string.sent_envelope_detail_category_visited_no), + contentText = if (uiState.visited == true) { + stringResource(R.string.sent_envelope_detail_category_visited_yes) + } else { + stringResource(R.string.sent_envelope_detail_category_visited_no) + }, isEmptyContent = uiState.visited == null, ) DetailItem( diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index f0751c7a..0a5a06d0 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -71,6 +71,7 @@ fun SentEnvelopeEditRoute( fun SentEnvelopeEditScreen( modifier: Modifier = Modifier, onClickBackIcon: () -> Unit = {}, + @Suppress("detekt:UnusedParameter") onClickSave: (Long) -> Unit = {}, ) { // TODO: 수정 필요 diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt index 4f5ad535..95b15d83 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentScreen.kt @@ -140,7 +140,7 @@ fun SentScreen( padding = PaddingValues(SusuTheme.spacing.spacing_m), ) EmptyView( - onClickAddEnvelope = onClickAddEnvelope + onClickAddEnvelope = onClickAddEnvelope, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt index 853c7666..f138d45e 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/SentViewModel.kt @@ -45,10 +45,14 @@ class SentViewModel @Inject constructor( intent { copy( envelopesList = envelopesList.map { - if (it.friend.id == id) it.copy( - envelopesHistoryList = newEnvelopesHistoryList, - expand = !it.expand - ) else it + if (it.friend.id == id) { + it.copy( + envelopesHistoryList = newEnvelopesHistoryList, + expand = !it.expand, + ) + } else { + it + } }.toPersistentList(), ) } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt index 61f1913e..b0e363fb 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/component/SentCard.kt @@ -100,7 +100,7 @@ fun SentCard( .susuClickable( onClick = { onClickHistory(friend.id) - } + }, ) .rotate(degrees = degrees), ) diff --git a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt index 2c66c489..063f86a2 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt @@ -69,8 +69,8 @@ fun NavGraphBuilder.sentNavGraph( arguments = listOf( navArgument(SentRoute.ENVELOPE_ID_ARGUMENT_NAME) { type = NavType.LongType - } - ) + }, + ), ) { SentEnvelopeDetailRoute( popBackStack = popBackStack, From d3ea92c1e70bc605d670ca39f3cda40202c27d14 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Wed, 31 Jan 2024 18:09:01 +0900 Subject: [PATCH 82/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B8=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=88=98=EC=A0=95=20Usecase=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelope/EditSentEnvelopeUseCase.kt | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt new file mode 100644 index 00000000..42ad74e0 --- /dev/null +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt @@ -0,0 +1,53 @@ +package com.susu.domain.usecase.envelope + +import com.susu.core.common.runCatchingIgnoreCancelled +import com.susu.core.model.Category +import com.susu.domain.repository.EnvelopesRepository +import com.susu.domain.repository.FriendRepository +import kotlinx.datetime.LocalDateTime +import javax.inject.Inject + +class EditSentEnvelopeUseCase @Inject constructor( + private val friendRepository: FriendRepository, + private val envelopesRepository: EnvelopesRepository, +) { + suspend operator fun invoke(param: Param) = runCatchingIgnoreCancelled { + with(param) { + friendRepository.editFriend( + id = friendId, + name = friendName, + phoneNumber = phoneNumber, + relationshipId = relationshipId, + customRelation = customRelation, + ) + + envelopesRepository.editEnvelope( + id = envelopeId, + type = "SENT", + friendId = friendId, + amount = amount, + gift = gift, + memo = memo, + categoryId = category.id.toLong(), + customCategory = category.customCategory, + hasVisited = hasVisited, + handedOverAt = handedOverAt, + ) + } + } + + data class Param( + val envelopeId: Long, + val friendId: Long, + val friendName: String, + val phoneNumber: String? = null, + val relationshipId: Long, + val customRelation: String? = null, + val category: Category, + val amount: Long, + val gift: String? = null, + val memo: String? = null, + val handedOverAt: LocalDateTime, + val hasVisited: Boolean? = null, + ) +} From 6fe3a01761f6c13568fe5307f4e5e05232d2722b Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 00:53:08 +0900 Subject: [PATCH 83/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B8=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=88=98=EC=A0=95=20UI=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopeedit/SentEnvelopeEditContract.kt | 27 +- .../envelopeedit/SentEnvelopeEditScreen.kt | 248 ++++++++++-------- .../envelopeedit/SentEnvelopeEditViewModel.kt | 141 +++++++++- 3 files changed, 301 insertions(+), 115 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt index 6a97855d..ab48bc6b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt @@ -1,13 +1,34 @@ package com.susu.feature.envelopeedit +import com.susu.core.model.Category +import com.susu.core.model.Relationship import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState +import com.susu.core.ui.util.currentDate +import java.time.LocalDateTime + data class SentEnvelopeEditState( val isLoading: Boolean = false, + val categoryConfig: List = emptyList(), + val relationshipConfig: List = emptyList(), + val amount: Long = 0L, + val gift: String? = null, + val memo: String? = null, + val hasVisited: Boolean? = null, + val handedOverAt: LocalDateTime = currentDate, + val friendName: String = "", + val relationshipId: Long = 0, + val customRelationship: String? = null, + val phoneNumber: String? = null, + val categoryId: Int = 0, + val customCategory: String? = null, + val showCustomCategory: Boolean = false, + val showCustomRelationship: Boolean = false, + val showDatePickerSheet: Boolean = false, ) : UiState -sealed interface SentEnvelopeEditEffect : SideEffect { -// data class NavigateEnvelope(val id: Long) : SentEnvelopeEditEffect - data object PopBackStack : SentEnvelopeEditEffect +sealed interface SentEnvelopeEditSideEffect : SideEffect { + data object PopBackStack : SentEnvelopeEditSideEffect + data class HandleException(val throwable: Throwable, val retry: () -> Unit) : SentEnvelopeEditSideEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 0a5a06d0..79ad0307 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -15,10 +15,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.RectangleShape @@ -27,6 +24,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.susu.core.designsystem.component.appbar.SusuDefaultAppBar import com.susu.core.designsystem.component.appbar.icon.BackIcon import com.susu.core.designsystem.component.bottomsheet.datepicker.SusuDatePickerBottomSheet @@ -37,13 +35,19 @@ import com.susu.core.designsystem.component.button.SmallButtonStyle import com.susu.core.designsystem.component.button.SusuFilledButton import com.susu.core.designsystem.component.textfield.SusuBasicTextField import com.susu.core.designsystem.component.textfield.SusuPriceTextField +import com.susu.core.designsystem.component.textfieldbutton.SusuTextFieldWrapContentButton +import com.susu.core.designsystem.component.textfieldbutton.TextFieldButtonColor +import com.susu.core.designsystem.component.textfieldbutton.style.SmallTextFieldButtonStyle import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.Gray30 import com.susu.core.designsystem.theme.Gray40 import com.susu.core.designsystem.theme.Gray70 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.Category +import com.susu.core.model.Relationship import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.susuClickable +import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay import com.susu.feature.envelopeedit.component.EditDetailItem import com.susu.feature.sent.R @@ -51,18 +55,45 @@ import com.susu.feature.sent.R fun SentEnvelopeEditRoute( viewModel: SentEnvelopeEditViewModel = hiltViewModel(), popBackStack: () -> Unit, - navigateSentEnvelopeDetail: (Long) -> Unit, + navigateSentEnvelopeDetail: () -> Unit, ) { + val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { - SentEnvelopeEditEffect.PopBackStack -> popBackStack() - else -> {} + SentEnvelopeEditSideEffect.PopBackStack -> popBackStack() + is SentEnvelopeEditSideEffect.HandleException -> {} + } + } + + LaunchedEffect(key1 = Unit) { + viewModel.run { + initData() + getEnvelopConfig() } } SentEnvelopeEditScreen( + uiState = uiState, onClickBackIcon = viewModel::popBackStack, onClickSave = navigateSentEnvelopeDetail, + onMoneyUpdated = viewModel::updateAmount, + onSelectCategory = { viewModel.updateCategoryId(it.id) }, + onClickCustomCategoryAdd = viewModel::showCustomCategoryInput, + onCustomCategoryUpdated = viewModel::updateCustomCategory, + onCustomCategoryCleared = { viewModel.updateCustomCategory("") }, + onFriendNameUpdated = viewModel::updateFriendName, + onSelectRelationship = { viewModel.updateRelationshipId(it.id) }, + onClickCustomRelationshipAdd = viewModel::showCustomRelationshipInput, + onCustomRelationshipUpdated = viewModel::updateCustomRelationship, + onCustomRelationshipCleared = { viewModel.updateCustomRelationship("") }, + onClickDateText = viewModel::showDatePickerSheet, + onHasVisitedUpdated = viewModel::updateHasVisited, + onGiftUpdated = viewModel::updateGift, + onMemoUpdated = viewModel::updateMemo, + onPhoneNumberUpdated = viewModel::updatePhoneNumber, + onDateUpdated = viewModel::updateHandedOverAt, + onDatePickerSheetDismissed = viewModel::hideDatePickerSheet, ) } @@ -70,18 +101,27 @@ fun SentEnvelopeEditRoute( @Composable fun SentEnvelopeEditScreen( modifier: Modifier = Modifier, + uiState: SentEnvelopeEditState = SentEnvelopeEditState(), onClickBackIcon: () -> Unit = {}, - @Suppress("detekt:UnusedParameter") - onClickSave: (Long) -> Unit = {}, + onClickSave: () -> Unit = {}, + onMoneyUpdated: (Long) -> Unit = {}, + onSelectCategory: (Category) -> Unit = {}, + onClickCustomCategoryAdd: () -> Unit = {}, + onCustomCategoryUpdated: (String) -> Unit = {}, + onCustomCategoryCleared: () -> Unit = {}, + onFriendNameUpdated: (String) -> Unit = {}, + onSelectRelationship: (Relationship) -> Unit = {}, + onClickCustomRelationshipAdd: () -> Unit = {}, + onCustomRelationshipUpdated: (String) -> Unit = {}, + onCustomRelationshipCleared: () -> Unit = {}, + onClickDateText: () -> Unit = {}, + onHasVisitedUpdated: (Boolean) -> Unit = {}, + onGiftUpdated: (String) -> Unit = {}, + onMemoUpdated: (String) -> Unit = {}, + onPhoneNumberUpdated: (String) -> Unit = {}, + onDateUpdated: (year: Int, month: Int, day: Int) -> Unit = { _, _, _ -> }, + onDatePickerSheetDismissed: () -> Unit = {}, ) { - // TODO: 수정 필요 - var money by remember { mutableStateOf(150000) } - var name by remember { mutableStateOf("김철수") } - var present by remember { mutableStateOf("") } - var phone by remember { mutableStateOf("") } - var memo by remember { mutableStateOf("") } - var isSheetOpen by remember { mutableStateOf(false) } - Box( modifier = modifier .fillMaxSize() @@ -96,7 +136,7 @@ fun SentEnvelopeEditScreen( }, ) Column( - modifier = modifier + modifier = Modifier .verticalScroll(rememberScrollState()) .weight(1f) .padding( @@ -106,110 +146,92 @@ fun SentEnvelopeEditScreen( ), ) { SusuPriceTextField( - text = money.toString(), - onTextChange = { money = it.toInt() }, + text = uiState.amount.toString(), + onTextChange = { onMoneyUpdated(it.toLongOrNull() ?: 0L) }, textStyle = SusuTheme.typography.title_xxl, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) - Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_m)) + Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_event), categoryTextAlign = Alignment.Top, ) { - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_event_wedding), - isActive = true, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_event_first_birth), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_edit_funeral), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_edit_birthday), - isActive = false, - onClick = {}, - ) + uiState.categoryConfig.forEach { category -> + SusuFilledButton( + color = FilledButtonColor.Orange, + style = SmallButtonStyle.height32, + text = category.name, + isActive = category.id == uiState.categoryId, + onClick = { onSelectCategory(category) }, + ) + } AddConditionButton( - onClick = {}, + onClick = onClickCustomCategoryAdd, ) + if (uiState.showCustomCategory) { + SusuTextFieldWrapContentButton( + style = SmallTextFieldButtonStyle.height32, + color = TextFieldButtonColor.Orange, + text = uiState.customCategory ?: "", + onTextChange = onCustomCategoryUpdated, + onClickClearIcon = onCustomCategoryCleared, + onClickCloseIcon = { /* 무슨 동작을 해야하지?? */ }, + ) + } } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_name), categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = name, - onTextChange = { name = it }, + text = uiState.friendName, + onTextChange = onFriendNameUpdated, placeholder = stringResource(R.string.sent_envelope_edit_category_name_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_relationship), categoryTextAlign = Alignment.Top, ) { - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_relationship_friend), - isActive = true, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_relationship_family), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_relationship_relatives), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_relationship_colleague), - isActive = false, - onClick = {}, - ) + uiState.relationshipConfig.forEach { relationship -> + SusuFilledButton( + color = FilledButtonColor.Orange, + style = SmallButtonStyle.height32, + text = relationship.relation, + isActive = relationship.id == uiState.relationshipId, + onClick = { onSelectRelationship(relationship) }, + ) + } AddConditionButton( - onClick = {}, + onClick = onClickCustomRelationshipAdd, ) + if (uiState.showCustomRelationship) { + SusuTextFieldWrapContentButton( + style = SmallTextFieldButtonStyle.height32, + color = TextFieldButtonColor.Orange, + text = uiState.customRelationship ?: "", + onTextChange = onCustomRelationshipUpdated, + onClickClearIcon = onCustomRelationshipCleared, + onClickCloseIcon = { /* 무슨 동작을 해야하지?? */ }, + ) + } } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_date), categoryTextAlign = Alignment.CenterVertically, ) { Text( - text = "2023년 11월 25일", + text = uiState.handedOverAt.to_yyyy_korYear_M_korMonth_d_korDay(), style = SusuTheme.typography.title_s, color = Gray100, - modifier = modifier + modifier = Modifier .fillMaxWidth() .susuClickable( rippleEnabled = false, - onClick = { isSheetOpen = true }, + onClick = onClickDateText, ), ) } @@ -221,83 +243,89 @@ fun SentEnvelopeEditScreen( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, text = stringResource(R.string.sent_envelope_edit_category_visited_yes), - isActive = true, - onClick = {}, - modifier = modifier.weight(1f), + isActive = uiState.hasVisited == true, + onClick = { onHasVisitedUpdated(true) }, + modifier = Modifier.weight(1f), ) SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, text = stringResource(R.string.sent_envelope_edit_category_visited_no), - isActive = false, - onClick = {}, - modifier = modifier.weight(1f), + isActive = uiState.hasVisited == false, + onClick = { onHasVisitedUpdated(false) }, + modifier = Modifier.weight(1f), ) } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_present), - categoryTextColor = if (present.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (uiState.gift != null) Gray70 else Gray40, categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = present, - onTextChange = { present = it }, + text = uiState.gift ?: "", + onTextChange = onGiftUpdated, placeholder = stringResource(R.string.sent_envelope_edit_category_present_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_phone), - categoryTextColor = if (phone.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (uiState.phoneNumber != null) Gray70 else Gray40, categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( - text = phone, - onTextChange = { phone = it }, + text = uiState.phoneNumber ?: "", + onTextChange = onPhoneNumberUpdated, placeholder = stringResource(R.string.sent_envelope_edit_category_phone_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } EditDetailItem( categoryText = stringResource(R.string.sent_envelope_edit_category_memo), - categoryTextColor = if (memo.isNotEmpty()) Gray70 else Gray40, + categoryTextColor = if (uiState.memo != null) Gray70 else Gray40, categoryTextAlign = Alignment.Top, ) { SusuBasicTextField( - text = memo, - onTextChange = { memo = it }, + text = uiState.memo ?: "", + onTextChange = onMemoUpdated, placeholder = stringResource(R.string.sent_envelope_edit_category_memo_placeholder), placeholderColor = Gray30, textStyle = SusuTheme.typography.title_s, maxLines = 2, - modifier = modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth(), ) } - Spacer(modifier = modifier.size(240.dp)) + Spacer(modifier = Modifier.size(240.dp)) } SusuFilledButton( - modifier = modifier + modifier = Modifier .fillMaxWidth() .imePadding(), color = FilledButtonColor.Black, style = MediumButtonStyle.height60, shape = RectangleShape, text = stringResource(R.string.sent_envelope_edit_save), -// onClick = { onClickSave() }, // TODO: 친구 id 값 넣어주기 + onClick = onClickSave, ) } - // DatePickerBottomSheet - if (isSheetOpen) { + if (uiState.showDatePickerSheet) { SusuDatePickerBottomSheet( maximumContainerHeight = 346.dp, - onDismissRequest = { _, _, _ -> isSheetOpen = false }, + initialYear = uiState.handedOverAt.year, + initialMonth = uiState.handedOverAt.monthValue, + initialDay = uiState.handedOverAt.dayOfMonth, + onDismissRequest = { year, month, day -> + onDateUpdated(year, month, day) + onDatePickerSheetDismissed() + }, + onItemSelected = { year, month, day -> onDateUpdated(year, month, day) }, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index 4f3c76ae..eba0d666 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -1,12 +1,149 @@ package com.susu.feature.envelopeedit +import androidx.lifecycle.viewModelScope +import com.susu.core.model.Category +import com.susu.core.model.Envelope +import com.susu.core.model.Friend +import com.susu.core.model.Relationship import com.susu.core.ui.base.BaseViewModel +import com.susu.core.ui.util.currentDate +import com.susu.core.ui.util.getSafeLocalDateTime +import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase +import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import kotlinx.datetime.toJavaLocalDateTime +import kotlinx.datetime.toKotlinLocalDateTime import javax.inject.Inject @HiltViewModel -class SentEnvelopeEditViewModel @Inject constructor() : BaseViewModel( +class SentEnvelopeEditViewModel @Inject constructor( + private val getCategoryConfigUseCase: GetCategoryConfigUseCase, + private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, +) : BaseViewModel( SentEnvelopeEditState(), ) { - fun popBackStack() = postSideEffect(SentEnvelopeEditEffect.PopBackStack) + + fun initData() { + // TODO: Argument로 받은 데이터로 설정. UI 개발을 위해 임의의 데이터 사용. + val category = Category( + id = 1, + name = "결혼식", + ) + val envelope = Envelope( + amount = 150000, + hasVisited = true, + handedOverAt = currentDate.toKotlinLocalDateTime(), + friend = Friend( + name = "김철수", + ), + relationship = Relationship( + id = 1, + relation = "친구", + ), + ) + + intent { + copy( + amount = envelope.amount, + gift = envelope.gift, + memo = envelope.memo, + hasVisited = envelope.hasVisited, + handedOverAt = envelope.handedOverAt.toJavaLocalDateTime(), + friendName = envelope.friend.name, + relationshipId = envelope.relationship.id, + customRelationship = envelope.relationship.customRelation, + phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, + categoryId = category.id, + customCategory = category.customCategory, + ) + } + } + + fun getEnvelopConfig() { + viewModelScope.launch { + getRelationShipConfigListUseCase().onSuccess { + intent { copy(relationshipConfig = it.dropLast(1)) } + } + getCategoryConfigUseCase().onSuccess { + intent { copy(categoryConfig = it.dropLast(1)) } + } + } + } + + fun popBackStack() = postSideEffect(SentEnvelopeEditSideEffect.PopBackStack) + + fun updateAmount(amount: Long) { + intent { copy(amount = amount) } + } + + fun updateGift(gift: String?) { + intent { copy(gift = gift?.ifEmpty { null }) } + } + + fun updateMemo(memo: String?) { + intent { copy(memo = memo?.ifEmpty { null }) } + } + + fun updateHasVisited(hasVisited: Boolean?) { + intent { + if (hasVisited == currentState.hasVisited) { + copy(hasVisited = null) + } else { + copy(hasVisited = hasVisited) + } + } + } + + fun updateHandedOverAt(year: Int, month: Int, day: Int) { + intent { copy(handedOverAt = getSafeLocalDateTime(year = year, month = month, day = day)) } + } + + fun updateFriendName(name: String) { + intent { copy(friendName = name) } + } + + fun updateRelationshipId(relationshipId: Long) { + intent { copy(relationshipId = relationshipId) } + } + + fun updateCustomRelationship(customRelationship: String) { + intent { copy(customRelationship = customRelationship) } + } + + fun updatePhoneNumber(phoneNumber: String?) { + intent { copy(phoneNumber = phoneNumber?.ifEmpty { null }) } + } + + fun updateCategoryId(categoryId: Int) { + intent { copy(categoryId = categoryId) } + } + + fun updateCustomCategory(customCategory: String?) { + intent { copy(customCategory = customCategory) } + } + + fun showCustomCategoryInput() { + intent { copy(showCustomCategory = true) } + } + + fun hideCustomCategoryInput() { + intent { copy(showCustomCategory = false) } + } + + fun showCustomRelationshipInput() { + intent { copy(showCustomRelationship = true) } + } + + fun hideCustomRelationshipInput() { + intent { copy(showCustomRelationship = false) } + } + + fun showDatePickerSheet() { + intent { copy(showDatePickerSheet = true) } + } + + fun hideDatePickerSheet() { + intent { copy(showDatePickerSheet = false) } + } } From 487b7fc87526e5b73b98e7a1536449b616062707 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 14:23:52 +0900 Subject: [PATCH 84/95] =?UTF-8?q?feat:=20=EB=B3=B4=EB=82=B8=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=88=98=EC=A0=95=20custom=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC,=20=EA=B4=80=EA=B3=84=20=EC=9E=85=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopeedit/SentEnvelopeEditContract.kt | 5 +- .../envelopeedit/SentEnvelopeEditScreen.kt | 72 ++++++++++++++++--- .../envelopeedit/SentEnvelopeEditViewModel.kt | 34 +++++++-- 3 files changed, 95 insertions(+), 16 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt index ab48bc6b..70ece152 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt @@ -7,7 +7,6 @@ import com.susu.core.ui.base.UiState import com.susu.core.ui.util.currentDate import java.time.LocalDateTime - data class SentEnvelopeEditState( val isLoading: Boolean = false, val categoryConfig: List = emptyList(), @@ -20,9 +19,11 @@ data class SentEnvelopeEditState( val friendName: String = "", val relationshipId: Long = 0, val customRelationship: String? = null, + val customRelationshipSaved: Boolean = false, val phoneNumber: String? = null, val categoryId: Int = 0, val customCategory: String? = null, + val customCategorySaved: Boolean = false, val showCustomCategory: Boolean = false, val showCustomRelationship: Boolean = false, val showDatePickerSheet: Boolean = false, @@ -31,4 +32,6 @@ data class SentEnvelopeEditState( sealed interface SentEnvelopeEditSideEffect : SideEffect { data object PopBackStack : SentEnvelopeEditSideEffect data class HandleException(val throwable: Throwable, val retry: () -> Unit) : SentEnvelopeEditSideEffect + data object FocusCustomCategory : SentEnvelopeEditSideEffect + data object FocusCustomRelationship : SentEnvelopeEditSideEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 79ad0307..cef9ae82 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -1,5 +1,6 @@ package com.susu.feature.envelopeedit +import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -16,8 +17,11 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType @@ -50,6 +54,8 @@ import com.susu.core.ui.extension.susuClickable import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay import com.susu.feature.envelopeedit.component.EditDetailItem import com.susu.feature.sent.R +import kotlinx.coroutines.android.awaitFrame +import kotlinx.coroutines.launch @Composable fun SentEnvelopeEditRoute( @@ -58,11 +64,27 @@ fun SentEnvelopeEditRoute( navigateSentEnvelopeDetail: () -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val categoryFocusRequester = remember { FocusRequester() } + val relationshipFocusRequester = remember { FocusRequester() } + val scope = rememberCoroutineScope() viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeEditSideEffect.PopBackStack -> popBackStack() is SentEnvelopeEditSideEffect.HandleException -> {} + SentEnvelopeEditSideEffect.FocusCustomCategory -> { + scope.launch { + awaitFrame() + categoryFocusRequester.requestFocus() + } + } + + SentEnvelopeEditSideEffect.FocusCustomRelationship -> { + scope.launch { + awaitFrame() + relationshipFocusRequester.requestFocus() + } + } } } @@ -73,6 +95,10 @@ fun SentEnvelopeEditRoute( } } + BackHandler { + popBackStack() + } + SentEnvelopeEditScreen( uiState = uiState, onClickBackIcon = viewModel::popBackStack, @@ -94,6 +120,12 @@ fun SentEnvelopeEditRoute( onPhoneNumberUpdated = viewModel::updatePhoneNumber, onDateUpdated = viewModel::updateHandedOverAt, onDatePickerSheetDismissed = viewModel::hideDatePickerSheet, + categoryFocusRequester = categoryFocusRequester, + relationshipFocusRequester = relationshipFocusRequester, + onCustomCategoryClosed = viewModel::hideCustomCategoryInput, + onCustomCategoryInnerButtonClicked = viewModel::toggleCustomCategoryInputSaved, + onCustomRelationshipClosed = viewModel::hideCustomRelationshipInput, + onCustomRelationshipInnerButtonClicked = viewModel::toggleCustomRelationshipInputSaved, ) } @@ -102,6 +134,8 @@ fun SentEnvelopeEditRoute( fun SentEnvelopeEditScreen( modifier: Modifier = Modifier, uiState: SentEnvelopeEditState = SentEnvelopeEditState(), + categoryFocusRequester: FocusRequester = remember { FocusRequester() }, + relationshipFocusRequester: FocusRequester = remember { FocusRequester() }, onClickBackIcon: () -> Unit = {}, onClickSave: () -> Unit = {}, onMoneyUpdated: (Long) -> Unit = {}, @@ -109,11 +143,15 @@ fun SentEnvelopeEditScreen( onClickCustomCategoryAdd: () -> Unit = {}, onCustomCategoryUpdated: (String) -> Unit = {}, onCustomCategoryCleared: () -> Unit = {}, + onCustomCategoryClosed: () -> Unit = {}, + onCustomCategoryInnerButtonClicked: () -> Unit = {}, onFriendNameUpdated: (String) -> Unit = {}, onSelectRelationship: (Relationship) -> Unit = {}, onClickCustomRelationshipAdd: () -> Unit = {}, onCustomRelationshipUpdated: (String) -> Unit = {}, onCustomRelationshipCleared: () -> Unit = {}, + onCustomRelationshipClosed: () -> Unit = {}, + onCustomRelationshipInnerButtonClicked: () -> Unit = {}, onClickDateText: () -> Unit = {}, onHasVisitedUpdated: (Boolean) -> Unit = {}, onGiftUpdated: (String) -> Unit = {}, @@ -156,7 +194,7 @@ fun SentEnvelopeEditScreen( categoryText = stringResource(R.string.sent_envelope_edit_category_event), categoryTextAlign = Alignment.Top, ) { - uiState.categoryConfig.forEach { category -> + uiState.categoryConfig.dropLast(1).forEach { category -> SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, @@ -165,17 +203,23 @@ fun SentEnvelopeEditScreen( onClick = { onSelectCategory(category) }, ) } - AddConditionButton( - onClick = onClickCustomCategoryAdd, - ) if (uiState.showCustomCategory) { SusuTextFieldWrapContentButton( - style = SmallTextFieldButtonStyle.height32, + focusRequester = categoryFocusRequester, color = TextFieldButtonColor.Orange, + style = SmallTextFieldButtonStyle.height32, text = uiState.customCategory ?: "", + isFocused = uiState.categoryId == uiState.categoryConfig.last().id, + isSaved = uiState.customCategorySaved, onTextChange = onCustomCategoryUpdated, onClickClearIcon = onCustomCategoryCleared, - onClickCloseIcon = { /* 무슨 동작을 해야하지?? */ }, + onClickCloseIcon = onCustomCategoryClosed, + onClickFilledButton = onCustomCategoryInnerButtonClicked, + onClickButton = { onSelectCategory(uiState.categoryConfig.last()) }, + ) + } else { + AddConditionButton( + onClick = onClickCustomCategoryAdd, ) } } @@ -196,7 +240,7 @@ fun SentEnvelopeEditScreen( categoryText = stringResource(R.string.sent_envelope_edit_category_relationship), categoryTextAlign = Alignment.Top, ) { - uiState.relationshipConfig.forEach { relationship -> + uiState.relationshipConfig.dropLast(1).forEach { relationship -> SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, @@ -205,17 +249,23 @@ fun SentEnvelopeEditScreen( onClick = { onSelectRelationship(relationship) }, ) } - AddConditionButton( - onClick = onClickCustomRelationshipAdd, - ) if (uiState.showCustomRelationship) { SusuTextFieldWrapContentButton( + focusRequester = relationshipFocusRequester, style = SmallTextFieldButtonStyle.height32, color = TextFieldButtonColor.Orange, text = uiState.customRelationship ?: "", + isFocused = uiState.relationshipId == uiState.relationshipConfig.last().id, + isSaved = uiState.customRelationshipSaved, onTextChange = onCustomRelationshipUpdated, onClickClearIcon = onCustomRelationshipCleared, - onClickCloseIcon = { /* 무슨 동작을 해야하지?? */ }, + onClickCloseIcon = onCustomRelationshipClosed, + onClickFilledButton = onCustomRelationshipInnerButtonClicked, + onClickButton = { onSelectRelationship(uiState.relationshipConfig.last()) }, + ) + } else { + AddConditionButton( + onClick = onClickCustomRelationshipAdd, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index eba0d666..7f329e1b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -63,10 +63,10 @@ class SentEnvelopeEditViewModel @Inject constructor( fun getEnvelopConfig() { viewModelScope.launch { getRelationShipConfigListUseCase().onSuccess { - intent { copy(relationshipConfig = it.dropLast(1)) } + intent { copy(relationshipConfig = it) } } getCategoryConfigUseCase().onSuccess { - intent { copy(categoryConfig = it.dropLast(1)) } + intent { copy(categoryConfig = it) } } } } @@ -124,7 +124,20 @@ class SentEnvelopeEditViewModel @Inject constructor( } fun showCustomCategoryInput() { - intent { copy(showCustomCategory = true) } + intent { + copy( + showCustomCategory = true, + categoryId = categoryConfig.last().id, + customCategorySaved = false, + ) + } + postSideEffect(SentEnvelopeEditSideEffect.FocusCustomCategory) + } + + fun toggleCustomCategoryInputSaved() = intent { + copy( + customCategorySaved = !customCategorySaved, + ) } fun hideCustomCategoryInput() { @@ -132,7 +145,20 @@ class SentEnvelopeEditViewModel @Inject constructor( } fun showCustomRelationshipInput() { - intent { copy(showCustomRelationship = true) } + intent { + copy( + showCustomRelationship = true, + relationshipId = relationshipConfig.last().id, + customRelationshipSaved = false, + ) + } + postSideEffect(SentEnvelopeEditSideEffect.FocusCustomRelationship) + } + + fun toggleCustomRelationshipInputSaved() = intent { + copy( + customRelationshipSaved = !customRelationshipSaved, + ) } fun hideCustomRelationshipInput() { From 4787576de3f1f72785d0b84e856fadaa095b7360 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 14:51:35 +0900 Subject: [PATCH 85/95] =?UTF-8?q?fix:=20=EC=B4=88=EA=B8=B0=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EA=B0=80=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC/?= =?UTF-8?q?=EA=B4=80=EA=B3=84=EC=9D=BC=20=EA=B2=BD=EC=9A=B0=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopeedit/SentEnvelopeEditContract.kt | 6 +- .../envelopeedit/SentEnvelopeEditScreen.kt | 1 - .../envelopeedit/SentEnvelopeEditViewModel.kt | 61 +++++++++++-------- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt index 70ece152..4f2b69b0 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditContract.kt @@ -5,12 +5,14 @@ import com.susu.core.model.Relationship import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState import com.susu.core.ui.util.currentDate +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf import java.time.LocalDateTime data class SentEnvelopeEditState( val isLoading: Boolean = false, - val categoryConfig: List = emptyList(), - val relationshipConfig: List = emptyList(), + val categoryConfig: PersistentList = persistentListOf(), + val relationshipConfig: PersistentList = persistentListOf(), val amount: Long = 0L, val gift: String? = null, val memo: String? = null, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index cef9ae82..1330fd7d 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -91,7 +91,6 @@ fun SentEnvelopeEditRoute( LaunchedEffect(key1 = Unit) { viewModel.run { initData() - getEnvelopConfig() } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index 7f329e1b..9587d95b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -11,6 +11,7 @@ import com.susu.core.ui.util.getSafeLocalDateTime import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import kotlinx.datetime.toJavaLocalDateTime import kotlinx.datetime.toKotlinLocalDateTime @@ -27,8 +28,9 @@ class SentEnvelopeEditViewModel @Inject constructor( fun initData() { // TODO: Argument로 받은 데이터로 설정. UI 개발을 위해 임의의 데이터 사용. val category = Category( - id = 1, - name = "결혼식", + id = 5, + name = "커스텀", + customCategory = "커스텀", ) val envelope = Envelope( amount = 150000, @@ -38,35 +40,40 @@ class SentEnvelopeEditViewModel @Inject constructor( name = "김철수", ), relationship = Relationship( - id = 1, - relation = "친구", + id = 5, + relation = "나", + customRelation = "나", ), ) - intent { - copy( - amount = envelope.amount, - gift = envelope.gift, - memo = envelope.memo, - hasVisited = envelope.hasVisited, - handedOverAt = envelope.handedOverAt.toJavaLocalDateTime(), - friendName = envelope.friend.name, - relationshipId = envelope.relationship.id, - customRelationship = envelope.relationship.customRelation, - phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, - categoryId = category.id, - customCategory = category.customCategory, - ) - } - } - - fun getEnvelopConfig() { viewModelScope.launch { - getRelationShipConfigListUseCase().onSuccess { - intent { copy(relationshipConfig = it) } - } - getCategoryConfigUseCase().onSuccess { - intent { copy(categoryConfig = it) } + val relationshipConfigResult = getRelationShipConfigListUseCase() + val categoryConfigResult = getCategoryConfigUseCase() + + if (relationshipConfigResult.isSuccess && categoryConfigResult.isSuccess) { + val relationshipConfig = relationshipConfigResult.getOrDefault(emptyList()).toPersistentList() + val categoryConfig = categoryConfigResult.getOrDefault(emptyList()).toPersistentList() + intent { + copy( + categoryConfig = categoryConfig, + relationshipConfig = relationshipConfig, + amount = envelope.amount, + gift = envelope.gift, + memo = envelope.memo, + hasVisited = envelope.hasVisited, + handedOverAt = envelope.handedOverAt.toJavaLocalDateTime(), + friendName = envelope.friend.name, + relationshipId = envelope.relationship.id, + customRelationship = envelope.relationship.customRelation, + phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, + categoryId = category.id, + customCategory = category.customCategory, + showCustomCategory = category.id == categoryConfig.last().id, + customCategorySaved = category.id == categoryConfig.last().id, + showCustomRelationship = envelope.relationship.id == relationshipConfig.last().id, + customRelationshipSaved = envelope.relationship.id == relationshipConfig.last().id, + ) + } } } } From 614cef708c85851099788dcff2d011cc6baa77f6 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 15:06:00 +0900 Subject: [PATCH 86/95] =?UTF-8?q?feat:=20=EB=B0=9B=EC=9D=80=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=88=98=EC=A0=95=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelope/EditSentEnvelopeUseCase.kt | 8 ++--- .../envelopeedit/SentEnvelopeEditScreen.kt | 7 +++- .../envelopeedit/SentEnvelopeEditViewModel.kt | 34 +++++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt index 42ad74e0..fc684b8c 100644 --- a/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt @@ -1,7 +1,6 @@ package com.susu.domain.usecase.envelope import com.susu.core.common.runCatchingIgnoreCancelled -import com.susu.core.model.Category import com.susu.domain.repository.EnvelopesRepository import com.susu.domain.repository.FriendRepository import kotlinx.datetime.LocalDateTime @@ -28,8 +27,8 @@ class EditSentEnvelopeUseCase @Inject constructor( amount = amount, gift = gift, memo = memo, - categoryId = category.id.toLong(), - customCategory = category.customCategory, + categoryId = categoryId.toLong(), + customCategory = customCategory, hasVisited = hasVisited, handedOverAt = handedOverAt, ) @@ -43,7 +42,8 @@ class EditSentEnvelopeUseCase @Inject constructor( val phoneNumber: String? = null, val relationshipId: Long, val customRelation: String? = null, - val category: Category, + val categoryId: Int, + val customCategory: String? = null, val amount: Long, val gift: String? = null, val memo: String? = null, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 1330fd7d..e8547ccc 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -37,6 +37,7 @@ import com.susu.core.designsystem.component.button.FilledButtonColor import com.susu.core.designsystem.component.button.MediumButtonStyle import com.susu.core.designsystem.component.button.SmallButtonStyle import com.susu.core.designsystem.component.button.SusuFilledButton +import com.susu.core.designsystem.component.screen.LoadingScreen import com.susu.core.designsystem.component.textfield.SusuBasicTextField import com.susu.core.designsystem.component.textfield.SusuPriceTextField import com.susu.core.designsystem.component.textfieldbutton.SusuTextFieldWrapContentButton @@ -101,7 +102,7 @@ fun SentEnvelopeEditRoute( SentEnvelopeEditScreen( uiState = uiState, onClickBackIcon = viewModel::popBackStack, - onClickSave = navigateSentEnvelopeDetail, + onClickSave = viewModel::editEnvelope, onMoneyUpdated = viewModel::updateAmount, onSelectCategory = { viewModel.updateCategoryId(it.id) }, onClickCustomCategoryAdd = viewModel::showCustomCategoryInput, @@ -377,6 +378,10 @@ fun SentEnvelopeEditScreen( onItemSelected = { year, month, day -> onDateUpdated(year, month, day) }, ) } + + if (uiState.isLoading) { + LoadingScreen() + } } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index 9587d95b..f88433d4 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -9,6 +9,7 @@ import com.susu.core.ui.base.BaseViewModel import com.susu.core.ui.util.currentDate import com.susu.core.ui.util.getSafeLocalDateTime import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase +import com.susu.domain.usecase.envelope.EditSentEnvelopeUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList @@ -21,9 +22,11 @@ import javax.inject.Inject class SentEnvelopeEditViewModel @Inject constructor( private val getCategoryConfigUseCase: GetCategoryConfigUseCase, private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, + private val editSentEnvelopeUseCase: EditSentEnvelopeUseCase, ) : BaseViewModel( SentEnvelopeEditState(), ) { + private var originalEnvelope: Envelope = Envelope() fun initData() { // TODO: Argument로 받은 데이터로 설정. UI 개발을 위해 임의의 데이터 사용. @@ -45,6 +48,7 @@ class SentEnvelopeEditViewModel @Inject constructor( customRelation = "나", ), ) + originalEnvelope = envelope viewModelScope.launch { val relationshipConfigResult = getRelationShipConfigListUseCase() @@ -78,6 +82,36 @@ class SentEnvelopeEditViewModel @Inject constructor( } } + fun editEnvelope() { + viewModelScope.launch { + intent { copy(isLoading = true) } + editSentEnvelopeUseCase( + param = with(currentState) { + EditSentEnvelopeUseCase.Param( + envelopeId = originalEnvelope.id, + friendId = originalEnvelope.friend.id, + friendName = friendName, + phoneNumber = phoneNumber, + relationshipId = relationshipId, + customRelation = customRelationship, + categoryId = categoryId, + customCategory = customCategory, + amount = amount, + gift = gift, + memo = memo, + handedOverAt = handedOverAt.toKotlinLocalDateTime(), + hasVisited = hasVisited, + ) + }, + ).onSuccess { + popBackStack() + }.onFailure { + postSideEffect(SentEnvelopeEditSideEffect.HandleException(it, ::editEnvelope)) + } + intent { copy(isLoading = false) } + } + } + fun popBackStack() = postSideEffect(SentEnvelopeEditSideEffect.PopBackStack) fun updateAmount(amount: Long) { From 6e32c278f3547d10a4ff64e98268050ea119d540 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Thu, 1 Feb 2024 15:09:55 +0900 Subject: [PATCH 87/95] =?UTF-8?q?chore:=20SentEnvelopeEditScreen=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../envelopeedit/SentEnvelopeEditScreen.kt | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index e8547ccc..15b4fdb4 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -101,31 +101,31 @@ fun SentEnvelopeEditRoute( SentEnvelopeEditScreen( uiState = uiState, + categoryFocusRequester = categoryFocusRequester, + relationshipFocusRequester = relationshipFocusRequester, onClickBackIcon = viewModel::popBackStack, onClickSave = viewModel::editEnvelope, - onMoneyUpdated = viewModel::updateAmount, - onSelectCategory = { viewModel.updateCategoryId(it.id) }, onClickCustomCategoryAdd = viewModel::showCustomCategoryInput, + onClickCustomRelationshipAdd = viewModel::showCustomRelationshipInput, + onClickDateText = viewModel::showDatePickerSheet, + onClickCustomCategoryInnerButton = viewModel::toggleCustomCategoryInputSaved, + onClickCustomRelationshipInnerButton = viewModel::toggleCustomRelationshipInputSaved, + onCloseCustomCategory = viewModel::hideCustomCategoryInput, + onCloseCustomRelationship = viewModel::hideCustomRelationshipInput, + onSelectCategory = { viewModel.updateCategoryId(it.id) }, + onSelectRelationship = { viewModel.updateRelationshipId(it.id) }, + onMoneyUpdated = viewModel::updateAmount, + onFriendNameUpdated = viewModel::updateFriendName, onCustomCategoryUpdated = viewModel::updateCustomCategory, onCustomCategoryCleared = { viewModel.updateCustomCategory("") }, - onFriendNameUpdated = viewModel::updateFriendName, - onSelectRelationship = { viewModel.updateRelationshipId(it.id) }, - onClickCustomRelationshipAdd = viewModel::showCustomRelationshipInput, onCustomRelationshipUpdated = viewModel::updateCustomRelationship, onCustomRelationshipCleared = { viewModel.updateCustomRelationship("") }, - onClickDateText = viewModel::showDatePickerSheet, + onDatePickerSheetDismissed = viewModel::hideDatePickerSheet, + onDateUpdated = viewModel::updateHandedOverAt, onHasVisitedUpdated = viewModel::updateHasVisited, + onPhoneNumberUpdated = viewModel::updatePhoneNumber, onGiftUpdated = viewModel::updateGift, onMemoUpdated = viewModel::updateMemo, - onPhoneNumberUpdated = viewModel::updatePhoneNumber, - onDateUpdated = viewModel::updateHandedOverAt, - onDatePickerSheetDismissed = viewModel::hideDatePickerSheet, - categoryFocusRequester = categoryFocusRequester, - relationshipFocusRequester = relationshipFocusRequester, - onCustomCategoryClosed = viewModel::hideCustomCategoryInput, - onCustomCategoryInnerButtonClicked = viewModel::toggleCustomCategoryInputSaved, - onCustomRelationshipClosed = viewModel::hideCustomRelationshipInput, - onCustomRelationshipInnerButtonClicked = viewModel::toggleCustomRelationshipInputSaved, ) } @@ -143,15 +143,15 @@ fun SentEnvelopeEditScreen( onClickCustomCategoryAdd: () -> Unit = {}, onCustomCategoryUpdated: (String) -> Unit = {}, onCustomCategoryCleared: () -> Unit = {}, - onCustomCategoryClosed: () -> Unit = {}, - onCustomCategoryInnerButtonClicked: () -> Unit = {}, + onCloseCustomCategory: () -> Unit = {}, + onClickCustomCategoryInnerButton: () -> Unit = {}, onFriendNameUpdated: (String) -> Unit = {}, onSelectRelationship: (Relationship) -> Unit = {}, onClickCustomRelationshipAdd: () -> Unit = {}, onCustomRelationshipUpdated: (String) -> Unit = {}, onCustomRelationshipCleared: () -> Unit = {}, - onCustomRelationshipClosed: () -> Unit = {}, - onCustomRelationshipInnerButtonClicked: () -> Unit = {}, + onCloseCustomRelationship: () -> Unit = {}, + onClickCustomRelationshipInnerButton: () -> Unit = {}, onClickDateText: () -> Unit = {}, onHasVisitedUpdated: (Boolean) -> Unit = {}, onGiftUpdated: (String) -> Unit = {}, @@ -213,8 +213,8 @@ fun SentEnvelopeEditScreen( isSaved = uiState.customCategorySaved, onTextChange = onCustomCategoryUpdated, onClickClearIcon = onCustomCategoryCleared, - onClickCloseIcon = onCustomCategoryClosed, - onClickFilledButton = onCustomCategoryInnerButtonClicked, + onClickCloseIcon = onCloseCustomCategory, + onClickFilledButton = onClickCustomCategoryInnerButton, onClickButton = { onSelectCategory(uiState.categoryConfig.last()) }, ) } else { @@ -259,8 +259,8 @@ fun SentEnvelopeEditScreen( isSaved = uiState.customRelationshipSaved, onTextChange = onCustomRelationshipUpdated, onClickClearIcon = onCustomRelationshipCleared, - onClickCloseIcon = onCustomRelationshipClosed, - onClickFilledButton = onCustomRelationshipInnerButtonClicked, + onClickCloseIcon = onCloseCustomRelationship, + onClickFilledButton = onClickCustomRelationshipInnerButton, onClickButton = { onSelectRelationship(uiState.relationshipConfig.last()) }, ) } else { From 37be45e5ec7ad1e5869ce592b2427947f8886939 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Mon, 5 Feb 2024 15:27:05 +0900 Subject: [PATCH 88/95] =?UTF-8?q?chore:=20sent=20=EB=AA=A8=EB=93=88=20kotl?= =?UTF-8?q?in-serializtion=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- feature/sent/build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/feature/sent/build.gradle.kts b/feature/sent/build.gradle.kts index 32c8ef94..d9b3139b 100644 --- a/feature/sent/build.gradle.kts +++ b/feature/sent/build.gradle.kts @@ -6,3 +6,7 @@ plugins { android { namespace = "com.susu.feature.sent" } + +dependencies { + implementation(libs.kotlinx.serialization.json) +} From c646bb8ea17bf562ff752f7a4464a6f36ec963b6 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Mon, 5 Feb 2024 15:27:55 +0900 Subject: [PATCH 89/95] =?UTF-8?q?refactor:=20SentEnvelopeDetailState?= =?UTF-8?q?=EA=B0=80=20EnvelopeDetail=EC=9D=84=20=EA=B0=80=EC=A7=80?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/core/model/EnvelopeDetail.kt | 15 ++- .../com/susu/core/model/FriendRelationship.kt | 5 + .../SentEnvelopeDetailContract.kt | 11 +- .../SentEnvelopeDetailScreen.kt | 105 +++++++++--------- .../SentEnvelopeDetailViewModel.kt | 12 +- .../envelopedetail/component/DetailItem.kt | 6 +- .../envelopeedit/SentEnvelopeEditScreen.kt | 1 - .../feature/sent/navigation/SentNavigation.kt | 1 - 8 files changed, 75 insertions(+), 81 deletions(-) diff --git a/core/model/src/main/java/com/susu/core/model/EnvelopeDetail.kt b/core/model/src/main/java/com/susu/core/model/EnvelopeDetail.kt index f1e9bfee..02860b00 100644 --- a/core/model/src/main/java/com/susu/core/model/EnvelopeDetail.kt +++ b/core/model/src/main/java/com/susu/core/model/EnvelopeDetail.kt @@ -1,9 +1,14 @@ package com.susu.core.model +import androidx.compose.runtime.Stable +import kotlinx.serialization.Serializable + +@Stable +@Serializable data class EnvelopeDetail( - val envelope: Envelope, - val category: Category, - val relationship: Relationship, - val friendRelationship: FriendRelationship, - val friend: Friend, + val envelope: Envelope = Envelope(), + val category: Category = Category(), + val relationship: Relationship = Relationship(), + val friendRelationship: FriendRelationship = FriendRelationship(), + val friend: Friend = Friend(), ) diff --git a/core/model/src/main/java/com/susu/core/model/FriendRelationship.kt b/core/model/src/main/java/com/susu/core/model/FriendRelationship.kt index d81e15a4..9f9e1166 100644 --- a/core/model/src/main/java/com/susu/core/model/FriendRelationship.kt +++ b/core/model/src/main/java/com/susu/core/model/FriendRelationship.kt @@ -1,5 +1,10 @@ package com.susu.core.model +import androidx.compose.runtime.Stable +import kotlinx.serialization.Serializable + +@Stable +@Serializable data class FriendRelationship( val id: Long = 0, val friendId: Long = 0, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt index 585ca58d..360ab1b8 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt @@ -1,19 +1,12 @@ package com.susu.feature.envelopedetail +import com.susu.core.model.EnvelopeDetail import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState data class SentEnvelopeDetailState( val isLoading: Boolean = false, - val money: Int = 0, - val event: String = "", - val name: String = "", - val relationship: String = "", - val date: String = "", - val visited: Boolean? = null, - val gift: String = "", - val phoneNumber: String = "", - val memo: String = "", + val envelopeDetail: EnvelopeDetail = EnvelopeDetail(), ) : UiState sealed interface SentEnvelopeDetailEffect : SideEffect { diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt index 45a14c51..f95606d2 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt @@ -25,8 +25,10 @@ import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.toMoneyFormat +import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay import com.susu.feature.envelopedetail.component.DetailItem import com.susu.feature.sent.R +import kotlinx.datetime.toJavaLocalDateTime @Composable fun SentEnvelopeDetailRoute( @@ -56,8 +58,8 @@ fun SentEnvelopeDetailRoute( @Composable fun SentEnvelopeDetailScreen( - uiState: SentEnvelopeDetailState = SentEnvelopeDetailState(), modifier: Modifier = Modifier, + uiState: SentEnvelopeDetailState = SentEnvelopeDetailState(), onClickBackIcon: () -> Unit = {}, onClickEdit: () -> Unit = {}, onClickDelete: () -> Unit = {}, @@ -97,57 +99,58 @@ fun SentEnvelopeDetailScreen( ) .verticalScroll(scrollState), ) { - Text( - text = uiState.money.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), - style = SusuTheme.typography.title_xxl, - color = Gray100, - ) - Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_m)) - Column { - DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_event), - contentText = uiState.event, - isEmptyContent = uiState.event.isEmpty(), - ) - DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_name), - contentText = uiState.name, - isEmptyContent = uiState.name.isEmpty(), - ) - DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_relationship), - contentText = uiState.relationship, - isEmptyContent = uiState.relationship.isEmpty(), - ) - DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_date), - contentText = uiState.date, - isEmptyContent = uiState.date.isEmpty(), - ) - DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_visited), - contentText = if (uiState.visited == true) { - stringResource(R.string.sent_envelope_detail_category_visited_yes) - } else { - stringResource(R.string.sent_envelope_detail_category_visited_no) - }, - isEmptyContent = uiState.visited == null, - ) - DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_gift), - contentText = uiState.gift, - isEmptyContent = uiState.gift.isEmpty(), - ) - DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_phone), - contentText = uiState.phoneNumber, - isEmptyContent = uiState.phoneNumber.isEmpty(), - ) - DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_memo), - contentText = uiState.memo, - isEmptyContent = uiState.memo.isEmpty(), + with(uiState.envelopeDetail) { + Text( + text = envelope.amount.toMoneyFormat() + stringResource(R.string.sent_envelope_card_money_won), + style = SusuTheme.typography.title_xxl, + color = Gray100, ) + Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_m)) + Column { + DetailItem( + categoryText = stringResource(R.string.sent_envelope_detail_category_event), + contentText = category.category, + isEmptyContent = category.category.isEmpty(), + ) + DetailItem( + categoryText = stringResource(R.string.sent_envelope_detail_category_name), + contentText = friend.name, + isEmptyContent = friend.name.isEmpty(), + ) + DetailItem( + categoryText = stringResource(R.string.sent_envelope_detail_category_relationship), + contentText = relationship.relation, + isEmptyContent = relationship.relation.isEmpty(), + ) + DetailItem( + categoryText = stringResource(R.string.sent_envelope_detail_category_date), + contentText = envelope.handedOverAt.toJavaLocalDateTime().to_yyyy_korYear_M_korMonth_d_korDay(), + ) + DetailItem( + categoryText = stringResource(R.string.sent_envelope_detail_category_visited), + contentText = if (envelope.hasVisited == true) { + stringResource(R.string.sent_envelope_detail_category_visited_yes) + } else { + stringResource(R.string.sent_envelope_detail_category_visited_no) + }, + isEmptyContent = envelope.hasVisited == null, + ) + DetailItem( + categoryText = stringResource(R.string.sent_envelope_detail_category_gift), + contentText = envelope.gift ?: "", + isEmptyContent = envelope.gift.isNullOrEmpty(), + ) + DetailItem( + categoryText = stringResource(R.string.sent_envelope_detail_category_phone), + contentText = friend.phoneNumber, + isEmptyContent = friend.phoneNumber.isEmpty(), + ) + DetailItem( + categoryText = stringResource(R.string.sent_envelope_detail_category_memo), + contentText = envelope.memo ?: "", + isEmptyContent = envelope.memo.isNullOrEmpty(), + ) + } } } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt index c0607b44..52c23234 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt @@ -3,12 +3,10 @@ package com.susu.feature.envelopedetail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.ui.base.BaseViewModel -import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay import com.susu.domain.usecase.envelope.GetEnvelopeDetailUseCase import com.susu.feature.sent.navigation.SentRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch -import kotlinx.datetime.toJavaLocalDateTime import javax.inject.Inject @HiltViewModel @@ -23,15 +21,7 @@ class SentEnvelopeDetailViewModel @Inject constructor( getEnvelopeDetailUseCase(id).onSuccess { envelopeDetail -> intent { copy( - money = envelopeDetail.envelope.amount.toInt(), - event = envelopeDetail.category.category, - name = envelopeDetail.friend.name, - relationship = envelopeDetail.relationship.relation, - date = envelopeDetail.envelope.handedOverAt.toJavaLocalDateTime().to_yyyy_korYear_M_korMonth_d_korDay(), - visited = envelopeDetail.envelope.hasVisited, - gift = envelopeDetail.envelope.gift!!, - phoneNumber = envelopeDetail.friend.phoneNumber, - memo = envelopeDetail.envelope.memo!!, + envelopeDetail = envelopeDetail, ) } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/component/DetailItem.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/component/DetailItem.kt index eee9c38a..102c1982 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/component/DetailItem.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/component/DetailItem.kt @@ -22,15 +22,15 @@ import com.susu.core.designsystem.theme.SusuTheme @Composable fun DetailItem( - modifier: Modifier = Modifier, categoryText: String, + contentText: String, + modifier: Modifier = Modifier, + isEmptyContent: Boolean = false, categoryStyle: TextStyle = SusuTheme.typography.title_xxs, categoryTextColor: Color = Gray60, categoryWidth: Dp = 72.dp, - contentText: String, contentStyle: TextStyle = SusuTheme.typography.title_s, contentColor: Color = Gray100, - isEmptyContent: Boolean, padding: PaddingValues = PaddingValues(vertical = SusuTheme.spacing.spacing_m), ) { if (!isEmptyContent) { diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 15b4fdb4..0ae8c25d 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -62,7 +62,6 @@ import kotlinx.coroutines.launch fun SentEnvelopeEditRoute( viewModel: SentEnvelopeEditViewModel = hiltViewModel(), popBackStack: () -> Unit, - navigateSentEnvelopeDetail: () -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value val categoryFocusRequester = remember { FocusRequester() } diff --git a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt index 063f86a2..98672005 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt @@ -81,7 +81,6 @@ fun NavGraphBuilder.sentNavGraph( composable(route = SentRoute.sentEnvelopeEditRoute) { SentEnvelopeEditRoute( popBackStack = popBackStack, - navigateSentEnvelopeDetail = navigateSentEnvelopeDetail, ) } From 8266a1e28a28a2c9d0d62a847126bff258debf98 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Mon, 5 Feb 2024 15:42:13 +0900 Subject: [PATCH 90/95] =?UTF-8?q?feat:=20=EC=83=81=EC=84=B8=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=B4=89=ED=88=AC=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=9B=EC=95=84=20=ED=8E=B8=EC=A7=91=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../susu/feature/navigator/MainNavigator.kt | 7 +- .../SentEnvelopeDetailContract.kt | 2 +- .../SentEnvelopeDetailScreen.kt | 5 +- .../SentEnvelopeDetailViewModel.kt | 7 +- .../envelopeedit/SentEnvelopeEditViewModel.kt | 82 ++++++++----------- .../feature/sent/navigation/SentNavigation.kt | 15 ++-- 6 files changed, 57 insertions(+), 61 deletions(-) diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt index d4572615..b7729cea 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainNavigator.kt @@ -10,6 +10,7 @@ import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.Envelope +import com.susu.core.model.EnvelopeDetail import com.susu.core.model.Ledger import com.susu.core.model.Vote import com.susu.feature.community.navigation.CommunityRoute @@ -65,7 +66,7 @@ internal class MainNavigator( ReceivedRoute.envelopeEditRoute("{${ReceivedRoute.ENVELOPE_ARGUMENT_NAME}}", "{${ReceivedRoute.LEDGER_ID_ARGUMENT_NAME}}"), SentRoute.sentEnvelopeRoute("{${SentRoute.FRIEND_ID_ARGUMENT_NAME}}"), SentRoute.sentEnvelopeDetailRoute("{${SentRoute.ENVELOPE_ID_ARGUMENT_NAME}}"), - SentRoute.sentEnvelopeEditRoute, + SentRoute.sentEnvelopeEditRoute("{${SentRoute.ENVELOPE_DETAIL_ARGUMENT_NAME}}"), CommunityRoute.route, CommunityRoute.voteAddRoute, CommunityRoute.voteSearchRoute, @@ -111,8 +112,8 @@ internal class MainNavigator( navController.navigateSentEnvelopeDetail(id) } - fun navigateSentEnvelopeEdit() { - navController.navigateSentEnvelopeEdit() + fun navigateSentEnvelopeEdit(envelopeDetail: EnvelopeDetail) { + navController.navigateSentEnvelopeEdit(envelopeDetail) } fun navigateSentEnvelopeAdd() { diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt index 360ab1b8..c2aa7b1a 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt @@ -10,6 +10,6 @@ data class SentEnvelopeDetailState( ) : UiState sealed interface SentEnvelopeDetailEffect : SideEffect { - data object NavigateEnvelopeEdit : SentEnvelopeDetailEffect + data class NavigateEnvelopeEdit(val envelopeDetail: EnvelopeDetail) : SentEnvelopeDetailEffect data object PopBackStack : SentEnvelopeDetailEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt index f95606d2..6107eeb4 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt @@ -23,6 +23,7 @@ import com.susu.core.designsystem.component.appbar.icon.DeleteText import com.susu.core.designsystem.component.appbar.icon.EditText import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.SusuTheme +import com.susu.core.model.EnvelopeDetail import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.toMoneyFormat import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay @@ -34,14 +35,14 @@ import kotlinx.datetime.toJavaLocalDateTime fun SentEnvelopeDetailRoute( viewModel: SentEnvelopeDetailViewModel = hiltViewModel(), popBackStack: () -> Unit, - navigateSentEnvelopeEdit: () -> Unit, + navigateSentEnvelopeEdit: (EnvelopeDetail) -> Unit, ) { val uiState = viewModel.uiState.collectAsStateWithLifecycle().value viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeDetailEffect.PopBackStack -> popBackStack() - is SentEnvelopeDetailEffect.NavigateEnvelopeEdit -> navigateSentEnvelopeEdit() + is SentEnvelopeDetailEffect.NavigateEnvelopeEdit -> navigateSentEnvelopeEdit(sideEffect.envelopeDetail) } } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt index 52c23234..def94763 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt @@ -27,6 +27,11 @@ class SentEnvelopeDetailViewModel @Inject constructor( } } - fun navigateSentEnvelopeEdit() = postSideEffect(SentEnvelopeDetailEffect.NavigateEnvelopeEdit) + fun navigateSentEnvelopeEdit() = postSideEffect( + SentEnvelopeDetailEffect.NavigateEnvelopeEdit( + currentState.envelopeDetail, + ), + ) + fun popBackStack() = postSideEffect(SentEnvelopeDetailEffect.PopBackStack) } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index f88433d4..70002328 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -1,21 +1,21 @@ package com.susu.feature.envelopeedit +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope -import com.susu.core.model.Category -import com.susu.core.model.Envelope -import com.susu.core.model.Friend -import com.susu.core.model.Relationship +import com.susu.core.model.EnvelopeDetail import com.susu.core.ui.base.BaseViewModel -import com.susu.core.ui.util.currentDate +import com.susu.core.ui.extension.decodeFromUri import com.susu.core.ui.util.getSafeLocalDateTime import com.susu.domain.usecase.categoryconfig.GetCategoryConfigUseCase import com.susu.domain.usecase.envelope.EditSentEnvelopeUseCase import com.susu.domain.usecase.envelope.GetRelationShipConfigListUseCase +import com.susu.feature.sent.navigation.SentRoute import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import kotlinx.datetime.toJavaLocalDateTime import kotlinx.datetime.toKotlinLocalDateTime +import kotlinx.serialization.json.Json import javax.inject.Inject @HiltViewModel @@ -23,33 +23,15 @@ class SentEnvelopeEditViewModel @Inject constructor( private val getCategoryConfigUseCase: GetCategoryConfigUseCase, private val getRelationShipConfigListUseCase: GetRelationShipConfigListUseCase, private val editSentEnvelopeUseCase: EditSentEnvelopeUseCase, + savedStateHandle: SavedStateHandle, ) : BaseViewModel( SentEnvelopeEditState(), ) { - private var originalEnvelope: Envelope = Envelope() + private val envelopeDetail = Json.decodeFromUri( + savedStateHandle.get(SentRoute.ENVELOPE_DETAIL_ARGUMENT_NAME)!!, + ) fun initData() { - // TODO: Argument로 받은 데이터로 설정. UI 개발을 위해 임의의 데이터 사용. - val category = Category( - id = 5, - name = "커스텀", - customCategory = "커스텀", - ) - val envelope = Envelope( - amount = 150000, - hasVisited = true, - handedOverAt = currentDate.toKotlinLocalDateTime(), - friend = Friend( - name = "김철수", - ), - relationship = Relationship( - id = 5, - relation = "나", - customRelation = "나", - ), - ) - originalEnvelope = envelope - viewModelScope.launch { val relationshipConfigResult = getRelationShipConfigListUseCase() val categoryConfigResult = getCategoryConfigUseCase() @@ -57,26 +39,28 @@ class SentEnvelopeEditViewModel @Inject constructor( if (relationshipConfigResult.isSuccess && categoryConfigResult.isSuccess) { val relationshipConfig = relationshipConfigResult.getOrDefault(emptyList()).toPersistentList() val categoryConfig = categoryConfigResult.getOrDefault(emptyList()).toPersistentList() - intent { - copy( - categoryConfig = categoryConfig, - relationshipConfig = relationshipConfig, - amount = envelope.amount, - gift = envelope.gift, - memo = envelope.memo, - hasVisited = envelope.hasVisited, - handedOverAt = envelope.handedOverAt.toJavaLocalDateTime(), - friendName = envelope.friend.name, - relationshipId = envelope.relationship.id, - customRelationship = envelope.relationship.customRelation, - phoneNumber = envelope.friend.phoneNumber.ifEmpty { null }, - categoryId = category.id, - customCategory = category.customCategory, - showCustomCategory = category.id == categoryConfig.last().id, - customCategorySaved = category.id == categoryConfig.last().id, - showCustomRelationship = envelope.relationship.id == relationshipConfig.last().id, - customRelationshipSaved = envelope.relationship.id == relationshipConfig.last().id, - ) + with(envelopeDetail) { + intent { + copy( + categoryConfig = categoryConfig, + relationshipConfig = relationshipConfig, + amount = envelope.amount, + gift = envelope.gift, + memo = envelope.memo, + hasVisited = envelope.hasVisited, + handedOverAt = envelope.handedOverAt.toJavaLocalDateTime(), + friendName = friend.name, + relationshipId = relationship.id, + customRelationship = relationship.customRelation, + phoneNumber = friend.phoneNumber.ifEmpty { null }, + categoryId = category.id, + customCategory = category.customCategory, + showCustomCategory = category.id == categoryConfig.last().id, + customCategorySaved = category.id == categoryConfig.last().id, + showCustomRelationship = relationship.id == relationshipConfig.last().id, + customRelationshipSaved = relationship.id == relationshipConfig.last().id, + ) + } } } } @@ -88,8 +72,8 @@ class SentEnvelopeEditViewModel @Inject constructor( editSentEnvelopeUseCase( param = with(currentState) { EditSentEnvelopeUseCase.Param( - envelopeId = originalEnvelope.id, - friendId = originalEnvelope.friend.id, + envelopeId = envelopeDetail.envelope.id, + friendId = envelopeDetail.friend.id, friendName = friendName, phoneNumber = phoneNumber, relationshipId = relationshipId, diff --git a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt index 98672005..614acd05 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt @@ -7,11 +7,14 @@ import androidx.navigation.NavOptions import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument +import com.susu.core.model.EnvelopeDetail +import com.susu.core.ui.extension.encodeToUri import com.susu.feature.envelope.SentEnvelopeRoute import com.susu.feature.envelopeadd.SentEnvelopeAddRoute import com.susu.feature.envelopedetail.SentEnvelopeDetailRoute import com.susu.feature.envelopeedit.SentEnvelopeEditRoute import com.susu.feature.sent.SentRoute +import kotlinx.serialization.json.Json fun NavController.navigateSent(navOptions: NavOptions) { navigate(SentRoute.route, navOptions) @@ -25,8 +28,8 @@ fun NavController.navigateSentEnvelopeDetail(id: Long) { navigate(SentRoute.sentEnvelopeDetailRoute(id = id.toString())) } -fun NavController.navigateSentEnvelopeEdit() { - navigate(SentRoute.sentEnvelopeEditRoute) +fun NavController.navigateSentEnvelopeEdit(envelopeDetail: EnvelopeDetail) { + navigate(SentRoute.sentEnvelopeEditRoute(Json.encodeToUri(envelopeDetail))) } fun NavController.navigateSentEnvelopeAdd() { @@ -38,7 +41,7 @@ fun NavGraphBuilder.sentNavGraph( popBackStack: () -> Unit, navigateSentEnvelope: (Long) -> Unit, navigateSentEnvelopeDetail: (Long) -> Unit, - navigateSentEnvelopeEdit: () -> Unit, + navigateSentEnvelopeEdit: (EnvelopeDetail) -> Unit, navigateSentEnvelopeAdd: () -> Unit, handleException: (Throwable, () -> Unit) -> Unit, ) { @@ -78,7 +81,7 @@ fun NavGraphBuilder.sentNavGraph( ) } - composable(route = SentRoute.sentEnvelopeEditRoute) { + composable(route = SentRoute.sentEnvelopeEditRoute("{${SentRoute.ENVELOPE_DETAIL_ARGUMENT_NAME}}")) { SentEnvelopeEditRoute( popBackStack = popBackStack, ) @@ -94,7 +97,6 @@ fun NavGraphBuilder.sentNavGraph( object SentRoute { const val route = "sent" - const val sentEnvelopeEditRoute = "sent-envelope-edit" const val sentEnvelopeAddRoute = "sent-envelope-add" fun sentEnvelopeRoute(id: String) = "sent-envelope/$id" @@ -102,4 +104,7 @@ object SentRoute { fun sentEnvelopeDetailRoute(id: String) = "sent-envelope-detail/$id" const val ENVELOPE_ID_ARGUMENT_NAME = "sent-envelope-detail-id" + + fun sentEnvelopeEditRoute(envelopeDetail: String) = "sent-envelope-edit/$envelopeDetail" + const val ENVELOPE_DETAIL_ARGUMENT_NAME = "envelope-detail" } From a68ecaba364e4965bac94637a2a3e05504a29978 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Mon, 5 Feb 2024 16:08:28 +0900 Subject: [PATCH 91/95] =?UTF-8?q?feat:=20=EB=B0=9B=EC=9D=80=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=83=81=EC=84=B8=EC=97=90=20=EB=B4=89=ED=88=AC=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/susu/feature/navigator/MainScreen.kt | 2 ++ .../SentEnvelopeDetailContract.kt | 3 +++ .../SentEnvelopeDetailScreen.kt | 25 +++++++++++++++++++ .../SentEnvelopeDetailViewModel.kt | 12 +++++++++ .../feature/sent/navigation/SentNavigation.kt | 7 ++++++ feature/sent/src/main/res/values/strings.xml | 4 +++ 6 files changed, 53 insertions(+) diff --git a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt index 427e36ce..72185fc3 100644 --- a/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt +++ b/feature/navigator/src/main/java/com/susu/feature/navigator/MainScreen.kt @@ -85,6 +85,8 @@ internal fun MainScreen( navigateSentEnvelopeEdit = navigator::navigateSentEnvelopeEdit, navigateSentEnvelopeAdd = navigator::navigateSentEnvelopeAdd, handleException = viewModel::handleException, + onShowSnackbar = viewModel::onShowSnackbar, + onShowDialog = viewModel::onShowDialog, ) receivedNavGraph( diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt index c2aa7b1a..ad8a472b 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailContract.kt @@ -12,4 +12,7 @@ data class SentEnvelopeDetailState( sealed interface SentEnvelopeDetailEffect : SideEffect { data class NavigateEnvelopeEdit(val envelopeDetail: EnvelopeDetail) : SentEnvelopeDetailEffect data object PopBackStack : SentEnvelopeDetailEffect + data object ShowDeleteSuccessSnackBar : SentEnvelopeDetailEffect + data object ShowDeleteDialog : SentEnvelopeDetailEffect + data class HandleException(val throwable: Throwable, val retry: () -> Unit) : SentEnvelopeDetailEffect } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt index 6107eeb4..fad476ab 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel @@ -24,6 +25,8 @@ import com.susu.core.designsystem.component.appbar.icon.EditText import com.susu.core.designsystem.theme.Gray100 import com.susu.core.designsystem.theme.SusuTheme import com.susu.core.model.EnvelopeDetail +import com.susu.core.ui.DialogToken +import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.collectWithLifecycle import com.susu.core.ui.extension.toMoneyFormat import com.susu.core.ui.util.to_yyyy_korYear_M_korMonth_d_korDay @@ -36,13 +39,34 @@ fun SentEnvelopeDetailRoute( viewModel: SentEnvelopeDetailViewModel = hiltViewModel(), popBackStack: () -> Unit, navigateSentEnvelopeEdit: (EnvelopeDetail) -> Unit, + onShowSnackbar: (SnackbarToken) -> Unit, + onShowDialog: (DialogToken) -> Unit, + handleException: (Throwable, () -> Unit) -> Unit, ) { + val context = LocalContext.current val uiState = viewModel.uiState.collectAsStateWithLifecycle().value viewModel.sideEffect.collectWithLifecycle { sideEffect -> when (sideEffect) { SentEnvelopeDetailEffect.PopBackStack -> popBackStack() is SentEnvelopeDetailEffect.NavigateEnvelopeEdit -> navigateSentEnvelopeEdit(sideEffect.envelopeDetail) + SentEnvelopeDetailEffect.ShowDeleteDialog -> onShowDialog( + DialogToken( + title = context.getString(R.string.sent_envelope_detail_delete_title), + text = context.getString(R.string.sent_envelope_detail_delete_description), + confirmText = context.getString(com.susu.core.ui.R.string.word_delete), + dismissText = context.getString(com.susu.core.ui.R.string.word_cancel), + onConfirmRequest = viewModel::deleteEnvelope, + ), + ) + + SentEnvelopeDetailEffect.ShowDeleteSuccessSnackBar -> onShowSnackbar( + SnackbarToken( + message = context.getString(R.string.sent_envelope_detail_delete_snackbar), + ), + ) + + is SentEnvelopeDetailEffect.HandleException -> handleException(sideEffect.throwable, sideEffect.retry) } } @@ -54,6 +78,7 @@ fun SentEnvelopeDetailRoute( uiState = uiState, onClickBackIcon = viewModel::popBackStack, onClickEdit = viewModel::navigateSentEnvelopeEdit, + onClickDelete = viewModel::showDeleteDialog, ) } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt index def94763..e98e0197 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt @@ -3,6 +3,7 @@ package com.susu.feature.envelopedetail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.susu.core.ui.base.BaseViewModel +import com.susu.domain.usecase.envelope.DeleteEnvelopeUseCase import com.susu.domain.usecase.envelope.GetEnvelopeDetailUseCase import com.susu.feature.sent.navigation.SentRoute import dagger.hilt.android.lifecycle.HiltViewModel @@ -12,6 +13,7 @@ import javax.inject.Inject @HiltViewModel class SentEnvelopeDetailViewModel @Inject constructor( private val getEnvelopeDetailUseCase: GetEnvelopeDetailUseCase, + private val deleteEnvelopeUseCase: DeleteEnvelopeUseCase, savedStateHandle: SavedStateHandle, ) : BaseViewModel( SentEnvelopeDetailState(), @@ -27,6 +29,16 @@ class SentEnvelopeDetailViewModel @Inject constructor( } } + fun showDeleteDialog() { + postSideEffect(SentEnvelopeDetailEffect.ShowDeleteDialog) + } + + fun deleteEnvelope() = viewModelScope.launch { + deleteEnvelopeUseCase(envelopeId).onSuccess { + postSideEffect(SentEnvelopeDetailEffect.ShowDeleteSuccessSnackBar, SentEnvelopeDetailEffect.PopBackStack) + } + } + fun navigateSentEnvelopeEdit() = postSideEffect( SentEnvelopeDetailEffect.NavigateEnvelopeEdit( currentState.envelopeDetail, diff --git a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt index 614acd05..4d3d2e2d 100644 --- a/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt +++ b/feature/sent/src/main/java/com/susu/feature/sent/navigation/SentNavigation.kt @@ -8,6 +8,8 @@ import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.susu.core.model.EnvelopeDetail +import com.susu.core.ui.DialogToken +import com.susu.core.ui.SnackbarToken import com.susu.core.ui.extension.encodeToUri import com.susu.feature.envelope.SentEnvelopeRoute import com.susu.feature.envelopeadd.SentEnvelopeAddRoute @@ -43,6 +45,8 @@ fun NavGraphBuilder.sentNavGraph( navigateSentEnvelopeDetail: (Long) -> Unit, navigateSentEnvelopeEdit: (EnvelopeDetail) -> Unit, navigateSentEnvelopeAdd: () -> Unit, + onShowSnackbar: (SnackbarToken) -> Unit, + onShowDialog: (DialogToken) -> Unit, handleException: (Throwable, () -> Unit) -> Unit, ) { composable(route = SentRoute.route) { @@ -78,6 +82,9 @@ fun NavGraphBuilder.sentNavGraph( SentEnvelopeDetailRoute( popBackStack = popBackStack, navigateSentEnvelopeEdit = navigateSentEnvelopeEdit, + onShowSnackbar = onShowSnackbar, + onShowDialog = onShowDialog, + handleException = handleException, ) } diff --git a/feature/sent/src/main/res/values/strings.xml b/feature/sent/src/main/res/values/strings.xml index 763b83c7..c5186c75 100644 --- a/feature/sent/src/main/res/values/strings.xml +++ b/feature/sent/src/main/res/values/strings.xml @@ -31,6 +31,7 @@ 01012345678 입력해주세요 저장 + %d년 %d월 %d일 누구에게 보냈나요 이름을 입력해주세요 @@ -70,4 +71,7 @@ 메모 아니요 + 봉투를 삭제할까요? + 삭제한 봉투는 다시 복구할 수 없어요 + 봉투가 삭제됐어요 From fff1bfbc8ab95c4b98b8919eee69787275c352b4 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Mon, 5 Feb 2024 16:16:34 +0900 Subject: [PATCH 92/95] =?UTF-8?q?chore:=20sent=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=20string=20Res=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/ui/src/main/res/values/strings.xml | 1 + .../envelopeadd/content/more/MoreContract.kt | 10 +-- .../content/visited/VisitedContent.kt | 8 +- .../SentEnvelopeDetailScreen.kt | 20 ++--- .../envelopeedit/SentEnvelopeEditScreen.kt | 22 +++--- .../envelopeedit/component/EditDetailItem.kt | 76 ------------------- feature/sent/src/main/res/values/strings.xml | 31 +------- 7 files changed, 33 insertions(+), 135 deletions(-) diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml index 9b183d82..cac268fc 100644 --- a/core/ui/src/main/res/values/strings.xml +++ b/core/ui/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ 전체 %s원 총 %d개 더보기 아이콘 + 경조사 경조사명 경조사 카테고리 경조사 기간 diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/more/MoreContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/more/MoreContract.kt index 17a05c02..0fc84479 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/more/MoreContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/more/MoreContract.kt @@ -2,8 +2,8 @@ package com.susu.feature.envelopeadd.content.more import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState +import com.susu.core.ui.R import com.susu.feature.envelopeadd.EnvelopeAddStep -import com.susu.feature.sent.R import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf @@ -13,10 +13,10 @@ data class MoreState( ) : UiState val moreStep = persistentMapOf( - EnvelopeAddStep.VISITED to R.string.sent_envelope_edit_category_visited, - EnvelopeAddStep.PRESENT to R.string.sent_envelope_edit_category_present, - EnvelopeAddStep.MEMO to R.string.sent_envelope_edit_category_memo, - EnvelopeAddStep.PHONE to R.string.sent_envelope_edit_category_phone, + EnvelopeAddStep.VISITED to R.string.word_is_visited, + EnvelopeAddStep.PRESENT to R.string.word_gift, + EnvelopeAddStep.MEMO to R.string.word_memo, + EnvelopeAddStep.PHONE to com.susu.feature.sent.R.string.sent_add_more_step_phone, ) sealed interface MoreSideEffect : SideEffect { diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/visited/VisitedContent.kt b/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/visited/VisitedContent.kt index 06693371..25bea028 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/visited/VisitedContent.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/visited/VisitedContent.kt @@ -86,7 +86,7 @@ fun VisitedContent( SusuFilledButton( color = FilledButtonColor.Orange, style = MediumButtonStyle.height60, - text = stringResource(id = R.string.sent_envelope_edit_category_visited_yes), + text = stringResource(id = com.susu.core.ui.R.string.word_yes), onClick = onClickVisitedButton, modifier = modifier.fillMaxWidth(), ) @@ -94,7 +94,7 @@ fun VisitedContent( SusuGhostButton( color = GhostButtonColor.Black, style = MediumButtonStyle.height60, - text = stringResource(id = R.string.sent_envelope_edit_category_visited_yes), + text = stringResource(id = com.susu.core.ui.R.string.word_yes), onClick = onClickVisitedButton, modifier = modifier.fillMaxWidth(), rippleEnabled = false, @@ -105,7 +105,7 @@ fun VisitedContent( SusuFilledButton( color = FilledButtonColor.Orange, style = MediumButtonStyle.height60, - text = stringResource(id = R.string.sent_envelope_edit_category_visited_no), + text = stringResource(id = com.susu.core.ui.R.string.word_no), onClick = onClickNotVisitedButton, modifier = modifier.fillMaxWidth(), ) @@ -113,7 +113,7 @@ fun VisitedContent( SusuGhostButton( color = GhostButtonColor.Black, style = MediumButtonStyle.height60, - text = stringResource(id = R.string.sent_envelope_edit_category_visited_no), + text = stringResource(id = com.susu.core.ui.R.string.word_no), onClick = onClickNotVisitedButton, modifier = modifier.fillMaxWidth(), rippleEnabled = false, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt index fad476ab..ada055f6 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailScreen.kt @@ -134,45 +134,45 @@ fun SentEnvelopeDetailScreen( Spacer(modifier = modifier.size(SusuTheme.spacing.spacing_m)) Column { DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_event), + categoryText = stringResource(com.susu.core.ui.R.string.word_event), contentText = category.category, isEmptyContent = category.category.isEmpty(), ) DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_name), + categoryText = stringResource(com.susu.core.ui.R.string.word_name), contentText = friend.name, isEmptyContent = friend.name.isEmpty(), ) DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_relationship), + categoryText = stringResource(com.susu.core.ui.R.string.word_relationship), contentText = relationship.relation, isEmptyContent = relationship.relation.isEmpty(), ) DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_date), + categoryText = stringResource(com.susu.core.ui.R.string.word_date), contentText = envelope.handedOverAt.toJavaLocalDateTime().to_yyyy_korYear_M_korMonth_d_korDay(), ) DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_visited), + categoryText = stringResource(com.susu.core.ui.R.string.word_is_visited), contentText = if (envelope.hasVisited == true) { - stringResource(R.string.sent_envelope_detail_category_visited_yes) + stringResource(com.susu.core.ui.R.string.word_yes) } else { - stringResource(R.string.sent_envelope_detail_category_visited_no) + stringResource(com.susu.core.ui.R.string.word_no) }, isEmptyContent = envelope.hasVisited == null, ) DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_gift), + categoryText = stringResource(com.susu.core.ui.R.string.word_gift), contentText = envelope.gift ?: "", isEmptyContent = envelope.gift.isNullOrEmpty(), ) DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_phone), + categoryText = stringResource(com.susu.core.ui.R.string.word_phone_number), contentText = friend.phoneNumber, isEmptyContent = friend.phoneNumber.isEmpty(), ) DetailItem( - categoryText = stringResource(R.string.sent_envelope_detail_category_memo), + categoryText = stringResource(com.susu.core.ui.R.string.word_memo), contentText = envelope.memo ?: "", isEmptyContent = envelope.memo.isNullOrEmpty(), ) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt index 0ae8c25d..6095c84a 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditScreen.kt @@ -190,7 +190,7 @@ fun SentEnvelopeEditScreen( ) Spacer(modifier = Modifier.size(SusuTheme.spacing.spacing_m)) EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_event), + categoryText = stringResource(com.susu.core.ui.R.string.word_event), categoryTextAlign = Alignment.Top, ) { uiState.categoryConfig.dropLast(1).forEach { category -> @@ -223,7 +223,7 @@ fun SentEnvelopeEditScreen( } } EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_name), + categoryText = stringResource(com.susu.core.ui.R.string.word_name), categoryTextAlign = Alignment.CenterVertically, ) { SusuBasicTextField( @@ -236,7 +236,7 @@ fun SentEnvelopeEditScreen( ) } EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_relationship), + categoryText = stringResource(com.susu.core.ui.R.string.word_relationship), categoryTextAlign = Alignment.Top, ) { uiState.relationshipConfig.dropLast(1).forEach { relationship -> @@ -269,7 +269,7 @@ fun SentEnvelopeEditScreen( } } EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_date), + categoryText = stringResource(com.susu.core.ui.R.string.word_date), categoryTextAlign = Alignment.CenterVertically, ) { Text( @@ -285,13 +285,13 @@ fun SentEnvelopeEditScreen( ) } EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_visited), + categoryText = stringResource(com.susu.core.ui.R.string.word_is_visited), categoryTextAlign = Alignment.Top, ) { SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_visited_yes), + text = stringResource(com.susu.core.ui.R.string.word_yes), isActive = uiState.hasVisited == true, onClick = { onHasVisitedUpdated(true) }, modifier = Modifier.weight(1f), @@ -299,14 +299,14 @@ fun SentEnvelopeEditScreen( SusuFilledButton( color = FilledButtonColor.Orange, style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_visited_no), + text = stringResource(com.susu.core.ui.R.string.word_no), isActive = uiState.hasVisited == false, onClick = { onHasVisitedUpdated(false) }, modifier = Modifier.weight(1f), ) } EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_present), + categoryText = stringResource(com.susu.core.ui.R.string.word_gift), categoryTextColor = if (uiState.gift != null) Gray70 else Gray40, categoryTextAlign = Alignment.CenterVertically, ) { @@ -320,7 +320,7 @@ fun SentEnvelopeEditScreen( ) } EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_phone), + categoryText = stringResource(com.susu.core.ui.R.string.word_phone_number), categoryTextColor = if (uiState.phoneNumber != null) Gray70 else Gray40, categoryTextAlign = Alignment.CenterVertically, ) { @@ -335,7 +335,7 @@ fun SentEnvelopeEditScreen( ) } EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_memo), + categoryText = stringResource(com.susu.core.ui.R.string.word_memo), categoryTextColor = if (uiState.memo != null) Gray70 else Gray40, categoryTextAlign = Alignment.Top, ) { @@ -359,7 +359,7 @@ fun SentEnvelopeEditScreen( color = FilledButtonColor.Black, style = MediumButtonStyle.height60, shape = RectangleShape, - text = stringResource(R.string.sent_envelope_edit_save), + text = stringResource(com.susu.core.ui.R.string.word_save), onClick = onClickSave, ) } diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/component/EditDetailItem.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/component/EditDetailItem.kt index f321a21c..9f7a73dd 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/component/EditDetailItem.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/component/EditDetailItem.kt @@ -1,7 +1,6 @@ package com.susu.feature.envelopeedit.component import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row @@ -13,26 +12,15 @@ import androidx.compose.foundation.layout.width import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.susu.core.designsystem.component.button.AddConditionButton -import com.susu.core.designsystem.component.button.FilledButtonColor -import com.susu.core.designsystem.component.button.SmallButtonStyle -import com.susu.core.designsystem.component.button.SusuFilledButton -import com.susu.core.designsystem.component.textfield.SusuBasicTextField -import com.susu.core.designsystem.theme.Gray30 import com.susu.core.designsystem.theme.Gray70 import com.susu.core.designsystem.theme.SusuTheme -import com.susu.feature.sent.R @OptIn(ExperimentalLayoutApi::class) @Composable @@ -69,67 +57,3 @@ fun EditDetailItem( } } } - -@Preview(showBackground = true, backgroundColor = 0xffffff) -@Composable -fun DetailItem() { - var name by remember { mutableStateOf("김철수") } - - SusuTheme { - Column( - verticalArrangement = Arrangement.spacedBy(16.dp), - ) { - // TextField 버전 - EditDetailItem( - categoryText = "이름", - categoryTextAlign = Alignment.CenterVertically, - ) { - SusuBasicTextField( - text = name, - onTextChange = { name = it }, - placeholder = stringResource(R.string.sent_envelope_edit_category_name_placeholder), - placeholderColor = Gray30, - textStyle = SusuTheme.typography.title_s, - modifier = Modifier.fillMaxWidth(), - ) - } - // Button 버전 - EditDetailItem( - categoryText = stringResource(R.string.sent_envelope_edit_category_event), - categoryTextAlign = Alignment.Top, - ) { - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_event_wedding), - isActive = true, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_event_first_birth), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_edit_funeral), - isActive = false, - onClick = {}, - ) - SusuFilledButton( - color = FilledButtonColor.Orange, - style = SmallButtonStyle.height32, - text = stringResource(R.string.sent_envelope_edit_category_edit_birthday), - isActive = false, - onClick = {}, - ) - AddConditionButton( - onClick = {}, - ) - } - } - } -} diff --git a/feature/sent/src/main/res/values/strings.xml b/feature/sent/src/main/res/values/strings.xml index c5186c75..0a5a69e1 100644 --- a/feature/sent/src/main/res/values/strings.xml +++ b/feature/sent/src/main/res/values/strings.xml @@ -6,27 +6,8 @@ 보냈어요 받았어요 전체 보기 - 내역 보기 - 경조사 - 이름 김철수 - 나와의 관계 - 날짜 - 방문 여부 - 선물 - 연락처 - 메모 - 결혼식 - 돌잔치 - 장례식 - 생일 기념일 - 친구 - 가족 - 친척 - 동료 - - 아니요 한끼 식사 01012345678 입력해주세요 @@ -61,17 +42,9 @@ "을 " "전체 " - 경조사 - 이름 - 나와의 관계 - 날짜 - 방문 여부 - 선물 - 연락처 - 메모 - - 아니요 + 봉투를 삭제할까요? 삭제한 봉투는 다시 복구할 수 없어요 봉투가 삭제됐어요 + 받은 이의 연락처 From bc4df8c396e49935373fc0fcba907937b2ecd76d Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Mon, 5 Feb 2024 16:29:28 +0900 Subject: [PATCH 93/95] =?UTF-8?q?feat:=20=EB=B4=89=ED=88=AC=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=ED=99=94=EB=A9=B4=20=EC=97=90=EB=9F=AC=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feature/envelopedetail/SentEnvelopeDetailViewModel.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt index e98e0197..5dec3166 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopedetail/SentEnvelopeDetailViewModel.kt @@ -26,6 +26,8 @@ class SentEnvelopeDetailViewModel @Inject constructor( envelopeDetail = envelopeDetail, ) } + }.onFailure { + postSideEffect(SentEnvelopeDetailEffect.HandleException(it, ::getEnvelopeDetail)) } } @@ -36,6 +38,8 @@ class SentEnvelopeDetailViewModel @Inject constructor( fun deleteEnvelope() = viewModelScope.launch { deleteEnvelopeUseCase(envelopeId).onSuccess { postSideEffect(SentEnvelopeDetailEffect.ShowDeleteSuccessSnackBar, SentEnvelopeDetailEffect.PopBackStack) + }.onFailure { + postSideEffect(SentEnvelopeDetailEffect.HandleException(it, ::deleteEnvelope)) } } From 9ef04042a7ef5db16daeb556c02fa3faf67d5e1f Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Mon, 5 Feb 2024 16:36:00 +0900 Subject: [PATCH 94/95] =?UTF-8?q?fix:=20=EB=B3=B4=EB=82=B8=20=EB=B4=89?= =?UTF-8?q?=ED=88=AC=20=EC=83=81=EC=84=B8=EB=A5=BC=20=ED=86=B5=ED=95=B4=20?= =?UTF-8?q?=EB=B0=9B=EC=9D=80=20=EB=B4=89=ED=88=AC=EB=A5=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=ED=96=88=EC=9D=84=20=EB=95=8C,=20=EB=B3=B4=EB=82=B8?= =?UTF-8?q?=20=EB=B4=89=ED=88=AC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt | 3 ++- .../com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt index fc684b8c..e3e90f54 100644 --- a/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt +++ b/domain/src/main/java/com/susu/domain/usecase/envelope/EditSentEnvelopeUseCase.kt @@ -22,7 +22,7 @@ class EditSentEnvelopeUseCase @Inject constructor( envelopesRepository.editEnvelope( id = envelopeId, - type = "SENT", + type = envelopeType, friendId = friendId, amount = amount, gift = gift, @@ -37,6 +37,7 @@ class EditSentEnvelopeUseCase @Inject constructor( data class Param( val envelopeId: Long, + val envelopeType: String, val friendId: Long, val friendName: String, val phoneNumber: String? = null, diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt index 70002328..2e273fee 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeedit/SentEnvelopeEditViewModel.kt @@ -73,6 +73,7 @@ class SentEnvelopeEditViewModel @Inject constructor( param = with(currentState) { EditSentEnvelopeUseCase.Param( envelopeId = envelopeDetail.envelope.id, + envelopeType = envelopeDetail.envelope.type, friendId = envelopeDetail.friend.id, friendName = friendName, phoneNumber = phoneNumber, From 81a99822cc8178430f38a1ea4670bcc4ddd44689 Mon Sep 17 00:00:00 2001 From: yangsooplus Date: Mon, 5 Feb 2024 16:59:04 +0900 Subject: [PATCH 95/95] chore: detekt check --- .../com/susu/feature/envelopeadd/content/more/MoreContract.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/more/MoreContract.kt b/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/more/MoreContract.kt index 0fc84479..578909da 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/more/MoreContract.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopeadd/content/more/MoreContract.kt @@ -1,8 +1,8 @@ package com.susu.feature.envelopeadd.content.more +import com.susu.core.ui.R import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState -import com.susu.core.ui.R import com.susu.feature.envelopeadd.EnvelopeAddStep import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf