-
Notifications
You must be signed in to change notification settings - Fork 1
Feature/#132 프로필 수정 ui 구현 #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7a14521
a30fe1a
ae71e81
c039321
fd6ea0d
5548e2a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| package com.teamsolply.solply.designsystem.component.dropdown | ||
|
|
||
| import androidx.compose.animation.AnimatedVisibility | ||
| import androidx.compose.animation.slideInVertically | ||
| import androidx.compose.animation.slideOutVertically | ||
| import androidx.compose.foundation.background | ||
| import androidx.compose.foundation.border | ||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.ColumnScope | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.height | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.layout.width | ||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||
| import androidx.compose.material3.HorizontalDivider | ||
| import androidx.compose.material3.Icon | ||
| import androidx.compose.material3.Text | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.ui.Alignment | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.draw.clip | ||
| import androidx.compose.ui.draw.scale | ||
| import androidx.compose.ui.res.painterResource | ||
| import androidx.compose.ui.unit.dp | ||
| import com.teamsolply.solply.designsystem.R | ||
| import com.teamsolply.solply.designsystem.theme.SolplyTheme | ||
| import com.teamsolply.solply.ui.extension.customClickable | ||
|
|
||
| @Composable | ||
| fun SolplyBasicDropDown( | ||
| defaultLabel: String, | ||
| onClickDropIcon: () -> Unit, | ||
| modifier: Modifier = Modifier, | ||
| isDropped: Boolean = false, | ||
| content: @Composable ColumnScope.() -> Unit, | ||
| ) { | ||
| Column( | ||
| modifier = modifier | ||
| .clip( | ||
| RoundedCornerShape(20.dp) | ||
| ) | ||
| .border( | ||
| width = 1.dp, | ||
| color = SolplyTheme.colors.gray300, | ||
| shape = RoundedCornerShape(20.dp) | ||
| ) | ||
| ) { | ||
| Row( | ||
| modifier = Modifier | ||
| .fillMaxWidth() | ||
| .then( | ||
| if (isDropped) { | ||
| Modifier.background( | ||
| color = SolplyTheme.colors.gray100 | ||
| ) | ||
| } else { | ||
| Modifier.background( | ||
| color = SolplyTheme.colors.white | ||
| ) | ||
| } | ||
| ), | ||
| verticalAlignment = Alignment.CenterVertically, | ||
| horizontalArrangement = Arrangement.SpaceBetween | ||
| ) { | ||
| Text( | ||
| text = defaultLabel, | ||
| color = SolplyTheme.colors.gray900, | ||
| style = SolplyTheme.typography.body16M, | ||
| modifier = Modifier.padding(start = 20.dp) | ||
| ) | ||
| Icon( | ||
| painter = painterResource(R.drawable.ic_arrow_down), | ||
| contentDescription = "", | ||
| modifier = Modifier | ||
| .padding(end = 20.dp, top = 14.dp, bottom = 14.dp) | ||
| .height(24.dp) | ||
| .width(24.dp) | ||
| .scale( | ||
| scaleX = 1f, | ||
| scaleY = if (isDropped) -1f else 1f | ||
| ) | ||
| .customClickable( | ||
| rippleEnabled = false, | ||
| onClick = onClickDropIcon | ||
| ) | ||
| ) | ||
| } | ||
| HorizontalDivider(color = SolplyTheme.colors.gray300) | ||
| AnimatedVisibility( | ||
| visible = isDropped, | ||
| enter = slideInVertically(), | ||
| exit = slideOutVertically() | ||
| ) { | ||
| Column( | ||
| modifier = Modifier.fillMaxWidth() | ||
| ) { | ||
| content() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:width="24dp" | ||
| android:height="24dp" | ||
| android:viewportWidth="24" | ||
| android:viewportHeight="24"> | ||
| <path | ||
| android:strokeWidth="1" | ||
| android:pathData="M8.286,10.552L12.143,14.49L16,10.552" | ||
| android:strokeLineJoin="round" | ||
| android:fillColor="#00000000" | ||
| android:strokeColor="#242424" | ||
| android:strokeLineCap="round"/> | ||
| </vector> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,22 @@ | ||
| package com.teamsolply.solply.mypage | ||
|
|
||
| import com.teamsolply.solply.mypage.model.DropDownPersonaItem | ||
| 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 town: String = "연희동", | ||
| val nickname: String = "", | ||
| ) : UiState | ||
|
|
||
| sealed interface MypageIntent : UiIntent | ||
| sealed interface MypageIntent : UiIntent { | ||
|
|
||
| sealed interface MypageSideEffect : SideEffect | ||
| } | ||
|
Comment on lines
+14
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major detekt 경고의 원인이 된 빈 인터페이스 본문 제거 필요
-sealed interface MypageIntent : UiIntent {
-
-}
+sealed interface MypageIntent : UiIntentAs per static analysis hints 🧰 Tools🪛 detekt (1.23.8)[warning] 14-16: The class or object MypageIntent is empty. (detekt.empty-blocks.EmptyClassBlock) 🤖 Prompt for AI Agents |
||
|
|
||
| sealed interface MypageSideEffect : SideEffect { | ||
| data object NavigateToBack : MypageSideEffect | ||
| data object NavigateToProfile : MypageSideEffect | ||
| data object NavigateToMypage : MypageSideEffect | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -34,17 +34,20 @@ import com.teamsolply.solply.ui.extension.customClickable | |||||||||||||||||||||||||||||||||||||||||
| fun MypageRoute( | ||||||||||||||||||||||||||||||||||||||||||
| paddingValues: PaddingValues, | ||||||||||||||||||||||||||||||||||||||||||
| navigateToBack: () -> Unit, | ||||||||||||||||||||||||||||||||||||||||||
| navigateToProfile: () -> Unit, | ||||||||||||||||||||||||||||||||||||||||||
| viewModel: MypageViewModel = hiltViewModel() | ||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||
| MypageScreen( | ||||||||||||||||||||||||||||||||||||||||||
| onBackButtonClick = navigateToBack, | ||||||||||||||||||||||||||||||||||||||||||
| onProfileEditClick = navigateToProfile, | ||||||||||||||||||||||||||||||||||||||||||
| modifier = Modifier.padding(paddingValues), | ||||||||||||||||||||||||||||||||||||||||||
| onBackButtonClick = navigateToBack | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| @Composable | ||||||||||||||||||||||||||||||||||||||||||
| fun MypageScreen( | ||||||||||||||||||||||||||||||||||||||||||
| onBackButtonClick: () -> Unit, | ||||||||||||||||||||||||||||||||||||||||||
| onProfileEditClick: () -> Unit, | ||||||||||||||||||||||||||||||||||||||||||
| modifier: Modifier = Modifier | ||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||
| Column( | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -85,7 +88,12 @@ fun MypageScreen( | |||||||||||||||||||||||||||||||||||||||||
| style = SolplyTheme.typography.display20Sb | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| Row( | ||||||||||||||||||||||||||||||||||||||||||
| modifier = Modifier.padding(top = 12.dp) | ||||||||||||||||||||||||||||||||||||||||||
| modifier = Modifier | ||||||||||||||||||||||||||||||||||||||||||
| .padding(top = 12.dp) | ||||||||||||||||||||||||||||||||||||||||||
| .customClickable( | ||||||||||||||||||||||||||||||||||||||||||
| rippleEnabled = false, | ||||||||||||||||||||||||||||||||||||||||||
| onClick = onProfileEditClick | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+91
to
97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 프로필 수정 탭 영역이 Material 최소 터치 타깃(48dp)을 만족하지 않습니다. Row가 - Row(
- modifier = Modifier
- .padding(top = 12.dp)
- .customClickable(
- rippleEnabled = false,
- onClick = onProfileEditClick
- )
- ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 12.dp)
+ .customClickable(
+ rippleEnabled = false,
+ onClick = onProfileEditClick
+ )
+ .padding(horizontal = 20.dp, vertical = 12.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||||||||||||||||||||
| text = "프로필 수정", | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -172,7 +180,8 @@ fun MypageScreen( | |||||||||||||||||||||||||||||||||||||||||
| private fun MypageScreenPreview() { | ||||||||||||||||||||||||||||||||||||||||||
| SolplyTheme { | ||||||||||||||||||||||||||||||||||||||||||
| MypageScreen( | ||||||||||||||||||||||||||||||||||||||||||
| onBackButtonClick = {} | ||||||||||||||||||||||||||||||||||||||||||
| onBackButtonClick = {}, | ||||||||||||||||||||||||||||||||||||||||||
| onProfileEditClick = {} | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,137 @@ | ||||||||||||||||||||||||||||||||||
| package com.teamsolply.solply.mypage.component | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import androidx.compose.foundation.background | ||||||||||||||||||||||||||||||||||
| import androidx.compose.foundation.layout.Arrangement | ||||||||||||||||||||||||||||||||||
| import androidx.compose.foundation.layout.Column | ||||||||||||||||||||||||||||||||||
| import androidx.compose.foundation.layout.Row | ||||||||||||||||||||||||||||||||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||||||||||||||||||||||||||||||||
| import androidx.compose.foundation.layout.padding | ||||||||||||||||||||||||||||||||||
| import androidx.compose.material3.HorizontalDivider | ||||||||||||||||||||||||||||||||||
| import androidx.compose.material3.Text | ||||||||||||||||||||||||||||||||||
| import androidx.compose.runtime.Composable | ||||||||||||||||||||||||||||||||||
| import androidx.compose.runtime.getValue | ||||||||||||||||||||||||||||||||||
| import androidx.compose.runtime.mutableIntStateOf | ||||||||||||||||||||||||||||||||||
| import androidx.compose.runtime.mutableStateOf | ||||||||||||||||||||||||||||||||||
| import androidx.compose.runtime.remember | ||||||||||||||||||||||||||||||||||
| import androidx.compose.runtime.setValue | ||||||||||||||||||||||||||||||||||
| import androidx.compose.ui.Alignment | ||||||||||||||||||||||||||||||||||
| import androidx.compose.ui.Modifier | ||||||||||||||||||||||||||||||||||
| import androidx.compose.ui.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.ui.extension.customClickable | ||||||||||||||||||||||||||||||||||
| import kotlinx.collections.immutable.persistentListOf | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @Composable | ||||||||||||||||||||||||||||||||||
| fun SolplyPersonaDropDown( | ||||||||||||||||||||||||||||||||||
| placeholder: String, | ||||||||||||||||||||||||||||||||||
| onClickItem: (Int) -> Unit, | ||||||||||||||||||||||||||||||||||
| onClickDropIcon: () -> Unit, | ||||||||||||||||||||||||||||||||||
| dropDownContents: List<DropDownPersonaItem>, | ||||||||||||||||||||||||||||||||||
| selectedIndex: Int, | ||||||||||||||||||||||||||||||||||
| modifier: Modifier = Modifier, | ||||||||||||||||||||||||||||||||||
| isDropped: Boolean = false, | ||||||||||||||||||||||||||||||||||
| isSelected: Boolean = false | ||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||
| SolplyBasicDropDown( | ||||||||||||||||||||||||||||||||||
| defaultLabel = if (isSelected) dropDownContents.get(selectedIndex).label else placeholder, | ||||||||||||||||||||||||||||||||||
| onClickDropIcon = onClickDropIcon, | ||||||||||||||||||||||||||||||||||
| isDropped = isDropped, | ||||||||||||||||||||||||||||||||||
| modifier = modifier | ||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||
| dropDownContents.forEachIndexed { index, item -> | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+44
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 선택 인덱스 검증 없이
- defaultLabel = if (isSelected) dropDownContents.get(selectedIndex).label else placeholder,
+ defaultLabel = if (isSelected) {
+ dropDownContents.getOrNull(selectedIndex)?.label ?: placeholder
+ } else {
+ placeholder
+ },📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| if (index != selectedIndex) { | ||||||||||||||||||||||||||||||||||
| Row( | ||||||||||||||||||||||||||||||||||
| modifier = Modifier | ||||||||||||||||||||||||||||||||||
| .fillMaxWidth() | ||||||||||||||||||||||||||||||||||
| .background( | ||||||||||||||||||||||||||||||||||
| color = SolplyTheme.colors.white | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| .customClickable( | ||||||||||||||||||||||||||||||||||
| rippleEnabled = false, | ||||||||||||||||||||||||||||||||||
| onClick = { onClickItem(index) } | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| verticalAlignment = Alignment.CenterVertically, | ||||||||||||||||||||||||||||||||||
| horizontalArrangement = Arrangement.Start | ||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||||||||||||
| text = item.label, | ||||||||||||||||||||||||||||||||||
| color = SolplyTheme.colors.gray900, | ||||||||||||||||||||||||||||||||||
| style = SolplyTheme.typography.body16M, | ||||||||||||||||||||||||||||||||||
| modifier = Modifier.padding( | ||||||||||||||||||||||||||||||||||
| start = 20.dp, | ||||||||||||||||||||||||||||||||||
| top = 14.dp, | ||||||||||||||||||||||||||||||||||
| bottom = 14.dp | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| HorizontalDivider(color = SolplyTheme.colors.gray300) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @Preview | ||||||||||||||||||||||||||||||||||
| @Composable | ||||||||||||||||||||||||||||||||||
| private fun SolplyPersonaDropDownPreview() { | ||||||||||||||||||||||||||||||||||
| var isDropped by remember { mutableStateOf(false) } | ||||||||||||||||||||||||||||||||||
| var isSelected by remember { mutableStateOf(false) } | ||||||||||||||||||||||||||||||||||
| var selectedIndex by remember { mutableIntStateOf(-1) } | ||||||||||||||||||||||||||||||||||
| SolplyTheme { | ||||||||||||||||||||||||||||||||||
| Column( | ||||||||||||||||||||||||||||||||||
| modifier = Modifier | ||||||||||||||||||||||||||||||||||
| .fillMaxWidth() | ||||||||||||||||||||||||||||||||||
| .background(SolplyTheme.colors.white) | ||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||
| SolplyPersonaDropDown( | ||||||||||||||||||||||||||||||||||
| placeholder = "조용한 공간에 오래 머물고 싶어요", | ||||||||||||||||||||||||||||||||||
| onClickItem = {}, | ||||||||||||||||||||||||||||||||||
| onClickDropIcon = {}, | ||||||||||||||||||||||||||||||||||
| dropDownContents = persistentListOf( | ||||||||||||||||||||||||||||||||||
| DropDownPersonaItem( | ||||||||||||||||||||||||||||||||||
| "이곳저곳 둘러보고 싶어요" | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| DropDownPersonaItem( | ||||||||||||||||||||||||||||||||||
| "취향이 담긴 곳을 찾고 싶어요" | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| DropDownPersonaItem( | ||||||||||||||||||||||||||||||||||
| "자연을 감상하며 쉬고 싶어요" | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| DropDownPersonaItem( | ||||||||||||||||||||||||||||||||||
| "조용한 공간에 오래 머물고 싶어요" | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| isDropped = false, | ||||||||||||||||||||||||||||||||||
| selectedIndex = 0, | ||||||||||||||||||||||||||||||||||
| isSelected = false | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| SolplyPersonaDropDown( | ||||||||||||||||||||||||||||||||||
| isDropped = isDropped, | ||||||||||||||||||||||||||||||||||
| placeholder = "선택해주세요", | ||||||||||||||||||||||||||||||||||
| onClickItem = { | ||||||||||||||||||||||||||||||||||
| selectedIndex = it | ||||||||||||||||||||||||||||||||||
| isSelected = true | ||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||
| onClickDropIcon = { isDropped = !isDropped }, | ||||||||||||||||||||||||||||||||||
| dropDownContents = persistentListOf( | ||||||||||||||||||||||||||||||||||
| DropDownPersonaItem( | ||||||||||||||||||||||||||||||||||
| "이곳저곳 둘러보고 싶어요" | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| DropDownPersonaItem( | ||||||||||||||||||||||||||||||||||
| "취향이 담긴 곳을 찾고 싶어요" | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| DropDownPersonaItem( | ||||||||||||||||||||||||||||||||||
| "자연을 감상하며 쉬고 싶어요" | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| DropDownPersonaItem( | ||||||||||||||||||||||||||||||||||
| "조용한 공간에 오래 머물고 싶어요" | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||
| selectedIndex = selectedIndex, | ||||||||||||||||||||||||||||||||||
| isSelected = isSelected | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
드롭다운 토글 아이콘에 접근성 라벨이 없습니다.
아이콘에
contentDescription=""로 설정된 상태에서 클릭 동작을 부여하면 스크린리더가 “레이블 없는 버튼”으로 인식해 조작 의도를 전달하지 못합니다. 최소한 기본 라벨을 읽어 주도록 설정해 주세요.Icon( painter = painterResource(R.drawable.ic_arrow_down), - contentDescription = "", + contentDescription = defaultLabel, modifier = Modifier .padding(end = 20.dp, top = 14.dp, bottom = 14.dp) .height(24.dp) .width(24.dp)