diff --git a/core/model/src/main/java/com/susu/core/model/Term.kt b/core/model/src/main/java/com/susu/core/model/Term.kt index fee956dd..145c01ce 100644 --- a/core/model/src/main/java/com/susu/core/model/Term.kt +++ b/core/model/src/main/java/com/susu/core/model/Term.kt @@ -4,4 +4,5 @@ data class Term( val id: Int, val title: String, val isEssential: Boolean, + val canRead: Boolean = true, ) diff --git a/data/src/main/java/com/susu/data/remote/model/response/TermResponse.kt b/data/src/main/java/com/susu/data/remote/model/response/TermResponse.kt index dea1c6e1..d8b7b041 100644 --- a/data/src/main/java/com/susu/data/remote/model/response/TermResponse.kt +++ b/data/src/main/java/com/susu/data/remote/model/response/TermResponse.kt @@ -9,6 +9,7 @@ data class TermResponse( val id: Int, val title: String, val isEssential: Boolean, + val isIncludeDetail: Boolean = true, ) @Serializable @@ -23,6 +24,7 @@ fun TermResponse.toModel(): Term = Term( id = id, title = title, isEssential = isEssential, + canRead = isIncludeDetail, ) fun TermDetailResponse.toModel(): TermDetail = TermDetail( diff --git a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpContract.kt b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpContract.kt index 82ca6c9d..10e4a169 100644 --- a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpContract.kt +++ b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpContract.kt @@ -5,6 +5,8 @@ import com.susu.core.ui.Gender import com.susu.core.ui.base.SideEffect import com.susu.core.ui.base.UiState import com.susu.feature.loginsignup.R +import kotlinx.collections.immutable.PersistentSet +import kotlinx.collections.immutable.persistentSetOf sealed interface SignUpEffect : SideEffect { data object NavigateToLogin : SignUpEffect @@ -16,7 +18,7 @@ sealed interface SignUpEffect : SideEffect { data class SignUpState( val isLoading: Boolean = false, val currentStep: SignUpStep = SignUpStep.TERMS, - val agreedTerms: List = emptyList(), + val agreedTerms: PersistentSet = persistentSetOf(), val name: String = "", val isNameValid: Boolean = true, val gender: Gender = Gender.NONE, diff --git a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpScreen.kt b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpScreen.kt index dffc9972..a5e8c7cb 100644 --- a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpScreen.kt +++ b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpScreen.kt @@ -18,6 +18,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.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -44,6 +45,7 @@ import com.susu.feature.loginsignup.R import com.susu.feature.loginsignup.signup.content.AdditionalContent import com.susu.feature.loginsignup.signup.content.NameContent import com.susu.feature.loginsignup.signup.content.TermsContent +import kotlinx.collections.immutable.toPersistentList @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -76,7 +78,15 @@ fun SignUpRoute( } } - Box(modifier = Modifier.fillMaxSize().padding(padding)) { + LaunchedEffect(key1 = Unit) { + termViewModel.getTermList() + } + + Box( + modifier = Modifier + .fillMaxSize() + .padding(padding), + ) { SignUpScreen( uiState = uiState, termState = termState, @@ -119,7 +129,7 @@ fun SignUpRoute( modifier = Modifier.fillMaxSize(), descriptionText = targetState.description?.let { stringResource(id = it) } ?: "", terms = termState.terms, - agreedTerms = uiState.agreedTerms, + agreedTerms = uiState.agreedTerms.toPersistentList(), onDetailClick = { termViewModel.updateCurrentTerm(it) viewModel.goTermDetail() @@ -241,7 +251,9 @@ fun SignUpScreen( } content() SusuFilledButton( - modifier = Modifier.fillMaxWidth().imePadding(), + modifier = Modifier + .fillMaxWidth() + .imePadding(), shape = RectangleShape, color = FilledButtonColor.Black, style = MediumButtonStyle.height60, diff --git a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpViewModel.kt b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpViewModel.kt index 41a060b6..dddbe448 100644 --- a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpViewModel.kt +++ b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/SignUpViewModel.kt @@ -12,6 +12,8 @@ import com.susu.core.ui.base.BaseViewModel import com.susu.domain.usecase.loginsignup.SignUpUseCase import com.susu.feature.loginsignup.social.KakaoLoginHelper import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.persistentSetOf +import kotlinx.collections.immutable.toPersistentSet import kotlinx.coroutines.launch import javax.inject.Inject @@ -43,19 +45,19 @@ class SignUpViewModel @Inject constructor( } fun agreeTerm(termId: Int) { - intent { copy(agreedTerms = agreedTerms + termId) } + intent { copy(agreedTerms = agreedTerms.add(termId)) } } fun disagreeTerm(termId: Int) { - intent { copy(agreedTerms = agreedTerms - termId) } + intent { copy(agreedTerms = agreedTerms.remove(termId)) } } fun agreeAllTerms(entireTermIds: List) { - intent { copy(agreedTerms = entireTermIds) } + intent { copy(agreedTerms = entireTermIds.toPersistentSet()) } } fun disagreeAllTerms() { - intent { copy(agreedTerms = emptyList()) } + intent { copy(agreedTerms = persistentSetOf()) } } fun goNextStep() { @@ -96,7 +98,7 @@ class SignUpViewModel @Inject constructor( } else { null }, - termAgreement = uiState.value.agreedTerms, + termAgreement = uiState.value.agreedTerms.toList(), ), ).onSuccess { postSideEffect(SignUpEffect.NavigateToReceived) diff --git a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/TermContract.kt b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/TermContract.kt index c832749b..a393e892 100644 --- a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/TermContract.kt +++ b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/TermContract.kt @@ -4,6 +4,8 @@ import com.susu.core.model.Term import com.susu.core.model.TermDetail 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 TermEffect : SideEffect { data class ShowToast(val msg: String) : TermEffect @@ -11,6 +13,6 @@ sealed interface TermEffect : SideEffect { data class TermState( val isLoading: Boolean = false, - val terms: List = emptyList(), + val terms: PersistentList = persistentListOf(), val currentTerm: TermDetail = TermDetail(0, "", false, ""), ) : UiState diff --git a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/TermViewModel.kt b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/TermViewModel.kt index 01d3933e..1aa81e39 100644 --- a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/TermViewModel.kt +++ b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/TermViewModel.kt @@ -5,6 +5,7 @@ import com.susu.core.ui.base.BaseViewModel import com.susu.domain.usecase.loginsignup.GetTermDetailUseCase import com.susu.domain.usecase.loginsignup.GetTermsUseCase import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.launch import javax.inject.Inject @@ -14,11 +15,11 @@ class TermViewModel @Inject constructor( private val getTermDetailUseCase: GetTermDetailUseCase, ) : BaseViewModel(TermState()) { - init { + fun getTermList() { viewModelScope.launch { intent { copy(isLoading = true) } getTermsUseCase().onSuccess { - intent { copy(terms = it, isLoading = false) } + intent { copy(terms = it.toPersistentList(), isLoading = false) } }.onFailure { postSideEffect(TermEffect.ShowToast(it.message ?: "약관을 불러오지 못했어요")) } diff --git a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/content/TermsContent.kt b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/content/TermsContent.kt index 0fc20875..cabf25bd 100644 --- a/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/content/TermsContent.kt +++ b/feature/loginsignup/src/main/java/com/susu/feature/loginsignup/signup/content/TermsContent.kt @@ -62,6 +62,7 @@ fun TermsContent( title = term.title, checked = agreedTerms.contains(term.id), isEssential = term.isEssential, + canRead = term.canRead, onDetailClick = { onDetailClick(term.id) }, onCheckClick = { onTermChecked(it, term.id) @@ -79,11 +80,13 @@ fun TermListItem( isEssential: Boolean = true, canRead: Boolean = true, onCheckClick: (Boolean) -> Unit = {}, - onDetailClick: (() -> Unit)? = null, + onDetailClick: (() -> Unit) = {}, ) { Row( - modifier = if (onDetailClick != null) { - modifier.susuClickable(onClick = onDetailClick).padding(vertical = SusuTheme.spacing.spacing_m) + modifier = if (canRead) { + modifier + .susuClickable(onClick = onDetailClick) + .padding(vertical = SusuTheme.spacing.spacing_m) } else { modifier.padding(vertical = SusuTheme.spacing.spacing_m) }, @@ -122,7 +125,11 @@ fun TermsContentPreview() { TermsContent( modifier = Modifier.fillMaxSize(), descriptionText = "어쩌구저쩌구\n뭐를해주세요", - terms = listOf(Term(1, "노예 계약", true), Term(2, "농노 계약", true)), + terms = listOf( + Term(0, "14세 이상?", true, false), + Term(1, "노예 계약", true), + Term(2, "농노 계약", true), + ), ) } } diff --git a/feature/loginsignup/src/main/res/values/strings.xml b/feature/loginsignup/src/main/res/values/strings.xml index 1ab2aef1..4c07a409 100644 --- a/feature/loginsignup/src/main/res/values/strings.xml +++ b/feature/loginsignup/src/main/res/values/strings.xml @@ -33,4 +33,5 @@ 알 수 없는 에러가 발생했어요 카카오톡 서버에서 에러가 발생했어요 카카오톡 로그인 에러가 발생했어요 + 만 14세 이상입니다. diff --git a/feature/sent/src/main/java/com/susu/feature/envelopesearch/SentEnvelopeSearchViewModel.kt b/feature/sent/src/main/java/com/susu/feature/envelopesearch/SentEnvelopeSearchViewModel.kt index a02099ed..7b3d59cc 100644 --- a/feature/sent/src/main/java/com/susu/feature/envelopesearch/SentEnvelopeSearchViewModel.kt +++ b/feature/sent/src/main/java/com/susu/feature/envelopesearch/SentEnvelopeSearchViewModel.kt @@ -10,6 +10,8 @@ import com.susu.domain.usecase.friend.SearchFriendUseCase import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch import javax.inject.Inject @@ -52,35 +54,38 @@ class SentEnvelopeSearchViewModel @Inject constructor( } fun getEnvelopeList(search: String) = viewModelScope.launch { + if (search.isEmpty()) return@launch + val searchedFriends = searchFriendUseCase(search).getOrThrow() // 친구 검색 결과가 존재하면 봉투 검색 - val envelopesByFriend = if (searchedFriends.isNotEmpty()) { - searchSentEnvelopeListUseCase( - param = SearchSentEnvelopeListUseCase.Param( - friendIds = searchedFriends.map { it.friend.id.toInt() }, - ), - ) - } else { - Result.success(emptyList()) + val envelopesByFriend = async { + if (searchedFriends.isNotEmpty()) { + searchSentEnvelopeListUseCase( + param = SearchSentEnvelopeListUseCase.Param( + friendIds = searchedFriends.map { it.friend.id.toInt() }, + ), + ).getOrDefault(emptyList()) + } else { + emptyList() + } } // 숫자 형식일 경우는 금액으로 봉투 검색 - val envelopesByAmount = search.toLongOrNull()?.let { amount -> - searchSentEnvelopeListUseCase( - param = SearchSentEnvelopeListUseCase.Param( - fromAmount = amount, - toAmount = amount, - ), - ) - } ?: Result.success(emptyList()) + val envelopesByAmount = async { + search.toLongOrNull()?.let { amount -> + searchSentEnvelopeListUseCase( + param = SearchSentEnvelopeListUseCase.Param( + fromAmount = amount, + toAmount = amount, + ), + ).getOrDefault(emptyList()) + } ?: emptyList() + } // 두가지 조건을 검색 완료 시 결과를 통합 표시 - if (envelopesByFriend.isSuccess && envelopesByAmount.isSuccess) { - val searchedEnvelopes = - envelopesByFriend.getOrDefault(emptyList()) + envelopesByAmount.getOrDefault(emptyList()) - intent { copy(envelopeList = searchedEnvelopes.toPersistentList()) } - } + val result = awaitAll(envelopesByFriend, envelopesByAmount).flatten() + intent { copy(envelopeList = result.toPersistentList()) } } private fun updateRecentSearchList(searchList: List) {