diff --git a/core/designsystem/src/main/res/drawable/ic_google_logo.xml b/core/designsystem/src/main/res/drawable/ic_google_logo.xml new file mode 100644 index 00000000..55e2eeaf --- /dev/null +++ b/core/designsystem/src/main/res/drawable/ic_google_logo.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/request/PatchUserInfoRequestDto.kt b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/request/PatchUserInfoRequestDto.kt index c06efd1c..99121e84 100644 --- a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/request/PatchUserInfoRequestDto.kt +++ b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/request/PatchUserInfoRequestDto.kt @@ -8,12 +8,12 @@ data class PatchUserInfoRequestDto( @SerialName("selectedTownId") val selectedTownId: Long, - @SerialName("favoriteTownIdList") - val favoriteTownIdList: List, - @SerialName("persona") val persona: String, @SerialName("nickname") - val nickname: String + val nickname: String, + + @SerialName("policyAgreementInfos") + val policyAgreementInfos: List ) diff --git a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/request/PolicyAgreementInfoDto.kt b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/request/PolicyAgreementInfoDto.kt new file mode 100644 index 00000000..e22889f2 --- /dev/null +++ b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/request/PolicyAgreementInfoDto.kt @@ -0,0 +1,13 @@ +package com.teamsolply.solply.onboarding.dto.request + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PolicyAgreementInfoDto( + @SerialName("policyId") + val policyId: Long, + + @SerialName("isAgree") + val isAgree: Boolean +) diff --git a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/response/GetAllTownResponseDto.kt b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/response/GetAllTownResponseDto.kt index 6d4b84ed..db9d53da 100644 --- a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/response/GetAllTownResponseDto.kt +++ b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/dto/response/GetAllTownResponseDto.kt @@ -15,6 +15,8 @@ data class TownDto( val townId: Long, @SerialName("townName") val townName: String, + @SerialName("parentTownId") + val parentTownId: Long? = null, @SerialName("subTowns") val subTowns: List? = null ) diff --git a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/PolicyAgreementInfoMapper.kt b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/PolicyAgreementInfoMapper.kt new file mode 100644 index 00000000..ba5145f5 --- /dev/null +++ b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/PolicyAgreementInfoMapper.kt @@ -0,0 +1,10 @@ +package com.teamsolply.solply.onboarding.mapper + +import com.teamsolply.solply.onboarding.dto.request.PolicyAgreementInfoDto +import com.teamsolply.solply.onboarding.model.PolicyAgreementInfoEntity + +fun PolicyAgreementInfoEntity.toDto(): PolicyAgreementInfoDto = + PolicyAgreementInfoDto( + policyId = this.policyId, + isAgree = this.isAgree + ) diff --git a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/TownEntitiyMapper.kt b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/TownEntitiyMapper.kt index 33388052..c0309801 100644 --- a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/TownEntitiyMapper.kt +++ b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/mapper/TownEntitiyMapper.kt @@ -2,19 +2,44 @@ package com.teamsolply.solply.onboarding.mapper import com.teamsolply.solply.onboarding.dto.response.GetAllTownResponseDto import com.teamsolply.solply.onboarding.dto.response.TownDto +import com.teamsolply.solply.onboarding.model.ParentTownEntity import com.teamsolply.solply.onboarding.model.SubTownEntity import com.teamsolply.solply.onboarding.model.TownEntity fun GetAllTownResponseDto.toEntity(): TownEntity { + val parentTowns = towns.filter { it.parentTownId == null } + + val grouped = parentTowns.map { parent -> + ParentTownEntity( + townId = parent.townId, + townName = parent.townName, + subTowns = towns + .filter { it.parentTownId == parent.townId } + .map { dto -> + SubTownEntity( + townId = dto.townId, + townName = dto.townName + ) + } + ) + } + return TownEntity( - towns = towns.map { it.toSubEntity() } + parentTowns = grouped + ) +} + +fun TownDto.toParentEntity(): ParentTownEntity { + return ParentTownEntity( + townId = townId, + townName = townName, + subTowns = subTowns?.map { it.toSubEntity() } ?: emptyList() ) } fun TownDto.toSubEntity(): SubTownEntity { return SubTownEntity( - townId = this.townId, - townName = this.townName, - subTowns = this.subTowns?.map { it.toSubEntity() } + townId = townId, + townName = townName ) } diff --git a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepositoryImpl.kt b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepositoryImpl.kt index 898c0f24..cb48db68 100644 --- a/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepositoryImpl.kt +++ b/data/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepositoryImpl.kt @@ -1,8 +1,10 @@ package com.teamsolply.solply.onboarding.repository import com.teamsolply.solply.onboarding.dto.request.PatchUserInfoRequestDto +import com.teamsolply.solply.onboarding.mapper.toDto import com.teamsolply.solply.onboarding.mapper.toEntity import com.teamsolply.solply.onboarding.model.PersonaEntity +import com.teamsolply.solply.onboarding.model.PolicyAgreementInfoEntity import com.teamsolply.solply.onboarding.model.TownEntity import com.teamsolply.solply.onboarding.model.UserInfoEntity import com.teamsolply.solply.onboarding.source.remote.OnBoardingRemoteDataSource @@ -29,16 +31,16 @@ class OnBoardingRepositoryImpl @Inject constructor( override suspend fun patchUserInfo( selectedTownId: Long, - favoriteTownIdList: List, persona: String, - nickname: String + nickname: String, + policyAgreementInfos: List ): Result = runCatching { onBoardingRemoteDataSource.patchUserInfo( PatchUserInfoRequestDto( selectedTownId = selectedTownId, - favoriteTownIdList = favoriteTownIdList, persona = persona, - nickname = nickname + nickname = nickname, + policyAgreementInfos = policyAgreementInfos.map { it.toDto() } ) ) }.mapCatching { diff --git a/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/PolicyAgreementInfoEntity.kt b/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/PolicyAgreementInfoEntity.kt new file mode 100644 index 00000000..f8da33e0 --- /dev/null +++ b/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/PolicyAgreementInfoEntity.kt @@ -0,0 +1,6 @@ +package com.teamsolply.solply.onboarding.model + +data class PolicyAgreementInfoEntity( + val policyId: Long, + val isAgree: Boolean +) diff --git a/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/TownEntity.kt b/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/TownEntity.kt index 91cdeb5d..e5767279 100644 --- a/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/TownEntity.kt +++ b/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/model/TownEntity.kt @@ -1,11 +1,16 @@ package com.teamsolply.solply.onboarding.model data class TownEntity( - val towns: List + val parentTowns: List ) -data class SubTownEntity( +data class ParentTownEntity( val townId: Long, val townName: String, - val subTowns: List? = null + val subTowns: List +) + +data class SubTownEntity( + val townId: Long, + val townName: String ) diff --git a/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepository.kt b/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepository.kt index 8d965d09..b21d6347 100644 --- a/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepository.kt +++ b/domain/onboarding/src/main/java/com/teamsolply/solply/onboarding/repository/OnBoardingRepository.kt @@ -1,6 +1,7 @@ package com.teamsolply.solply.onboarding.repository import com.teamsolply.solply.onboarding.model.PersonaEntity +import com.teamsolply.solply.onboarding.model.PolicyAgreementInfoEntity import com.teamsolply.solply.onboarding.model.TownEntity import com.teamsolply.solply.onboarding.model.UserInfoEntity @@ -10,8 +11,8 @@ interface OnBoardingRepository { suspend fun checkNicknameDuplicate(nickname: String): Result suspend fun patchUserInfo( selectedTownId: Long, - favoriteTownIdList: List, persona: String, - nickname: String + nickname: String, + policyAgreementInfos: List ): Result } diff --git a/feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt b/feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt index 487f8f2d..d155ea1e 100644 --- a/feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt +++ b/feature/oauth/src/main/java/com/teamsolply/solply/oauth/OauthScreen.kt @@ -82,7 +82,7 @@ fun OauthScreen( Column( modifier.fillMaxSize() ) { - Spacer(modifier = Modifier.height(115.dp)) + Spacer(modifier = Modifier.height(180.dp)) Image( painter = painterResource(R.drawable.ic_logo_full_vector), contentDescription = "app_logo", @@ -151,7 +151,7 @@ fun OauthScreen( .height(52.dp) .padding(start = 20.dp, end = 20.dp) .background( - color = SolplyTheme.colors.black, + color = SolplyTheme.colors.white, shape = RoundedCornerShape(12.dp) ) .customClickable( @@ -163,16 +163,16 @@ fun OauthScreen( horizontalArrangement = Arrangement.Start ) { Icon( - painter = painterResource(R.drawable.ic_apple_logo), - contentDescription = "kakao_logo", + painter = painterResource(R.drawable.ic_google_logo), + contentDescription = "google_logo", tint = Color.Unspecified, modifier = Modifier .padding(start = 16.dp, end = 12.dp, top = 12.dp, bottom = 12.dp) ) Text( - text = stringResource(com.teamsolply.solply.oauth.R.string.apple_login), + text = stringResource(com.teamsolply.solply.oauth.R.string.google_login), style = SolplyTheme.typography.button16M, - color = SolplyTheme.colors.white + color = SolplyTheme.colors.black ) } Spacer(modifier = Modifier.height(48.dp)) diff --git a/feature/oauth/src/main/res/values/strings.xml b/feature/oauth/src/main/res/values/strings.xml index bec778d5..1bc495ae 100644 --- a/feature/oauth/src/main/res/values/strings.xml +++ b/feature/oauth/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ 카카오로 계속하기 - Apple로 계속하기 + Google로 계속하기 \ No newline at end of file diff --git a/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingContract.kt b/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingContract.kt index e6ee1c14..f00aa866 100644 --- a/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingContract.kt +++ b/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingContract.kt @@ -8,21 +8,25 @@ import com.teamsolply.solply.ui.base.UiState data class OnBoardingState( val currentPage: Int = 0, - val totalPageCount: Int = 3, + val totalPageCount: Int = 4, val townList: TownEntity = TownEntity( - towns = emptyList() + parentTowns = emptyList() ), val selectedRegionId: Long? = 1, val selectedTownId: Long? = null, val townBottomSheetShown: Boolean = false, + val personaList: PersonaEntity = PersonaEntity(personaList = emptyList()), val selectedPersona: String? = null, val userNickname: String = "", val isNicknameDuplicate: Boolean = false, val showStartingScreen: Boolean = false, - val isOnBoardingSuccess: Boolean = false + val isOnBoardingSuccess: Boolean = false, + val agree14: Boolean = false, + val agreeService: Boolean = false, + val agreePrivacy: Boolean = false ) : UiState sealed interface OnBoardingIntent : UiIntent { @@ -40,6 +44,9 @@ sealed interface OnBoardingIntent : UiIntent { ) : OnBoardingIntent data object ShowStartingScreen : OnBoardingIntent + data class ChangeAgree14(val isChecked: Boolean) : OnBoardingIntent + data class ChangeAgreeService(val isChecked: Boolean) : OnBoardingIntent + data class ChangeAgreePrivacy(val isChecked: Boolean) : OnBoardingIntent } sealed interface OnBoardingSideEffect : SideEffect { diff --git a/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt b/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt index c1ebe070..ace2de8e 100644 --- a/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/OnBoardingViewModel.kt @@ -1,6 +1,7 @@ package com.teamsolply.solply.onboarding import androidx.lifecycle.viewModelScope +import com.teamsolply.solply.onboarding.model.PolicyAgreementInfoEntity import com.teamsolply.solply.onboarding.repository.OnBoardingRepository import com.teamsolply.solply.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -83,6 +84,17 @@ class OnBoardingViewModel @Inject constructor( is OnBoardingIntent.ShowStartingScreen -> { patchUserInfo() } + is OnBoardingIntent.ChangeAgree14 -> { + reduce { copy(agree14 = intent.isChecked) } + } + + is OnBoardingIntent.ChangeAgreeService -> { + reduce { copy(agreeService = intent.isChecked) } + } + + is OnBoardingIntent.ChangeAgreePrivacy -> { + reduce { copy(agreePrivacy = intent.isChecked) } + } } } @@ -118,11 +130,18 @@ class OnBoardingViewModel @Inject constructor( viewModelScope.launch { uiState.value.selectedTownId?.let { selectedTownId -> uiState.value.selectedPersona?.let { selectedPersona -> + + val policyInfos = listOf( + PolicyAgreementInfoEntity(1, uiState.value.agree14), + PolicyAgreementInfoEntity(2, uiState.value.agreeService), + PolicyAgreementInfoEntity(3, uiState.value.agreePrivacy) + ) + onBoardingRepository.patchUserInfo( selectedTownId = selectedTownId, - favoriteTownIdList = uiState.value.townList.towns.map { it.townId }, persona = selectedPersona, - nickname = uiState.value.userNickname + nickname = uiState.value.userNickname, + policyAgreementInfos = policyInfos ).onSuccess { reduce { copy(showStartingScreen = true) } } diff --git a/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/AllowClauseScreen.kt b/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/AllowClauseScreen.kt index aa2933b8..b74a2554 100644 --- a/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/AllowClauseScreen.kt +++ b/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/AllowClauseScreen.kt @@ -17,11 +17,6 @@ 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.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.res.painterResource @@ -38,14 +33,10 @@ fun AllowClauseScreen( onNextClick: () -> Unit, onBoardingIntent: (OnBoardingIntent) -> Unit ) { - var isAllChecked by remember { mutableStateOf(false) } - var agree14 by remember { mutableStateOf(false) } - var agreeService by remember { mutableStateOf(false) } - var agreePrivacy by remember { mutableStateOf(false) } - - LaunchedEffect(agree14, agreeService, agreePrivacy) { - isAllChecked = agree14 && agreeService && agreePrivacy - } + val agree14 = state.agree14 + val agreeService = state.agreeService + val agreePrivacy = state.agreePrivacy + val isAllChecked = agree14 && agreeService && agreePrivacy Column( modifier = Modifier @@ -77,10 +68,9 @@ fun AllowClauseScreen( ) .customClickable(rippleEnabled = false) { val toggle = !isAllChecked - isAllChecked = toggle - agree14 = toggle - agreeService = toggle - agreePrivacy = toggle + onBoardingIntent(OnBoardingIntent.ChangeAgree14(toggle)) + onBoardingIntent(OnBoardingIntent.ChangeAgreeService(toggle)) + onBoardingIntent(OnBoardingIntent.ChangeAgreePrivacy(toggle)) } ) { Row( @@ -114,13 +104,17 @@ fun AllowClauseScreen( AgreementItem( text = "(필수) 만 14세 이상입니다", checked = agree14, - onClick = { agree14 = !agree14 } + onClick = { + onBoardingIntent(OnBoardingIntent.ChangeAgree14(!agree14)) + } ) AgreementItem( text = "(필수) 서비스 이용 약관", checked = agreeService, - onClick = { agreeService = !agreeService }, + onClick = { + onBoardingIntent(OnBoardingIntent.ChangeAgreeService(!agreeService)) + }, showArrow = true, onArrowClick = { // TODO: 서비스 이용 약관 이동 @@ -130,7 +124,9 @@ fun AllowClauseScreen( AgreementItem( text = "(필수) 개인정보 처리방침", checked = agreePrivacy, - onClick = { agreePrivacy = !agreePrivacy }, + onClick = { + onBoardingIntent(OnBoardingIntent.ChangeAgreePrivacy(!agreePrivacy)) + }, showArrow = true, onArrowClick = { // TODO: 개인정보 처리방침 이동 diff --git a/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt b/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt index e6a76da3..e6cc50ee 100644 --- a/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt +++ b/feature/onboarding/src/main/java/com/teamsolply/solply/onboarding/screen/SelectTownScreen.kt @@ -33,6 +33,7 @@ import com.teamsolply.solply.designsystem.theme.SolplyTheme import com.teamsolply.solply.onboarding.OnBoardingIntent import com.teamsolply.solply.onboarding.OnBoardingState import com.teamsolply.solply.onboarding.component.OnBoardingTownBottomSheet +import com.teamsolply.solply.onboarding.model.ParentTownEntity import com.teamsolply.solply.onboarding.model.SubTownEntity import com.teamsolply.solply.ui.extension.customClickable @@ -77,11 +78,13 @@ fun SelectTownScreen( ) } else { AddLocalAreaButton( - text = townList.towns - .flatMap { it.subTowns ?: emptyList() } + text = townList.parentTowns + .flatMap { it.subTowns } .find { it.townId == state.selectedTownId } ?.townName ?: "", - onClick = {}, + onClick = { + onBoardingIntent(OnBoardingIntent.ChangeTownBottomSheetShown) + }, selected = true ) } @@ -112,15 +115,9 @@ fun SelectTownScreen( ) { LeftRegionPane( borderColor = borderColor, - regions = townList.towns, + regions = townList.parentTowns, selectedRegionId = state.selectedRegionId, - onSelect = { id -> - onBoardingIntent( - OnBoardingIntent.ChangeRegion( - id - ) - ) - } + onSelect = { id -> onBoardingIntent(OnBoardingIntent.ChangeRegion(id)) } ) VerticalDivider( thickness = 1.dp, @@ -128,17 +125,11 @@ fun SelectTownScreen( ) RightTownPane( borderColor = borderColor, - towns = townList.towns + towns = townList.parentTowns .find { it.townId == state.selectedRegionId } ?.subTowns ?: emptyList(), selectedTownId = state.selectedTownId, - onSelect = { id -> - onBoardingIntent( - OnBoardingIntent.OnTownSelected( - id - ) - ) - } + onSelect = { id -> onBoardingIntent(OnBoardingIntent.OnTownSelected(id)) } ) } } @@ -184,7 +175,7 @@ fun SelectTownScreen( @Composable private fun LeftRegionPane( borderColor: Color, - regions: List, + regions: List, selectedRegionId: Long?, onSelect: (Long) -> Unit ) { @@ -194,7 +185,7 @@ private fun LeftRegionPane( .fillMaxHeight() .background(SolplyTheme.colors.gray100) ) { - itemsIndexed(items = regions) { index: Int, item: SubTownEntity -> + itemsIndexed(items = regions) { index: Int, item: ParentTownEntity -> val selected = selectedRegionId == item.townId val bg = if (selected) SolplyTheme.colors.white else SolplyTheme.colors.gray100 val textColor = if (selected) SolplyTheme.colors.black else SolplyTheme.colors.gray600 @@ -233,7 +224,7 @@ private fun LeftRegionPane( ) { Text( text = item.townName, - style = SolplyTheme.typography.body14M.copy(fontWeight = fontWeight), + style = SolplyTheme.typography.body16M.copy(fontWeight = fontWeight), color = textColor ) }