From 94bf86f02e04acea6b59ba551531b7b759e23b6f Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Tue, 30 Sep 2025 15:22:57 +0900 Subject: [PATCH 1/9] =?UTF-8?q?refactor#132=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=ED=99=94=EC=82=B4=ED=91=9C=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/teamsolply/solply/mypage/MypageScreen.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt index 94ee26f8..b0c7ecc4 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt @@ -93,12 +93,20 @@ fun MypageScreen( .customClickable( rippleEnabled = false, onClick = onProfileEditClick - ) + ), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically ) { Text( text = "프로필 수정", color = SolplyTheme.colors.gray600, - style = SolplyTheme.typography.button14M + style = SolplyTheme.typography.button14M, + modifier = Modifier.padding(start = 16.dp) + ) + Icon( + painter = painterResource(com.teamsolply.solply.designsystem.R.drawable.ic_next_arrow), + contentDescription = "", + tint = SolplyTheme.colors.gray600 ) } Spacer(modifier = Modifier.height(44.dp)) From dd74a6e3b128079e72c5a080c842853012f47a47 Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Wed, 1 Oct 2025 02:23:32 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feature#147=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=9C=A0=EC=A0=80=EC=A0=95=EB=B3=B4=20&?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=20=EC=9E=A5=EC=86=8C=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20api=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../solply/designsystem/theme/Typography.kt | 2 +- .../datasource/MypageRemoteDataSource.kt | 8 +++- .../dto/response/GetUserInfoResponseDto.kt | 24 +++++++++++ .../dto/response/PlaceListResponseDto.kt | 30 +++++++++++++ .../mypage/repository/MypageRepositoryImpl.kt | 33 +++++++++++++- .../solply/mypage/model/PlaceInfoEntity.kt | 12 ++++++ .../solply/mypage/model/UserInfo.kt | 13 ++++++ .../mypage/repository/MypageRepository.kt | 8 +++- .../solply/mypage/MypageContract.kt | 15 +++++-- .../teamsolply/solply/mypage/MypageScreen.kt | 41 +++++++++++++++--- .../solply/mypage/MypageViewModel.kt | 32 +++++++++++++- .../mypage/component/EmptyPlaceContainer.kt | 1 - .../component/SavedPlaceListContainer.kt | 43 +++++++++++++++++++ .../mypage/navigation/MypageNavigation.kt | 1 + .../datasource/MypageRemoteDataSourceImpl.kt | 9 +++- .../solply/mypage/service/MypageService.kt | 17 +++++++- 16 files changed, 271 insertions(+), 18 deletions(-) create mode 100644 data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt create mode 100644 data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/PlaceListResponseDto.kt create mode 100644 domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PlaceInfoEntity.kt create mode 100644 domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt create mode 100644 feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt diff --git a/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/theme/Typography.kt b/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/theme/Typography.kt index c26c0c12..e26d829c 100644 --- a/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/theme/Typography.kt +++ b/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/theme/Typography.kt @@ -208,7 +208,7 @@ fun SolplyTypography(): SolplyTypography { fontFamily = PretendardSemiBold, fontWeight = FontWeight.SemiBold, fontSize = 20.sp, - lineHeight = (20 * 1.5).sp + lineHeight = (20 * 1.5).sp, ), display16Sb = TextStyle( fontFamily = PretendardSemiBold, diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt index 4b07212c..1b0cc752 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt @@ -1,3 +1,9 @@ package com.teamsolply.solply.mypage.datasource -interface MypageRemoteDataSource +import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto +import com.teamsolply.solply.mypage.dto.response.PlaceListResponseDto + +interface MypageRemoteDataSource { + suspend fun getUserInfo(): GetUserInfoResponseDto + suspend fun getPlaceList(townId: Long): PlaceListResponseDto +} diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt new file mode 100644 index 00000000..1a08dc93 --- /dev/null +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt @@ -0,0 +1,24 @@ +package com.teamsolply.solply.mypage.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GetUserInfoResponseDto( + @SerialName("userId") + val userId: Long, + @SerialName("nickname") + val nickname: String, + @SerialName("selectedTown") + val selectedTown: SelectedTownDto, + @SerialName("persona") + val persona: String +) + +@Serializable +data class SelectedTownDto( + @SerialName("townId") + val townId: Long, + @SerialName("townName") + val townName: String +) diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/PlaceListResponseDto.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/PlaceListResponseDto.kt new file mode 100644 index 00000000..a9d1cf92 --- /dev/null +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/PlaceListResponseDto.kt @@ -0,0 +1,30 @@ +package com.teamsolply.solply.mypage.dto.response + +import com.teamsolply.solply.model.PlaceType +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class PlaceListResponseDto( + + @SerialName("places") + val placeList: List +) + +@Serializable +data class PlaceResponseDto( + @SerialName("placeId") + val placeId: Long, + + @SerialName("placeName") + val placeName: String, + + @SerialName("thumbnailImageUrl") + val imageUrl: String, + + @SerialName("primaryTag") + val tag: PlaceType, + + @SerialName("isBookmarked") + val isSaved: Boolean +) diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt index 68ada57a..a939190e 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt @@ -1,8 +1,39 @@ package com.teamsolply.solply.mypage.repository import com.teamsolply.solply.mypage.datasource.MypageRemoteDataSource +import com.teamsolply.solply.mypage.model.PlaceInfoEntity +import com.teamsolply.solply.mypage.model.SelectedTownInfo +import com.teamsolply.solply.mypage.model.UserInfo import javax.inject.Inject class MypageRepositoryImpl @Inject constructor( private val mypageRemoteDataSource: MypageRemoteDataSource -) : MypageRepository +) : MypageRepository { + override suspend fun getUserInfo(): Result = runCatching { + mypageRemoteDataSource.getUserInfo() + }.mapCatching { userDto -> + UserInfo( + userId = userDto.userId, + nickname = userDto.nickname, + selectedTown = SelectedTownInfo( + townId = userDto.selectedTown.townId, + townName = userDto.selectedTown.townName + ), + persona = userDto.persona + ) + } + + override suspend fun getPlaceList(townId: Long): Result> = runCatching { + mypageRemoteDataSource.getPlaceList(townId).placeList + }.mapCatching { placeList -> + placeList.map { place -> + PlaceInfoEntity( + placeId = place.placeId, + placeName = place.placeName, + placeType = place.tag, + imageUrls = place.imageUrl, + isSaved = place.isSaved + ) + } + } +} diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PlaceInfoEntity.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PlaceInfoEntity.kt new file mode 100644 index 00000000..d391c0cc --- /dev/null +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PlaceInfoEntity.kt @@ -0,0 +1,12 @@ +package com.teamsolply.solply.mypage.model + +import com.teamsolply.solply.model.PlaceType + +data class PlaceInfoEntity( + val placeId: Long, + val placeName: String, + val placeType: PlaceType, + val imageUrls: String, + val isSaved: Boolean, + val isSelected: Boolean = false +) diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt new file mode 100644 index 00000000..dcf45590 --- /dev/null +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt @@ -0,0 +1,13 @@ +package com.teamsolply.solply.mypage.model + +data class UserInfo( + val userId: Long, + val nickname: String, + val selectedTown: SelectedTownInfo, + val persona: String +) + +data class SelectedTownInfo( + val townId: Long, + val townName: String +) diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt index bcab9603..bba9518a 100644 --- a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt @@ -1,3 +1,9 @@ package com.teamsolply.solply.mypage.repository -interface MypageRepository +import com.teamsolply.solply.mypage.model.PlaceInfoEntity +import com.teamsolply.solply.mypage.model.UserInfo + +interface MypageRepository { + suspend fun getUserInfo(): Result + suspend fun getPlaceList(townId: Long): Result> +} diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt index a8cdbe30..c98769c4 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt @@ -1,18 +1,25 @@ package com.teamsolply.solply.mypage -import com.teamsolply.solply.mypage.model.DropDownPersonaItem +import com.teamsolply.solply.mypage.model.PlaceInfoEntity +import com.teamsolply.solply.mypage.model.SelectedTownInfo +import com.teamsolply.solply.mypage.model.UserInfo import com.teamsolply.solply.ui.base.SideEffect import com.teamsolply.solply.ui.base.UiIntent import com.teamsolply.solply.ui.base.UiState -import kotlinx.collections.immutable.persistentListOf data class MypageState( - val town: String = "연희동", + val userInfo: UserInfo = UserInfo( + userId = 1, + nickname = "숭이숭이숭이", + selectedTown = SelectedTownInfo(0, "망원동"), + persona = "REST" + ), val nickname: String = "", + val placeList: List = emptyList(), ) : UiState sealed interface MypageIntent : UiIntent { - + data object Init : MypageIntent } sealed interface MypageSideEffect : SideEffect { diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt index b0c7ecc4..759864d4 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt @@ -16,18 +16,24 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.LineHeightStyle 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.teamsolply.solply.designsystem.theme.SolplyTheme import com.teamsolply.solply.mypage.component.EmptyPlaceContainer import com.teamsolply.solply.mypage.component.MypageSettingItem +import com.teamsolply.solply.mypage.component.SavedPlaceListContainer +import com.teamsolply.solply.mypage.model.PlaceInfoEntity import com.teamsolply.solply.ui.extension.customClickable @Composable @@ -37,7 +43,14 @@ fun MypageRoute( navigateToProfile: () -> Unit, viewModel: MypageViewModel = hiltViewModel() ) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + LaunchedEffect(Unit) { + viewModel.sendIntent(MypageIntent.Init) + } + MypageScreen( + savedPlaceList = uiState.placeList, onBackButtonClick = navigateToBack, onProfileEditClick = navigateToProfile, modifier = Modifier.padding(paddingValues), @@ -46,10 +59,12 @@ fun MypageRoute( @Composable fun MypageScreen( + savedPlaceList: List, onBackButtonClick: () -> Unit, onProfileEditClick: () -> Unit, modifier: Modifier = Modifier ) { + Column( modifier .fillMaxSize() @@ -85,11 +100,16 @@ fun MypageScreen( Text( text = "닉네임", color = SolplyTheme.colors.black, - style = SolplyTheme.typography.display20Sb + style = SolplyTheme.typography.display20Sb.copy( + lineHeightStyle = LineHeightStyle( + alignment = LineHeightStyle.Alignment.Center, + trim = LineHeightStyle.Trim.None + ) + ), ) + Spacer(modifier = Modifier.height(12.dp)) Row( modifier = Modifier - .padding(top = 12.dp) .customClickable( rippleEnabled = false, onClick = onProfileEditClick @@ -128,7 +148,18 @@ fun MypageScreen( style = SolplyTheme.typography.body16M ) } - EmptyPlaceContainer() + if (savedPlaceList.isEmpty()) { + EmptyPlaceContainer( + modifier = Modifier + .padding(bottom = 16.dp) + ) + } else { + SavedPlaceListContainer( + savedPlaceList = savedPlaceList, + modifier = Modifier + .padding(start = 20.dp, bottom = 16.dp) + ) + } } Spacer(modifier = Modifier.height(16.dp)) Column( @@ -177,9 +208,6 @@ fun MypageScreen( isBorderEnabled = false ) } - Spacer( - modifier = Modifier.weight(36f) - ) } } @@ -188,6 +216,7 @@ fun MypageScreen( private fun MypageScreenPreview() { SolplyTheme { MypageScreen( + savedPlaceList = emptyList(), onBackButtonClick = {}, onProfileEditClick = {} ) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt index 1884a3a1..1edfe8ef 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt @@ -1,8 +1,11 @@ package com.teamsolply.solply.mypage +import androidx.lifecycle.viewModelScope import com.teamsolply.solply.mypage.repository.MypageRepository import com.teamsolply.solply.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.collections.immutable.toPersistentList +import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel @@ -11,6 +14,33 @@ class MypageViewModel @Inject constructor( ) : BaseViewModel(MypageState()) { override fun handleIntent(intent: MypageIntent) { - TODO("Not yet implemented") + when (intent) { + MypageIntent.Init -> fetchInitInfo() + } + } + + private fun fetchInitInfo() { + viewModelScope.launch { + mypageRepository.getUserInfo() + .onSuccess { userInfo -> + reduce { copy(userInfo = userInfo) } + + getPlaceList( + townId = userInfo.selectedTown.townId, + ) + } + } + } + + private fun getPlaceList(townId: Long) { + viewModelScope.launch { + mypageRepository.getPlaceList(townId).onSuccess { + reduce { + copy( + placeList = it.toPersistentList() + ) + } + } + } } } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/EmptyPlaceContainer.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/EmptyPlaceContainer.kt index 270abe9d..00e66430 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/EmptyPlaceContainer.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/EmptyPlaceContainer.kt @@ -17,7 +17,6 @@ fun EmptyPlaceContainer( ) { Box( modifier = modifier - .padding(bottom = 16.dp) .height(40.dp) .fillMaxWidth(), contentAlignment = Alignment.Center diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt new file mode 100644 index 00000000..5cdb6f52 --- /dev/null +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt @@ -0,0 +1,43 @@ +package com.teamsolply.solply.mypage.component + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import com.teamsolply.solply.designsystem.component.card.SolplyPlaceCard +import com.teamsolply.solply.designsystem.theme.SolplyTheme +import com.teamsolply.solply.mypage.model.PlaceInfoEntity + +@Composable +fun SavedPlaceListContainer( + savedPlaceList: List, + modifier: Modifier = Modifier, +) { + LazyRow( + modifier = modifier + .fillMaxWidth(), + ) { + items(savedPlaceList) { + SolplyPlaceCard( + name = it.placeName, + placeType = it.placeType, + imgRes = it.imageUrls, + selected = it.isSelected, + saved = it.isSaved, + touchable = false, + ) + } + } +} + +@Preview +@Composable +private fun SavedPlaceListContainerPreview() { + SolplyTheme { + SavedPlaceListContainer( + savedPlaceList = emptyList() + ) + } +} \ No newline at end of file diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/navigation/MypageNavigation.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/navigation/MypageNavigation.kt index 8937319c..d4dc3f3c 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/navigation/MypageNavigation.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/navigation/MypageNavigation.kt @@ -6,6 +6,7 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable +import androidx.navigation.toRoute import com.teamsolply.solply.mypage.MypageRoute import com.teamsolply.solply.mypage.MypageViewModel import com.teamsolply.solply.navigation.Route diff --git a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt index a19777cb..4065f92f 100644 --- a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt +++ b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt @@ -1,8 +1,15 @@ package com.teamsolply.solply.mypage.datasource +import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto import com.teamsolply.solply.mypage.service.MypageService import javax.inject.Inject class MypageRemoteDataSourceImpl @Inject constructor( private val mypageService: MypageService -) : MypageRemoteDataSource +) : MypageRemoteDataSource { + override suspend fun getUserInfo(): GetUserInfoResponseDto { + return mypageService.getUserInfo().data + } + override suspend fun getPlaceList(townId: Long) = + mypageService.getPlaceList(townId = townId).data +} diff --git a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt index 3d052ee7..74210e7e 100644 --- a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt +++ b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt @@ -1,3 +1,18 @@ package com.teamsolply.solply.mypage.service -interface MypageService +import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto +import com.teamsolply.solply.mypage.dto.response.PlaceListResponseDto +import com.teamsolply.solply.network.model.BaseResponse +import retrofit2.http.GET +import retrofit2.http.Query + +interface MypageService { + @GET("/api/users") + suspend fun getUserInfo(): BaseResponse + + @GET("api/places") + suspend fun getPlaceList( + @Query("townId") townId: Long, + @Query("isBookmarkSearch") isBookmarkedSearch: Boolean = true + ): BaseResponse +} From 0393a336d89575d3afd4196a52d7ce7ee3ae8d50 Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Wed, 1 Oct 2025 02:23:58 +0900 Subject: [PATCH 3/9] =?UTF-8?q?refactor#147=20=ED=83=88=ED=87=B4=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=ED=95=98=EB=8B=A8=20=EC=97=AC=EB=B0=B1=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt index 759864d4..729ce296 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt @@ -207,6 +207,7 @@ fun MypageScreen( onClick = { /* TODO */ }, isBorderEnabled = false ) + Spacer(modifier = Modifier.height(8.dp)) } } } From bf74e55dcc364f27435c80709d9e3a4c142ab576 Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Wed, 1 Oct 2025 03:28:14 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feature#147=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20dialog=20=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../solply/mypage/MypageContract.kt | 6 ++++ .../teamsolply/solply/mypage/MypageScreen.kt | 32 ++++++++++++++++--- .../solply/mypage/MypageViewModel.kt | 25 +++++++++++++-- .../mypage/component/MypageSettingItem.kt | 5 +++ .../mypage/src/main/res/values/strings.xml | 6 ++++ 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt index c98769c4..91858df4 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt @@ -16,10 +16,16 @@ data class MypageState( ), val nickname: String = "", val placeList: List = emptyList(), + val dialogState: Boolean = false ) : UiState sealed interface MypageIntent : UiIntent { data object Init : MypageIntent + + data object LogOutButtonClick : MypageIntent + + data object DialogConfirmClick : MypageIntent + data object DialogDismissClick : MypageIntent } sealed interface MypageSideEffect : SideEffect { diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt index 729ce296..cf1c460c 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt @@ -24,11 +24,13 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.LineHeightStyle 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.teamsolply.solply.designsystem.component.dialog.SolplyConfirmDialog import com.teamsolply.solply.designsystem.theme.SolplyTheme import com.teamsolply.solply.mypage.component.EmptyPlaceContainer import com.teamsolply.solply.mypage.component.MypageSettingItem @@ -51,8 +53,12 @@ fun MypageRoute( MypageScreen( savedPlaceList = uiState.placeList, + dialogState = uiState.dialogState, onBackButtonClick = navigateToBack, onProfileEditClick = navigateToProfile, + onLogOutClick = { viewModel.sendIntent(MypageIntent.LogOutButtonClick) }, + onDialogConfirmClick = { viewModel.sendIntent(MypageIntent.DialogConfirmClick) }, + onDialogDismissClick = { viewModel.sendIntent(MypageIntent.DialogDismissClick) }, modifier = Modifier.padding(paddingValues), ) } @@ -60,11 +66,23 @@ fun MypageRoute( @Composable fun MypageScreen( savedPlaceList: List, + dialogState: Boolean, onBackButtonClick: () -> Unit, onProfileEditClick: () -> Unit, + onLogOutClick: () -> Unit, + onDialogConfirmClick: () -> Unit, + onDialogDismissClick: () -> Unit, modifier: Modifier = Modifier ) { - + if (dialogState) { + SolplyConfirmDialog( + text = stringResource(R.string.logout_dialog), + confirmButtonText = stringResource(R.string.logout_dialog_confirm), + dismissButtonText = stringResource(R.string.mypage_dialog_cancel), + onClickConfirm = onDialogConfirmClick, + onClickDismiss = onDialogDismissClick + ) + } Column( modifier .fillMaxSize() @@ -118,7 +136,7 @@ fun MypageScreen( verticalAlignment = Alignment.CenterVertically ) { Text( - text = "프로필 수정", + text = stringResource(R.string.profile_edit), color = SolplyTheme.colors.gray600, style = SolplyTheme.typography.button14M, modifier = Modifier.padding(start = 16.dp) @@ -198,8 +216,8 @@ fun MypageScreen( isBorderEnabled = true ) MypageSettingItem( - text = "로그아웃", - onClick = { /* TODO */ }, + text = stringResource(R.string.mypage_logout), + onClick = onLogOutClick, isBorderEnabled = true ) MypageSettingItem( @@ -218,8 +236,12 @@ private fun MypageScreenPreview() { SolplyTheme { MypageScreen( savedPlaceList = emptyList(), + dialogState = false, onBackButtonClick = {}, - onProfileEditClick = {} + onProfileEditClick = {}, + onLogOutClick = {}, + onDialogConfirmClick = {}, + onDialogDismissClick = {}, ) } } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt index 1edfe8ef..fc8c4716 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt @@ -15,11 +15,32 @@ class MypageViewModel @Inject constructor( BaseViewModel(MypageState()) { override fun handleIntent(intent: MypageIntent) { when (intent) { - MypageIntent.Init -> fetchInitInfo() + MypageIntent.Init -> getInitInfo() + + MypageIntent.LogOutButtonClick -> { + reduce { + copy( + dialogState = true + ) + } + } + + MypageIntent.DialogConfirmClick -> { + // TODO 로그아웃 api + reduce { + copy(dialogState = false) + } + } + + MypageIntent.DialogDismissClick -> { + reduce { + copy(dialogState = false) + } + } } } - private fun fetchInitInfo() { + private fun getInitInfo() { viewModelScope.launch { mypageRepository.getUserInfo() .onSuccess { userInfo -> diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/MypageSettingItem.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/MypageSettingItem.kt index 366d9893..59b1aaeb 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/MypageSettingItem.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/MypageSettingItem.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.dp import com.teamsolply.solply.designsystem.theme.SolplyTheme +import com.teamsolply.solply.ui.extension.customClickable @Composable fun MypageSettingItem( @@ -41,6 +42,10 @@ fun MypageSettingItem( } else { Modifier } + ) + .customClickable( + rippleEnabled = false, + onClick = onClick ), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = if (info.isNotEmpty()) { diff --git a/feature/mypage/src/main/res/values/strings.xml b/feature/mypage/src/main/res/values/strings.xml index 4bd40bea..3debfbb7 100644 --- a/feature/mypage/src/main/res/values/strings.xml +++ b/feature/mypage/src/main/res/values/strings.xml @@ -1,7 +1,13 @@ + 로그아웃 + 프로필 수정 기본 프로필 닉네임 나의 솔플 스타일 + + 로그아웃하시겠습니까? + 취소 + 로그아웃 \ No newline at end of file From 97ae6190e21f914c7c244c905c4809e962913a06 Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Wed, 1 Oct 2025 17:24:30 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feature#147=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EB=B7=B0=EB=AA=A8=EB=8D=B8=20=EB=B0=8F?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../teamsolply/solply/mypage/MypageScreen.kt | 4 +-- .../mypage/profile/ProfileEditContract.kt | 31 +++++++++++++++++++ ...{ProfileScreen.kt => ProfileEditScreen.kt} | 6 ++-- .../mypage/profile/ProfileEditViewModel.kt | 28 +++++++++++++++++ .../mypage/src/main/res/values/strings.xml | 2 ++ 5 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt rename feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/{ProfileScreen.kt => ProfileEditScreen.kt} (98%) create mode 100644 feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt index cf1c460c..ad89dfd8 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt @@ -193,13 +193,13 @@ fun MypageScreen( .padding(top = 16.dp, start = 20.dp, end = 16.dp, bottom = 12.dp) ) { Text( - text = "계정 설정", + text = stringResource(R.string.mypage_account_setting), color = SolplyTheme.colors.black, style = SolplyTheme.typography.body16M ) } MypageSettingItem( - text = "고객센터", + text = stringResource(R.string.mypage_customer_service), onClick = { /* TODO */ }, isBorderEnabled = true ) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt new file mode 100644 index 00000000..6102f3b3 --- /dev/null +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt @@ -0,0 +1,31 @@ +package com.teamsolply.solply.mypage.profile + +import com.teamsolply.solply.mypage.model.PlaceInfoEntity +import com.teamsolply.solply.mypage.model.SelectedTownInfo +import com.teamsolply.solply.mypage.model.UserInfo +import com.teamsolply.solply.ui.base.SideEffect +import com.teamsolply.solply.ui.base.UiIntent +import com.teamsolply.solply.ui.base.UiState + +data class ProfileEditState( + val userInfo: UserInfo = UserInfo( + userId = 1, + nickname = "숭이숭이숭이", + selectedTown = SelectedTownInfo(0, "망원동"), + persona = "REST" + ), + val personaList: List = emptyList() +) : UiState + +sealed interface ProfileEditIntent : UiIntent { + data object Init : ProfileEditIntent + + data object DialogConfirmClick : ProfileEditIntent + data object DialogDismissClick : ProfileEditIntent +} + +sealed interface ProfileEditSideEffect : SideEffect { + data object NavigateToBack : ProfileEditSideEffect + data object NavigateToProfile : ProfileEditSideEffect + data object NavigateToMypage : ProfileEditSideEffect +} diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt similarity index 98% rename from feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileScreen.kt rename to feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt index 4fd3841e..6f059565 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt @@ -40,7 +40,7 @@ fun ProfileRoute( navigateToMypage: () -> Unit, viewModel: MypageViewModel = hiltViewModel() ) { - ProfileScreen( + ProfileEditScreen( onBackButtonClick = navigateToBack, onCompleteButtonClick = navigateToMypage, modifier = Modifier.padding(paddingValues), @@ -48,7 +48,7 @@ fun ProfileRoute( } @Composable -fun ProfileScreen( +fun ProfileEditScreen( onBackButtonClick: () -> Unit, onCompleteButtonClick: () -> Unit, modifier: Modifier = Modifier @@ -141,7 +141,7 @@ fun ProfileScreen( @Composable private fun ProfileScreenPreview() { SolplyTheme { - ProfileScreen( + ProfileEditScreen( onBackButtonClick = {}, onCompleteButtonClick = {} ) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt new file mode 100644 index 00000000..f7b471b3 --- /dev/null +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt @@ -0,0 +1,28 @@ +package com.teamsolply.solply.mypage.profile + +import androidx.lifecycle.viewModelScope +import com.teamsolply.solply.mypage.repository.MypageRepository +import com.teamsolply.solply.ui.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class ProfileEditViewModel @Inject constructor( + private val mypageRepository: MypageRepository +) : + BaseViewModel(ProfileEditState()) { + + override fun handleIntent(intent: ProfileEditIntent) { + TODO("Not yet implemented") + } + + private fun getUserInfo() { + viewModelScope.launch { + mypageRepository.getUserInfo() + .onSuccess { userInfo -> + reduce { copy(userInfo = userInfo) } + } + } + } +} \ No newline at end of file diff --git a/feature/mypage/src/main/res/values/strings.xml b/feature/mypage/src/main/res/values/strings.xml index 3debfbb7..0e2db4fd 100644 --- a/feature/mypage/src/main/res/values/strings.xml +++ b/feature/mypage/src/main/res/values/strings.xml @@ -1,5 +1,7 @@ + 계정 설정 + 고객센터 로그아웃 프로필 수정 From 2a8a3e44f6926377bac307acf74e7ce2d297280e Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Wed, 1 Oct 2025 18:52:22 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feature#147=20=ED=94=84=EB=A1=9C=ED=95=84?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=ED=8E=98=EB=A5=B4=EC=86=8C=EB=82=98=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../datasource/MypageRemoteDataSource.kt | 2 ++ .../dto/response/GetPersonaListResponseDto.kt | 18 +++++++++++++ .../mypage/repository/MypageRepositoryImpl.kt | 12 +++++++++ .../solply/mypage/model/PersonaEntity.kt | 6 +++++ .../mypage/repository/MypageRepository.kt | 2 ++ .../mypage/profile/ProfileEditContract.kt | 7 +++--- .../mypage/profile/ProfileEditScreen.kt | 2 +- .../mypage/profile/ProfileEditViewModel.kt | 25 ++++++++++++++++++- .../datasource/MypageRemoteDataSourceImpl.kt | 6 +++++ .../solply/mypage/service/MypageService.kt | 4 +++ 10 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetPersonaListResponseDto.kt create mode 100644 domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PersonaEntity.kt diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt index 1b0cc752..7b1a0c5b 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt @@ -1,9 +1,11 @@ package com.teamsolply.solply.mypage.datasource +import com.teamsolply.solply.mypage.dto.response.GetPersonaListResponseDto import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto import com.teamsolply.solply.mypage.dto.response.PlaceListResponseDto interface MypageRemoteDataSource { suspend fun getUserInfo(): GetUserInfoResponseDto suspend fun getPlaceList(townId: Long): PlaceListResponseDto + suspend fun getPersonaList(): GetPersonaListResponseDto } diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetPersonaListResponseDto.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetPersonaListResponseDto.kt new file mode 100644 index 00000000..a8377d43 --- /dev/null +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetPersonaListResponseDto.kt @@ -0,0 +1,18 @@ +package com.teamsolply.solply.mypage.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GetPersonaListResponseDto( + @SerialName("personaList") + val personaDtoList: List +) + +@Serializable +data class PersonaDto( + @SerialName("personaType") + val personaType: String, + @SerialName("description") + val description: String +) diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt index a939190e..f36ec097 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt @@ -1,6 +1,7 @@ package com.teamsolply.solply.mypage.repository import com.teamsolply.solply.mypage.datasource.MypageRemoteDataSource +import com.teamsolply.solply.mypage.model.PersonaEntity import com.teamsolply.solply.mypage.model.PlaceInfoEntity import com.teamsolply.solply.mypage.model.SelectedTownInfo import com.teamsolply.solply.mypage.model.UserInfo @@ -36,4 +37,15 @@ class MypageRepositoryImpl @Inject constructor( ) } } + + override suspend fun getPersonaList(): Result> = runCatching { + mypageRemoteDataSource.getPersonaList().personaDtoList + }.mapCatching { personaList -> + personaList.map { persona -> + PersonaEntity( + personaType = persona.personaType, + description = persona.description + ) + } + } } diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PersonaEntity.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PersonaEntity.kt new file mode 100644 index 00000000..138d7f3d --- /dev/null +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PersonaEntity.kt @@ -0,0 +1,6 @@ +package com.teamsolply.solply.mypage.model + +data class PersonaEntity( + val personaType: String, + val description: String +) diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt index bba9518a..0da6660b 100644 --- a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt @@ -1,9 +1,11 @@ package com.teamsolply.solply.mypage.repository +import com.teamsolply.solply.mypage.model.PersonaEntity import com.teamsolply.solply.mypage.model.PlaceInfoEntity import com.teamsolply.solply.mypage.model.UserInfo interface MypageRepository { suspend fun getUserInfo(): Result suspend fun getPlaceList(townId: Long): Result> + suspend fun getPersonaList(): Result> } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt index 6102f3b3..22207775 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt @@ -1,6 +1,6 @@ package com.teamsolply.solply.mypage.profile -import com.teamsolply.solply.mypage.model.PlaceInfoEntity +import com.teamsolply.solply.mypage.model.PersonaEntity import com.teamsolply.solply.mypage.model.SelectedTownInfo import com.teamsolply.solply.mypage.model.UserInfo import com.teamsolply.solply.ui.base.SideEffect @@ -14,14 +14,13 @@ data class ProfileEditState( selectedTown = SelectedTownInfo(0, "망원동"), persona = "REST" ), - val personaList: List = emptyList() + val personaList: List = emptyList() ) : UiState sealed interface ProfileEditIntent : UiIntent { data object Init : ProfileEditIntent - data object DialogConfirmClick : ProfileEditIntent - data object DialogDismissClick : ProfileEditIntent + data object CompleteButtonClick : ProfileEditIntent } sealed interface ProfileEditSideEffect : SideEffect { diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt index 6f059565..10903a92 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt @@ -38,7 +38,7 @@ fun ProfileRoute( paddingValues: PaddingValues, navigateToBack: () -> Unit, navigateToMypage: () -> Unit, - viewModel: MypageViewModel = hiltViewModel() + viewModel: ProfileEditViewModel = hiltViewModel() ) { ProfileEditScreen( onBackButtonClick = navigateToBack, diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt index f7b471b3..6e7db721 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt @@ -14,7 +14,21 @@ class ProfileEditViewModel @Inject constructor( BaseViewModel(ProfileEditState()) { override fun handleIntent(intent: ProfileEditIntent) { - TODO("Not yet implemented") + when (intent) { + ProfileEditIntent.Init -> init() + + ProfileEditIntent.CompleteButtonClick -> TODO() + } + } + + private fun init() { + getUserInfo() + getPersonaList() + reduce { + copy( + // TODO + ) + } } private fun getUserInfo() { @@ -25,4 +39,13 @@ class ProfileEditViewModel @Inject constructor( } } } + + private fun getPersonaList() { + viewModelScope.launch { + mypageRepository.getPersonaList() + .onSuccess { personaList -> + reduce { copy(personaList = personaList) } + } + } + } } \ No newline at end of file diff --git a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt index 4065f92f..7bc42668 100644 --- a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt +++ b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt @@ -1,5 +1,6 @@ package com.teamsolply.solply.mypage.datasource +import com.teamsolply.solply.mypage.dto.response.GetPersonaListResponseDto import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto import com.teamsolply.solply.mypage.service.MypageService import javax.inject.Inject @@ -10,6 +11,11 @@ class MypageRemoteDataSourceImpl @Inject constructor( override suspend fun getUserInfo(): GetUserInfoResponseDto { return mypageService.getUserInfo().data } + override suspend fun getPlaceList(townId: Long) = mypageService.getPlaceList(townId = townId).data + + override suspend fun getPersonaList(): GetPersonaListResponseDto { + return mypageService.getPersonaList().data + } } diff --git a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt index 74210e7e..0127a3d6 100644 --- a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt +++ b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt @@ -1,5 +1,6 @@ package com.teamsolply.solply.mypage.service +import com.teamsolply.solply.mypage.dto.response.GetPersonaListResponseDto import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto import com.teamsolply.solply.mypage.dto.response.PlaceListResponseDto import com.teamsolply.solply.network.model.BaseResponse @@ -15,4 +16,7 @@ interface MypageService { @Query("townId") townId: Long, @Query("isBookmarkSearch") isBookmarkedSearch: Boolean = true ): BaseResponse + + @GET("api/onboarding/questions/persona") + suspend fun getPersonaList(): BaseResponse } From ba0531a9d7cac8c18a233c90afd43636113e2151 Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Sat, 4 Oct 2025 02:54:32 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feature#147=20Persona=20enum=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/teamsolply/solply/model/Persona.kt | 11 ++++++ .../dto/response/GetPersonaListResponseDto.kt | 3 +- .../dto/response/GetUserInfoResponseDto.kt | 3 +- .../solply/mypage/model/PersonaEntity.kt | 4 +- .../solply/mypage/model/UserInfo.kt | 4 +- .../solply/mypage/MypageContract.kt | 3 +- .../mypage/component/SolplyPersonaDropDown.kt | 38 +++---------------- .../mypage/profile/ProfileEditContract.kt | 6 ++- .../mypage/profile/ProfileEditScreen.kt | 29 ++++++++++++-- .../mypage/profile/ProfileEditViewModel.kt | 19 ++-------- .../profile/navigation/ProfileNavigation.kt | 3 +- 11 files changed, 64 insertions(+), 59 deletions(-) create mode 100644 core/model/src/main/java/com/teamsolply/solply/model/Persona.kt diff --git a/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt b/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt new file mode 100644 index 00000000..9dabc4b2 --- /dev/null +++ b/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt @@ -0,0 +1,11 @@ +package com.teamsolply.solply.model + +import kotlinx.serialization.Serializable + +@Serializable +enum class Persona(val description: String) { + REST("조용한 공간에 오래 머물고 싶어요"), + EXPLORER("이곳저곳 둘러보고 싶어요"), + MOODING("취향이 담긴 곳을 찾고 싶어요"), + NATURAL("자연을 감상하며 쉬고 싶어요") +} \ No newline at end of file diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetPersonaListResponseDto.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetPersonaListResponseDto.kt index a8377d43..fc41fce3 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetPersonaListResponseDto.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetPersonaListResponseDto.kt @@ -1,5 +1,6 @@ package com.teamsolply.solply.mypage.dto.response +import com.teamsolply.solply.model.Persona import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -12,7 +13,7 @@ data class GetPersonaListResponseDto( @Serializable data class PersonaDto( @SerialName("personaType") - val personaType: String, + val personaType: Persona, @SerialName("description") val description: String ) diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt index 1a08dc93..9e4a11fa 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt @@ -1,5 +1,6 @@ package com.teamsolply.solply.mypage.dto.response +import com.teamsolply.solply.model.Persona import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -12,7 +13,7 @@ data class GetUserInfoResponseDto( @SerialName("selectedTown") val selectedTown: SelectedTownDto, @SerialName("persona") - val persona: String + val persona: Persona ) @Serializable diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PersonaEntity.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PersonaEntity.kt index 138d7f3d..aff00551 100644 --- a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PersonaEntity.kt +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/PersonaEntity.kt @@ -1,6 +1,8 @@ package com.teamsolply.solply.mypage.model +import com.teamsolply.solply.model.Persona + data class PersonaEntity( - val personaType: String, + val personaType: Persona, val description: String ) diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt index dcf45590..21399b65 100644 --- a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt @@ -1,10 +1,12 @@ package com.teamsolply.solply.mypage.model +import com.teamsolply.solply.model.Persona + data class UserInfo( val userId: Long, val nickname: String, val selectedTown: SelectedTownInfo, - val persona: String + val persona: Persona ) data class SelectedTownInfo( diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt index 91858df4..0b403b0f 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt @@ -1,5 +1,6 @@ package com.teamsolply.solply.mypage +import com.teamsolply.solply.model.Persona import com.teamsolply.solply.mypage.model.PlaceInfoEntity import com.teamsolply.solply.mypage.model.SelectedTownInfo import com.teamsolply.solply.mypage.model.UserInfo @@ -12,7 +13,7 @@ data class MypageState( userId = 1, nickname = "숭이숭이숭이", selectedTown = SelectedTownInfo(0, "망원동"), - persona = "REST" + persona = Persona.REST ), val nickname: String = "", val placeList: List = emptyList(), diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt index 00046180..82133dea 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.teamsolply.solply.designsystem.component.dropdown.SolplyBasicDropDown import com.teamsolply.solply.designsystem.theme.SolplyTheme -import com.teamsolply.solply.mypage.model.DropDownPersonaItem +import com.teamsolply.solply.mypage.model.PersonaEntity import com.teamsolply.solply.ui.extension.customClickable import kotlinx.collections.immutable.persistentListOf @@ -29,14 +29,14 @@ fun SolplyPersonaDropDown( placeholder: String, onClickItem: (Int) -> Unit, onClickDropIcon: () -> Unit, - dropDownContents: List, + dropDownContents: List, selectedIndex: Int, modifier: Modifier = Modifier, isDropped: Boolean = false, isSelected: Boolean = false ) { SolplyBasicDropDown( - defaultLabel = if (isSelected) dropDownContents.get(selectedIndex).label else placeholder, + defaultLabel = if (isSelected) dropDownContents.get(selectedIndex).description else placeholder, onClickDropIcon = onClickDropIcon, isDropped = isDropped, modifier = modifier @@ -57,7 +57,7 @@ fun SolplyPersonaDropDown( horizontalArrangement = Arrangement.Start ) { Text( - text = item.label, + text = item.description, color = SolplyTheme.colors.gray900, style = SolplyTheme.typography.body16M, modifier = Modifier.padding( @@ -89,20 +89,7 @@ private fun SolplyPersonaDropDownPreview() { placeholder = "조용한 공간에 오래 머물고 싶어요", onClickItem = {}, onClickDropIcon = {}, - dropDownContents = persistentListOf( - DropDownPersonaItem( - "이곳저곳 둘러보고 싶어요" - ), - DropDownPersonaItem( - "취향이 담긴 곳을 찾고 싶어요" - ), - DropDownPersonaItem( - "자연을 감상하며 쉬고 싶어요" - ), - DropDownPersonaItem( - "조용한 공간에 오래 머물고 싶어요" - ), - ), + dropDownContents = persistentListOf(), isDropped = false, selectedIndex = 0, isSelected = false @@ -115,20 +102,7 @@ private fun SolplyPersonaDropDownPreview() { isSelected = true }, onClickDropIcon = { isDropped = !isDropped }, - dropDownContents = persistentListOf( - DropDownPersonaItem( - "이곳저곳 둘러보고 싶어요" - ), - DropDownPersonaItem( - "취향이 담긴 곳을 찾고 싶어요" - ), - DropDownPersonaItem( - "자연을 감상하며 쉬고 싶어요" - ), - DropDownPersonaItem( - "조용한 공간에 오래 머물고 싶어요" - ), - ), + dropDownContents = persistentListOf(), selectedIndex = selectedIndex, isSelected = isSelected ) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt index 22207775..aea24a7c 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt @@ -1,5 +1,6 @@ package com.teamsolply.solply.mypage.profile +import com.teamsolply.solply.model.Persona import com.teamsolply.solply.mypage.model.PersonaEntity import com.teamsolply.solply.mypage.model.SelectedTownInfo import com.teamsolply.solply.mypage.model.UserInfo @@ -12,9 +13,10 @@ data class ProfileEditState( userId = 1, nickname = "숭이숭이숭이", selectedTown = SelectedTownInfo(0, "망원동"), - persona = "REST" + persona = Persona.REST ), - val personaList: List = emptyList() + val personaList: List = emptyList(), + val selectedPersonaIndex: Int = -1, ) : UiState sealed interface ProfileEditIntent : UiIntent { diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt index 10903a92..301db96e 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt @@ -15,6 +15,8 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape 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 import androidx.compose.ui.draw.clip @@ -24,14 +26,17 @@ 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.teamsolply.solply.designsystem.component.button.SolplyBasicButton import com.teamsolply.solply.designsystem.component.textfield.SolplyNicknameTextField import com.teamsolply.solply.designsystem.component.topbar.SolplyTopBar import com.teamsolply.solply.designsystem.theme.SolplyTheme -import com.teamsolply.solply.mypage.MypageViewModel import com.teamsolply.solply.mypage.R import com.teamsolply.solply.mypage.component.SolplyPersonaDropDown +import com.teamsolply.solply.mypage.model.PersonaEntity +import com.teamsolply.solply.ui.lifecycle.LaunchedEffectWithLifecycle import kotlinx.collections.immutable.persistentListOf +import kotlinx.coroutines.flow.collectLatest @Composable fun ProfileRoute( @@ -40,7 +45,19 @@ fun ProfileRoute( navigateToMypage: () -> Unit, viewModel: ProfileEditViewModel = hiltViewModel() ) { + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + + LaunchedEffect(Unit) { + viewModel.sendIntent(ProfileEditIntent.Init) + } + + LaunchedEffectWithLifecycle { + viewModel.sideEffect.collectLatest { } + } + ProfileEditScreen( + selectedPersonaIndex = uiState.selectedPersonaIndex, + personaList = uiState.personaList, onBackButtonClick = navigateToBack, onCompleteButtonClick = navigateToMypage, modifier = Modifier.padding(paddingValues), @@ -49,6 +66,8 @@ fun ProfileRoute( @Composable fun ProfileEditScreen( + selectedPersonaIndex: Int, + personaList: List, onBackButtonClick: () -> Unit, onCompleteButtonClick: () -> Unit, modifier: Modifier = Modifier @@ -123,15 +142,15 @@ fun ProfileEditScreen( placeholder = "선택해주세요.", onClickItem = {}, onClickDropIcon = {}, - dropDownContents = persistentListOf(), - selectedIndex = -1, + dropDownContents = personaList, + selectedIndex = selectedPersonaIndex, modifier = Modifier.padding(vertical = 12.dp) ) } Spacer(modifier = Modifier.weight(12f)) SolplyBasicButton( text = "완료", - onClick = {}, + onClick = onCompleteButtonClick, modifier = Modifier.padding(vertical = 24.dp, horizontal = 16.dp) ) } @@ -142,6 +161,8 @@ fun ProfileEditScreen( private fun ProfileScreenPreview() { SolplyTheme { ProfileEditScreen( + selectedPersonaIndex = -1, + personaList = persistentListOf(), onBackButtonClick = {}, onCompleteButtonClick = {} ) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt index 6e7db721..01b63d7a 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt @@ -22,30 +22,19 @@ class ProfileEditViewModel @Inject constructor( } private fun init() { - getUserInfo() - getPersonaList() - reduce { - copy( - // TODO - ) - } - } - - private fun getUserInfo() { viewModelScope.launch { mypageRepository.getUserInfo() .onSuccess { userInfo -> reduce { copy(userInfo = userInfo) } } - } - } - - private fun getPersonaList() { - viewModelScope.launch { mypageRepository.getPersonaList() .onSuccess { personaList -> reduce { copy(personaList = personaList) } } + reduce { + val selectedIndex = personaList.indexOfFirst { it.personaType == userInfo.persona } + copy(selectedPersonaIndex = selectedIndex) + } } } } \ No newline at end of file diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/navigation/ProfileNavigation.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/navigation/ProfileNavigation.kt index a6e4148e..14a7f2da 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/navigation/ProfileNavigation.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/navigation/ProfileNavigation.kt @@ -8,6 +8,7 @@ import androidx.navigation.NavOptions import androidx.navigation.compose.composable import com.teamsolply.solply.mypage.MypageRoute import com.teamsolply.solply.mypage.MypageViewModel +import com.teamsolply.solply.mypage.profile.ProfileEditViewModel import com.teamsolply.solply.mypage.profile.ProfileRoute import com.teamsolply.solply.navigation.Route import kotlinx.serialization.Serializable @@ -24,7 +25,7 @@ fun NavGraphBuilder.profileNavGraph( navigateToMypage: () -> Unit, ) { composable { backStackEntry -> - val viewModel: MypageViewModel = hiltViewModel(backStackEntry) + val viewModel: ProfileEditViewModel = hiltViewModel(backStackEntry) ProfileRoute( paddingValues = paddingValues, navigateToBack = navigateToBack, From cc43cb805c1f341bdbeda94ef1a6ce4096c2e562 Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Mon, 6 Oct 2025 03:04:56 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feature#147=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20-=20=ED=94=84=EB=A1=9C=ED=95=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/teamsolply/solply/model/Persona.kt | 3 +- .../datasource/MypageRemoteDataSource.kt | 2 + .../dto/response/GetUserInfoResponseDto.kt | 18 +++- .../response/NicknameDuplicateResponseDto.kt | 10 ++ .../mypage/repository/MypageRepositoryImpl.kt | 18 ++-- .../solply/mypage/model/UserInfo.kt | 3 +- .../mypage/repository/MypageRepository.kt | 1 + .../solply/mypage/MypageContract.kt | 8 +- .../teamsolply/solply/mypage/MypageScreen.kt | 19 +++- .../solply/mypage/MypageViewModel.kt | 7 ++ .../component/SavedPlaceListContainer.kt | 3 + .../mypage/component/SolplyPersonaDropDown.kt | 2 +- .../mypage/profile/ProfileEditContract.kt | 20 +++- .../mypage/profile/ProfileEditScreen.kt | 90 +++++++++++++--- .../mypage/profile/ProfileEditViewModel.kt | 101 ++++++++++++++++-- .../mypage/src/main/res/values/strings.xml | 5 + .../datasource/MypageRemoteDataSourceImpl.kt | 14 ++- .../solply/mypage/service/MypageService.kt | 9 +- 18 files changed, 288 insertions(+), 45 deletions(-) create mode 100644 data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/NicknameDuplicateResponseDto.kt diff --git a/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt b/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt index 9dabc4b2..591c3dc4 100644 --- a/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt +++ b/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt @@ -7,5 +7,6 @@ enum class Persona(val description: String) { REST("조용한 공간에 오래 머물고 싶어요"), EXPLORER("이곳저곳 둘러보고 싶어요"), MOODING("취향이 담긴 곳을 찾고 싶어요"), - NATURAL("자연을 감상하며 쉬고 싶어요") + NATURAL("자연을 감상하며 쉬고 싶어요"), + ANYTHING("특별히 선호하는 공간은 없어요"), } \ No newline at end of file diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt index 7b1a0c5b..3d18a557 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSource.kt @@ -2,10 +2,12 @@ package com.teamsolply.solply.mypage.datasource import com.teamsolply.solply.mypage.dto.response.GetPersonaListResponseDto import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto +import com.teamsolply.solply.mypage.dto.response.NicknameDuplicateResponseDto import com.teamsolply.solply.mypage.dto.response.PlaceListResponseDto interface MypageRemoteDataSource { suspend fun getUserInfo(): GetUserInfoResponseDto suspend fun getPlaceList(townId: Long): PlaceListResponseDto suspend fun getPersonaList(): GetPersonaListResponseDto + suspend fun checkNicknameDuplicate(nickname: String): NicknameDuplicateResponseDto } diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt index 9e4a11fa..420df55b 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt @@ -1,6 +1,8 @@ package com.teamsolply.solply.mypage.dto.response import com.teamsolply.solply.model.Persona +import com.teamsolply.solply.mypage.model.SelectedTownInfo +import com.teamsolply.solply.mypage.model.UserInfo import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -13,7 +15,9 @@ data class GetUserInfoResponseDto( @SerialName("selectedTown") val selectedTown: SelectedTownDto, @SerialName("persona") - val persona: Persona + val persona: Persona, + @SerialName("profileImageUrl") + val profileImageUrl: String? ) @Serializable @@ -23,3 +27,15 @@ data class SelectedTownDto( @SerialName("townName") val townName: String ) + +fun GetUserInfoResponseDto.toDomain(): UserInfo = + UserInfo( + userId = userId, + nickname = nickname, + selectedTown = SelectedTownInfo( + townId = selectedTown.townId, + townName = selectedTown.townName + ), + persona = persona, + profileImageUrl = profileImageUrl ?: "" // 서버 null → 도메인 기본값 + ) \ No newline at end of file diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/NicknameDuplicateResponseDto.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/NicknameDuplicateResponseDto.kt new file mode 100644 index 00000000..10157839 --- /dev/null +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/NicknameDuplicateResponseDto.kt @@ -0,0 +1,10 @@ +package com.teamsolply.solply.mypage.dto.response + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class NicknameDuplicateResponseDto( + @SerialName("isDuplicated") + val isDuplicated: Boolean +) diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt index f36ec097..4a5a6945 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt @@ -1,9 +1,10 @@ package com.teamsolply.solply.mypage.repository +import android.util.Log import com.teamsolply.solply.mypage.datasource.MypageRemoteDataSource +import com.teamsolply.solply.mypage.dto.response.toDomain import com.teamsolply.solply.mypage.model.PersonaEntity import com.teamsolply.solply.mypage.model.PlaceInfoEntity -import com.teamsolply.solply.mypage.model.SelectedTownInfo import com.teamsolply.solply.mypage.model.UserInfo import javax.inject.Inject @@ -13,15 +14,7 @@ class MypageRepositoryImpl @Inject constructor( override suspend fun getUserInfo(): Result = runCatching { mypageRemoteDataSource.getUserInfo() }.mapCatching { userDto -> - UserInfo( - userId = userDto.userId, - nickname = userDto.nickname, - selectedTown = SelectedTownInfo( - townId = userDto.selectedTown.townId, - townName = userDto.selectedTown.townName - ), - persona = userDto.persona - ) + userDto.toDomain() } override suspend fun getPlaceList(townId: Long): Result> = runCatching { @@ -41,6 +34,7 @@ class MypageRepositoryImpl @Inject constructor( override suspend fun getPersonaList(): Result> = runCatching { mypageRemoteDataSource.getPersonaList().personaDtoList }.mapCatching { personaList -> + Log.d("persona: ","repo impl start") personaList.map { persona -> PersonaEntity( personaType = persona.personaType, @@ -48,4 +42,8 @@ class MypageRepositoryImpl @Inject constructor( ) } } + + override suspend fun checkNicknameDuplicate(nickname: String): Result = runCatching { + mypageRemoteDataSource.checkNicknameDuplicate(nickname = nickname).isDuplicated + } } diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt index 21399b65..60379bfa 100644 --- a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/model/UserInfo.kt @@ -6,7 +6,8 @@ data class UserInfo( val userId: Long, val nickname: String, val selectedTown: SelectedTownInfo, - val persona: Persona + val persona: Persona, + val profileImageUrl: String = "" ) data class SelectedTownInfo( diff --git a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt index 0da6660b..86b7648a 100644 --- a/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt +++ b/domain/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepository.kt @@ -8,4 +8,5 @@ interface MypageRepository { suspend fun getUserInfo(): Result suspend fun getPlaceList(townId: Long): Result> suspend fun getPersonaList(): Result> + suspend fun checkNicknameDuplicate(nickname: String): Result } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt index 0b403b0f..bdeca22e 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageContract.kt @@ -13,9 +13,9 @@ data class MypageState( userId = 1, nickname = "숭이숭이숭이", selectedTown = SelectedTownInfo(0, "망원동"), - persona = Persona.REST + persona = Persona.REST, + profileImageUrl = "" ), - val nickname: String = "", val placeList: List = emptyList(), val dialogState: Boolean = false ) : UiState @@ -24,13 +24,15 @@ sealed interface MypageIntent : UiIntent { data object Init : MypageIntent data object LogOutButtonClick : MypageIntent + data object WithdrawButtonClick : MypageIntent data object DialogConfirmClick : MypageIntent data object DialogDismissClick : MypageIntent + + data object ProfileEditClick : MypageIntent } sealed interface MypageSideEffect : SideEffect { data object NavigateToBack : MypageSideEffect data object NavigateToProfile : MypageSideEffect - data object NavigateToMypage : MypageSideEffect } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt index ad89dfd8..76939cb6 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt @@ -37,6 +37,8 @@ import com.teamsolply.solply.mypage.component.MypageSettingItem import com.teamsolply.solply.mypage.component.SavedPlaceListContainer import com.teamsolply.solply.mypage.model.PlaceInfoEntity import com.teamsolply.solply.ui.extension.customClickable +import com.teamsolply.solply.ui.lifecycle.LaunchedEffectWithLifecycle +import kotlinx.coroutines.flow.collectLatest @Composable fun MypageRoute( @@ -51,11 +53,21 @@ fun MypageRoute( viewModel.sendIntent(MypageIntent.Init) } + LaunchedEffectWithLifecycle { + viewModel.sideEffect.collectLatest { sideEffect -> + when (sideEffect) { + MypageSideEffect.NavigateToBack -> navigateToBack() + MypageSideEffect.NavigateToProfile -> navigateToProfile() + } + } + } + MypageScreen( + nickname = uiState.userInfo.nickname, savedPlaceList = uiState.placeList, dialogState = uiState.dialogState, onBackButtonClick = navigateToBack, - onProfileEditClick = navigateToProfile, + onProfileEditClick = { viewModel.sendIntent(MypageIntent.ProfileEditClick) }, onLogOutClick = { viewModel.sendIntent(MypageIntent.LogOutButtonClick) }, onDialogConfirmClick = { viewModel.sendIntent(MypageIntent.DialogConfirmClick) }, onDialogDismissClick = { viewModel.sendIntent(MypageIntent.DialogDismissClick) }, @@ -65,6 +77,7 @@ fun MypageRoute( @Composable fun MypageScreen( + nickname: String, savedPlaceList: List, dialogState: Boolean, onBackButtonClick: () -> Unit, @@ -116,7 +129,7 @@ fun MypageScreen( ) Spacer(modifier = Modifier.height(12.dp)) Text( - text = "닉네임", + text = nickname, color = SolplyTheme.colors.black, style = SolplyTheme.typography.display20Sb.copy( lineHeightStyle = LineHeightStyle( @@ -175,6 +188,7 @@ fun MypageScreen( SavedPlaceListContainer( savedPlaceList = savedPlaceList, modifier = Modifier + .fillMaxWidth() .padding(start = 20.dp, bottom = 16.dp) ) } @@ -235,6 +249,7 @@ fun MypageScreen( private fun MypageScreenPreview() { SolplyTheme { MypageScreen( + nickname = "닉네임", savedPlaceList = emptyList(), dialogState = false, onBackButtonClick = {}, diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt index fc8c4716..f6e2e2df 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt @@ -25,6 +25,9 @@ class MypageViewModel @Inject constructor( } } + MypageIntent.WithdrawButtonClick -> { + } + MypageIntent.DialogConfirmClick -> { // TODO 로그아웃 api reduce { @@ -37,6 +40,10 @@ class MypageViewModel @Inject constructor( copy(dialogState = false) } } + + MypageIntent.ProfileEditClick -> { + postSideEffect(MypageSideEffect.NavigateToProfile) + } } } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt index 5cdb6f52..24205d37 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt @@ -1,11 +1,13 @@ package com.teamsolply.solply.mypage.component import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp import com.teamsolply.solply.designsystem.component.card.SolplyPlaceCard import com.teamsolply.solply.designsystem.theme.SolplyTheme import com.teamsolply.solply.mypage.model.PlaceInfoEntity @@ -27,6 +29,7 @@ fun SavedPlaceListContainer( selected = it.isSelected, saved = it.isSaved, touchable = false, + modifier = Modifier.size(height = 165.dp, width = 136.dp) ) } } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt index 82133dea..ca4cefbd 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt @@ -36,7 +36,7 @@ fun SolplyPersonaDropDown( isSelected: Boolean = false ) { SolplyBasicDropDown( - defaultLabel = if (isSelected) dropDownContents.get(selectedIndex).description else placeholder, + defaultLabel = if (isSelected) dropDownContents[selectedIndex].description else placeholder, onClickDropIcon = onClickDropIcon, isDropped = isDropped, modifier = modifier diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt index aea24a7c..a199eff3 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt @@ -13,20 +13,36 @@ data class ProfileEditState( userId = 1, nickname = "숭이숭이숭이", selectedTown = SelectedTownInfo(0, "망원동"), - persona = Persona.REST + persona = Persona.REST, + profileImageUrl = "" ), + val inputNickname: String = "", + val isNicknameDuplicate: Boolean = false, + val isEditSuccess: Boolean = false, val personaList: List = emptyList(), val selectedPersonaIndex: Int = -1, + val isDropped: Boolean = false, + val completeButtonEnabled: Boolean = false, + val dialogState: Boolean = false, ) : UiState sealed interface ProfileEditIntent : UiIntent { data object Init : ProfileEditIntent + data class ChangeInputNickname(val nickname: String) : ProfileEditIntent + data object ValidateNickname : ProfileEditIntent + data class ChangeEditingSuccess(val state: Boolean) : ProfileEditIntent + + data object DropDownIconClick : ProfileEditIntent + data class DropDownItemClick(val index: Int) : ProfileEditIntent data object CompleteButtonClick : ProfileEditIntent + data object BackButtonClick : ProfileEditIntent + + data object DialogConfirmClick : ProfileEditIntent + data object DialogDismissClick : ProfileEditIntent } sealed interface ProfileEditSideEffect : SideEffect { data object NavigateToBack : ProfileEditSideEffect - data object NavigateToProfile : ProfileEditSideEffect data object NavigateToMypage : ProfileEditSideEffect } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt index 301db96e..b45f664b 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt @@ -28,6 +28,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.teamsolply.solply.designsystem.component.button.SolplyBasicButton +import com.teamsolply.solply.designsystem.component.dialog.SolplyConfirmDialog import com.teamsolply.solply.designsystem.component.textfield.SolplyNicknameTextField import com.teamsolply.solply.designsystem.component.topbar.SolplyTopBar import com.teamsolply.solply.designsystem.theme.SolplyTheme @@ -52,26 +53,67 @@ fun ProfileRoute( } LaunchedEffectWithLifecycle { - viewModel.sideEffect.collectLatest { } + viewModel.sideEffect.collectLatest { sideEffect -> + when (sideEffect) { + ProfileEditSideEffect.NavigateToMypage -> { + navigateToMypage() + } + + ProfileEditSideEffect.NavigateToBack -> { + navigateToBack() + } + } + } } ProfileEditScreen( + nickname = uiState.inputNickname, + isNicknameDuplicated = uiState.isNicknameDuplicate, selectedPersonaIndex = uiState.selectedPersonaIndex, personaList = uiState.personaList, - onBackButtonClick = navigateToBack, - onCompleteButtonClick = navigateToMypage, + dialogState = uiState.dialogState, + isDropped = uiState.isDropped, + isEditSuccess = uiState.isEditSuccess, + onNicknameChanged = { viewModel.sendIntent(ProfileEditIntent.ChangeInputNickname(it)) }, + onNicknameValidateChanged = { viewModel.sendIntent(ProfileEditIntent.ChangeEditingSuccess(it)) }, + onDropIconClick = { viewModel.sendIntent(ProfileEditIntent.DropDownIconClick) }, + onDropDownItemClick = { viewModel.sendIntent(ProfileEditIntent.DropDownItemClick(it)) }, + onBackButtonClick = { viewModel.sendIntent(ProfileEditIntent.BackButtonClick) }, + onCompleteButtonClick = { viewModel.sendIntent(ProfileEditIntent.CompleteButtonClick) }, + onDialogConfirmClick = { viewModel.sendIntent(ProfileEditIntent.DialogConfirmClick) }, + onDialogDismissClick = { viewModel.sendIntent(ProfileEditIntent.DialogDismissClick) }, modifier = Modifier.padding(paddingValues), ) } @Composable fun ProfileEditScreen( + nickname: String, + isNicknameDuplicated: Boolean, selectedPersonaIndex: Int, personaList: List, + dialogState: Boolean, + isDropped: Boolean, + isEditSuccess: Boolean, + onNicknameChanged: (String) -> Unit, + onNicknameValidateChanged: (Boolean) -> Unit, + onDropIconClick: () -> Unit, + onDropDownItemClick: (Int) -> Unit, onBackButtonClick: () -> Unit, onCompleteButtonClick: () -> Unit, + onDialogConfirmClick: () -> Unit, + onDialogDismissClick: () -> Unit, modifier: Modifier = Modifier ) { + if (dialogState) { + SolplyConfirmDialog( + text = stringResource(R.string.profile_dialog), + confirmButtonText = stringResource(R.string.profile_dialog_confirm), + dismissButtonText = stringResource(R.string.profile_dialog_cancel), + onClickConfirm = onDialogConfirmClick, + onClickDismiss = onDialogDismissClick + ) + } Column( modifier = modifier .fillMaxSize() @@ -113,11 +155,13 @@ fun ProfileEditScreen( ) } SolplyNicknameTextField( - value = "", - isNicknameDuplicate = false, - onValueChange = {}, - checkNicknameValidate = { true }, - changeNicknameValidate = {}, + value = nickname, + isNicknameDuplicate = isNicknameDuplicated, + onValueChange = onNicknameChanged, + changeNicknameValidate = onNicknameValidateChanged, + checkNicknameValidate = { input -> + input.all { it.isLetterOrDigit() } + }, modifier = Modifier.padding(top = 12.dp) ) } @@ -139,18 +183,27 @@ fun ProfileEditScreen( ) } SolplyPersonaDropDown( - placeholder = "선택해주세요.", - onClickItem = {}, - onClickDropIcon = {}, + placeholder = stringResource(R.string.profile_persona_placeholder), + isDropped = isDropped, + onClickItem = onDropDownItemClick, + onClickDropIcon = onDropIconClick, dropDownContents = personaList, selectedIndex = selectedPersonaIndex, + isSelected = selectedPersonaIndex != -1, modifier = Modifier.padding(vertical = 12.dp) ) } Spacer(modifier = Modifier.weight(12f)) SolplyBasicButton( text = "완료", - onClick = onCompleteButtonClick, + selected = isEditSuccess, + onClick = { + if (isEditSuccess) { + onCompleteButtonClick() + } + }, + enabledBackgroundColor = SolplyTheme.colors.gray900, + disabledBackgroundColor = SolplyTheme.colors.gray300, modifier = Modifier.padding(vertical = 24.dp, horizontal = 16.dp) ) } @@ -161,10 +214,21 @@ fun ProfileEditScreen( private fun ProfileScreenPreview() { SolplyTheme { ProfileEditScreen( + nickname = "", + isNicknameDuplicated = false, selectedPersonaIndex = -1, personaList = persistentListOf(), + dialogState = false, + isDropped = false, + isEditSuccess = false, + onNicknameChanged = {}, + onNicknameValidateChanged = {}, + onDropIconClick = {}, + onDropDownItemClick = {}, onBackButtonClick = {}, - onCompleteButtonClick = {} + onCompleteButtonClick = {}, + onDialogConfirmClick = {}, + onDialogDismissClick = {} ) } } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt index 01b63d7a..4de075c0 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt @@ -1,9 +1,13 @@ package com.teamsolply.solply.mypage.profile +import android.util.Log import androidx.lifecycle.viewModelScope import com.teamsolply.solply.mypage.repository.MypageRepository import com.teamsolply.solply.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import javax.inject.Inject @@ -12,12 +16,76 @@ class ProfileEditViewModel @Inject constructor( private val mypageRepository: MypageRepository ) : BaseViewModel(ProfileEditState()) { + private val nicknameFlow = MutableStateFlow("") override fun handleIntent(intent: ProfileEditIntent) { when (intent) { ProfileEditIntent.Init -> init() - ProfileEditIntent.CompleteButtonClick -> TODO() + is ProfileEditIntent.ChangeInputNickname -> { + reduce { + copy(inputNickname = intent.nickname) + } + } + + ProfileEditIntent.ValidateNickname -> { + + } + + ProfileEditIntent.BackButtonClick -> { + reduce { + copy(dialogState = true) + } + } + + is ProfileEditIntent.ChangeEditingSuccess -> { + reduce { + copy( + isEditSuccess = intent.state + ) + } + } + + ProfileEditIntent.DropDownIconClick -> { + reduce { + copy( + isDropped = !isDropped + ) + } + } + + is ProfileEditIntent.DropDownItemClick -> { + reduce { + copy( + selectedPersonaIndex = intent.index, + isDropped = !isDropped + ) + } + } + + ProfileEditIntent.CompleteButtonClick -> { + viewModelScope.launch { + // TODO 프로필 수정 patch api + postSideEffect( + ProfileEditSideEffect.NavigateToMypage + ) + } + } + + ProfileEditIntent.DialogConfirmClick -> { + viewModelScope.launch { + // TODO 로그아웃 api + postSideEffect( + ProfileEditSideEffect.NavigateToBack + ) + } + } + + ProfileEditIntent.DialogDismissClick -> { + reduce { + copy(dialogState = false) + } + } } } @@ -25,16 +93,37 @@ class ProfileEditViewModel @Inject constructor( viewModelScope.launch { mypageRepository.getUserInfo() .onSuccess { userInfo -> - reduce { copy(userInfo = userInfo) } + reduce { + copy( + userInfo = userInfo, + inputNickname = userInfo.nickname + ) + } + } + .onFailure { + Log.i("getUser fail", "") } mypageRepository.getPersonaList() .onSuccess { personaList -> + Log.d("persona: ", personaList.toString()) reduce { copy(personaList = personaList) } + reduce { + val selectedIndex = + personaList.indexOfFirst { it.personaType == userInfo.persona } + copy(selectedPersonaIndex = selectedIndex) + } + } + nicknameFlow + .debounce(500) + .filter { it.isNotBlank() } + .collect { nickname -> + mypageRepository.checkNicknameDuplicate(nickname) + .onSuccess { isDuplicate -> + reduce { copy(isNicknameDuplicate = isDuplicate) } + }.onFailure { + reduce { copy(isNicknameDuplicate = false) } + } } - reduce { - val selectedIndex = personaList.indexOfFirst { it.personaType == userInfo.persona } - copy(selectedPersonaIndex = selectedIndex) - } } } } \ No newline at end of file diff --git a/feature/mypage/src/main/res/values/strings.xml b/feature/mypage/src/main/res/values/strings.xml index 0e2db4fd..7725f99c 100644 --- a/feature/mypage/src/main/res/values/strings.xml +++ b/feature/mypage/src/main/res/values/strings.xml @@ -8,6 +8,11 @@ 기본 프로필 닉네임 나의 솔플 스타일 + 선택해주세요. + + 변경 사항을 저장하지 않고\n나가시겠어요? + 나가기 + 취소 로그아웃하시겠습니까? 취소 diff --git a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt index 7bc42668..7cec758e 100644 --- a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt +++ b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/datasource/MypageRemoteDataSourceImpl.kt @@ -1,7 +1,9 @@ package com.teamsolply.solply.mypage.datasource +import android.util.Log import com.teamsolply.solply.mypage.dto.response.GetPersonaListResponseDto import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto +import com.teamsolply.solply.mypage.dto.response.NicknameDuplicateResponseDto import com.teamsolply.solply.mypage.service.MypageService import javax.inject.Inject @@ -9,13 +11,17 @@ class MypageRemoteDataSourceImpl @Inject constructor( private val mypageService: MypageService ) : MypageRemoteDataSource { override suspend fun getUserInfo(): GetUserInfoResponseDto { - return mypageService.getUserInfo().data + val user = mypageService.getUserInfo().data + Log.d("getUser ", user.nickname) + return user } override suspend fun getPlaceList(townId: Long) = mypageService.getPlaceList(townId = townId).data - override suspend fun getPersonaList(): GetPersonaListResponseDto { - return mypageService.getPersonaList().data - } + override suspend fun getPersonaList(): GetPersonaListResponseDto = + mypageService.getPersonaList().data + + override suspend fun checkNicknameDuplicate(nickname: String): NicknameDuplicateResponseDto = + mypageService.checkNicknameDuplicate(nickname = nickname).data } diff --git a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt index 0127a3d6..ff44e004 100644 --- a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt +++ b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt @@ -2,6 +2,7 @@ package com.teamsolply.solply.mypage.service import com.teamsolply.solply.mypage.dto.response.GetPersonaListResponseDto import com.teamsolply.solply.mypage.dto.response.GetUserInfoResponseDto +import com.teamsolply.solply.mypage.dto.response.NicknameDuplicateResponseDto import com.teamsolply.solply.mypage.dto.response.PlaceListResponseDto import com.teamsolply.solply.network.model.BaseResponse import retrofit2.http.GET @@ -17,6 +18,12 @@ interface MypageService { @Query("isBookmarkSearch") isBookmarkedSearch: Boolean = true ): BaseResponse - @GET("api/onboarding/questions/persona") + @GET("api/users/persona") suspend fun getPersonaList(): BaseResponse + + @GET("api/users/check-nickname") + suspend fun checkNicknameDuplicate( + @Query("nickname") nickname: String + ): BaseResponse + } From 160b35da4e8bb9cf64d0b590704b876bb4e2abbc Mon Sep 17 00:00:00 2001 From: ImHyungsuk Date: Mon, 6 Oct 2025 03:05:49 +0900 Subject: [PATCH 9/9] chore#147 apply ktlint --- .../designsystem/component/dropdown/SolplyBasicDropDown.kt | 4 ++-- .../com/teamsolply/solply/designsystem/theme/Typography.kt | 2 +- .../src/main/java/com/teamsolply/solply/model/Persona.kt | 4 ++-- .../solply/mypage/dto/response/GetUserInfoResponseDto.kt | 4 ++-- .../solply/mypage/repository/MypageRepositoryImpl.kt | 2 +- .../src/main/java/com/teamsolply/solply/main/MainScreen.kt | 1 - .../main/java/com/teamsolply/solply/mypage/MypageScreen.kt | 6 +++--- .../java/com/teamsolply/solply/mypage/MypageViewModel.kt | 2 +- .../solply/mypage/component/EmptyPlaceContainer.kt | 1 - .../solply/mypage/component/SavedPlaceListContainer.kt | 6 +++--- .../solply/mypage/component/SolplyPersonaDropDown.kt | 2 +- .../teamsolply/solply/mypage/navigation/MypageNavigation.kt | 3 +-- .../teamsolply/solply/mypage/profile/ProfileEditContract.kt | 2 +- .../teamsolply/solply/mypage/profile/ProfileEditScreen.kt | 2 +- .../solply/mypage/profile/ProfileEditViewModel.kt | 3 +-- .../solply/mypage/profile/navigation/ProfileNavigation.kt | 6 ++---- .../com/teamsolply/solply/mypage/service/MypageService.kt | 1 - 17 files changed, 22 insertions(+), 29 deletions(-) diff --git a/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/dropdown/SolplyBasicDropDown.kt b/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/dropdown/SolplyBasicDropDown.kt index 8f732d6d..fbf5cdc8 100644 --- a/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/dropdown/SolplyBasicDropDown.kt +++ b/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/component/dropdown/SolplyBasicDropDown.kt @@ -34,7 +34,7 @@ fun SolplyBasicDropDown( onClickDropIcon: () -> Unit, modifier: Modifier = Modifier, isDropped: Boolean = false, - content: @Composable ColumnScope.() -> Unit, + content: @Composable ColumnScope.() -> Unit ) { Column( modifier = modifier @@ -100,4 +100,4 @@ fun SolplyBasicDropDown( } } } -} \ No newline at end of file +} diff --git a/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/theme/Typography.kt b/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/theme/Typography.kt index e26d829c..c26c0c12 100644 --- a/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/theme/Typography.kt +++ b/core/designsystem/src/main/java/com/teamsolply/solply/designsystem/theme/Typography.kt @@ -208,7 +208,7 @@ fun SolplyTypography(): SolplyTypography { fontFamily = PretendardSemiBold, fontWeight = FontWeight.SemiBold, fontSize = 20.sp, - lineHeight = (20 * 1.5).sp, + lineHeight = (20 * 1.5).sp ), display16Sb = TextStyle( fontFamily = PretendardSemiBold, diff --git a/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt b/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt index 591c3dc4..d2a0de13 100644 --- a/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt +++ b/core/model/src/main/java/com/teamsolply/solply/model/Persona.kt @@ -8,5 +8,5 @@ enum class Persona(val description: String) { EXPLORER("이곳저곳 둘러보고 싶어요"), MOODING("취향이 담긴 곳을 찾고 싶어요"), NATURAL("자연을 감상하며 쉬고 싶어요"), - ANYTHING("특별히 선호하는 공간은 없어요"), -} \ No newline at end of file + ANYTHING("특별히 선호하는 공간은 없어요") +} diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt index 420df55b..a0fb06fa 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/dto/response/GetUserInfoResponseDto.kt @@ -37,5 +37,5 @@ fun GetUserInfoResponseDto.toDomain(): UserInfo = townName = selectedTown.townName ), persona = persona, - profileImageUrl = profileImageUrl ?: "" // 서버 null → 도메인 기본값 - ) \ No newline at end of file + profileImageUrl = profileImageUrl ?: "" // 서버 null → 도메인 기본값 + ) diff --git a/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt b/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt index 4a5a6945..b828e137 100644 --- a/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt +++ b/data/mypage/src/main/java/com/teamsolply/solply/mypage/repository/MypageRepositoryImpl.kt @@ -34,7 +34,7 @@ class MypageRepositoryImpl @Inject constructor( override suspend fun getPersonaList(): Result> = runCatching { mypageRemoteDataSource.getPersonaList().personaDtoList }.mapCatching { personaList -> - Log.d("persona: ","repo impl start") + Log.d("persona: ", "repo impl start") personaList.map { persona -> PersonaEntity( personaType = persona.personaType, diff --git a/feature/main/src/main/java/com/teamsolply/solply/main/MainScreen.kt b/feature/main/src/main/java/com/teamsolply/solply/main/MainScreen.kt index 394a55fb..3c1522d7 100644 --- a/feature/main/src/main/java/com/teamsolply/solply/main/MainScreen.kt +++ b/feature/main/src/main/java/com/teamsolply/solply/main/MainScreen.kt @@ -41,7 +41,6 @@ import com.teamsolply.solply.maps.navigation.mapsNavGraph import com.teamsolply.solply.model.SnackBarType import com.teamsolply.solply.mypage.navigation.Mypage import com.teamsolply.solply.mypage.navigation.mypageNavGraph -import com.teamsolply.solply.mypage.profile.navigation.Profile import com.teamsolply.solply.mypage.profile.navigation.profileNavGraph import com.teamsolply.solply.oauth.navigation.oauthNavGraph import com.teamsolply.solply.onboarding.navigation.onBoardingNavGraph diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt index 76939cb6..c6cbd419 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageScreen.kt @@ -71,7 +71,7 @@ fun MypageRoute( onLogOutClick = { viewModel.sendIntent(MypageIntent.LogOutButtonClick) }, onDialogConfirmClick = { viewModel.sendIntent(MypageIntent.DialogConfirmClick) }, onDialogDismissClick = { viewModel.sendIntent(MypageIntent.DialogDismissClick) }, - modifier = Modifier.padding(paddingValues), + modifier = Modifier.padding(paddingValues) ) } @@ -136,7 +136,7 @@ fun MypageScreen( alignment = LineHeightStyle.Alignment.Center, trim = LineHeightStyle.Trim.None ) - ), + ) ) Spacer(modifier = Modifier.height(12.dp)) Row( @@ -256,7 +256,7 @@ private fun MypageScreenPreview() { onProfileEditClick = {}, onLogOutClick = {}, onDialogConfirmClick = {}, - onDialogDismissClick = {}, + onDialogDismissClick = {} ) } } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt index f6e2e2df..024b3409 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/MypageViewModel.kt @@ -54,7 +54,7 @@ class MypageViewModel @Inject constructor( reduce { copy(userInfo = userInfo) } getPlaceList( - townId = userInfo.selectedTown.townId, + townId = userInfo.selectedTown.townId ) } } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/EmptyPlaceContainer.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/EmptyPlaceContainer.kt index 00e66430..a1e24f02 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/EmptyPlaceContainer.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/EmptyPlaceContainer.kt @@ -3,7 +3,6 @@ package com.teamsolply.solply.mypage.component import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt index 24205d37..1ecfdcbd 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SavedPlaceListContainer.kt @@ -15,11 +15,11 @@ import com.teamsolply.solply.mypage.model.PlaceInfoEntity @Composable fun SavedPlaceListContainer( savedPlaceList: List, - modifier: Modifier = Modifier, + modifier: Modifier = Modifier ) { LazyRow( modifier = modifier - .fillMaxWidth(), + .fillMaxWidth() ) { items(savedPlaceList) { SolplyPlaceCard( @@ -43,4 +43,4 @@ private fun SavedPlaceListContainerPreview() { savedPlaceList = emptyList() ) } -} \ No newline at end of file +} diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt index ca4cefbd..1c4f894f 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/component/SolplyPersonaDropDown.kt @@ -108,4 +108,4 @@ private fun SolplyPersonaDropDownPreview() { ) } } -} \ No newline at end of file +} diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/navigation/MypageNavigation.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/navigation/MypageNavigation.kt index d4dc3f3c..b272060d 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/navigation/MypageNavigation.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/navigation/MypageNavigation.kt @@ -6,7 +6,6 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable -import androidx.navigation.toRoute import com.teamsolply.solply.mypage.MypageRoute import com.teamsolply.solply.mypage.MypageViewModel import com.teamsolply.solply.navigation.Route @@ -21,7 +20,7 @@ fun NavController.navigateMypage( fun NavGraphBuilder.mypageNavGraph( navigateToBack: () -> Unit, navigateToProfile: () -> Unit, - paddingValues: PaddingValues, + paddingValues: PaddingValues ) { composable { backStackEntry -> val viewModel: MypageViewModel = hiltViewModel(backStackEntry) diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt index a199eff3..6f45d38e 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditContract.kt @@ -23,7 +23,7 @@ data class ProfileEditState( val selectedPersonaIndex: Int = -1, val isDropped: Boolean = false, val completeButtonEnabled: Boolean = false, - val dialogState: Boolean = false, + val dialogState: Boolean = false ) : UiState sealed interface ProfileEditIntent : UiIntent { diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt index b45f664b..8208519a 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditScreen.kt @@ -82,7 +82,7 @@ fun ProfileRoute( onCompleteButtonClick = { viewModel.sendIntent(ProfileEditIntent.CompleteButtonClick) }, onDialogConfirmClick = { viewModel.sendIntent(ProfileEditIntent.DialogConfirmClick) }, onDialogDismissClick = { viewModel.sendIntent(ProfileEditIntent.DialogDismissClick) }, - modifier = Modifier.padding(paddingValues), + modifier = Modifier.padding(paddingValues) ) } diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt index 4de075c0..1b47725b 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/ProfileEditViewModel.kt @@ -29,7 +29,6 @@ class ProfileEditViewModel @Inject constructor( } ProfileEditIntent.ValidateNickname -> { - } ProfileEditIntent.BackButtonClick -> { @@ -126,4 +125,4 @@ class ProfileEditViewModel @Inject constructor( } } } -} \ No newline at end of file +} diff --git a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/navigation/ProfileNavigation.kt b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/navigation/ProfileNavigation.kt index 14a7f2da..bf0ac064 100644 --- a/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/navigation/ProfileNavigation.kt +++ b/feature/mypage/src/main/java/com/teamsolply/solply/mypage/profile/navigation/ProfileNavigation.kt @@ -6,8 +6,6 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptions import androidx.navigation.compose.composable -import com.teamsolply.solply.mypage.MypageRoute -import com.teamsolply.solply.mypage.MypageViewModel import com.teamsolply.solply.mypage.profile.ProfileEditViewModel import com.teamsolply.solply.mypage.profile.ProfileRoute import com.teamsolply.solply.navigation.Route @@ -22,7 +20,7 @@ fun NavController.navigateProfile( fun NavGraphBuilder.profileNavGraph( paddingValues: PaddingValues, navigateToBack: () -> Unit, - navigateToMypage: () -> Unit, + navigateToMypage: () -> Unit ) { composable { backStackEntry -> val viewModel: ProfileEditViewModel = hiltViewModel(backStackEntry) @@ -36,4 +34,4 @@ fun NavGraphBuilder.profileNavGraph( } @Serializable -data object Profile : Route \ No newline at end of file +data object Profile : Route diff --git a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt index ff44e004..ce98be63 100644 --- a/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt +++ b/remote/mypage/src/main/java/com/teamsolply/solply/mypage/service/MypageService.kt @@ -25,5 +25,4 @@ interface MypageService { suspend fun checkNicknameDuplicate( @Query("nickname") nickname: String ): BaseResponse - }