-
Notifications
You must be signed in to change notification settings - Fork 2
[Feature] 콜밴 글쓰기 시간 선택 필드 추가 #1325
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
8c9d869
766e48f
38f9eb1
1c7b399
d863c65
690c186
b084470
694c975
fdcc07d
dc0e19d
dac817f
3ccc1a0
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,38 @@ | ||
| package `in`.koreatech.koin.feature.callvan.ui.create.component | ||
|
|
||
| import androidx.compose.foundation.clickable | ||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.material3.Text | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.ui.Alignment | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.compose.ui.unit.dp | ||
| import `in`.koreatech.koin.core.designsystem.theme.RebrandKoinTheme | ||
| import `in`.koreatech.koin.feature.callvan.R | ||
|
|
||
| @Composable | ||
| fun CallvanPickerFooter( | ||
| onReset: () -> Unit, | ||
| onConfirm: () -> Unit | ||
| ) { | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.End) | ||
| ) { | ||
| Text( | ||
| text = stringResource(R.string.callvan_create_picker_reset), | ||
| style = RebrandKoinTheme.typography.medium14, | ||
| color = RebrandKoinTheme.colors.primary500, | ||
| modifier = Modifier.clickable(onClick = onReset) | ||
KYM-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ) | ||
| Text( | ||
| text = stringResource(R.string.callvan_create_picker_confirm), | ||
| style = RebrandKoinTheme.typography.medium14, | ||
| color = RebrandKoinTheme.colors.primary500, | ||
| modifier = Modifier.clickable(onClick = onConfirm) | ||
| ) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,212 @@ | ||||||||||||||||||||||||
| package `in`.koreatech.koin.feature.callvan.ui.create.component | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import androidx.compose.animation.AnimatedVisibility | ||||||||||||||||||||||||
| import androidx.compose.animation.expandVertically | ||||||||||||||||||||||||
| import androidx.compose.animation.shrinkVertically | ||||||||||||||||||||||||
| import androidx.compose.foundation.border | ||||||||||||||||||||||||
| import androidx.compose.foundation.clickable | ||||||||||||||||||||||||
| 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.height | ||||||||||||||||||||||||
| import androidx.compose.foundation.layout.padding | ||||||||||||||||||||||||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||||||||||||||||||||||||
| import androidx.compose.material3.Card | ||||||||||||||||||||||||
| import androidx.compose.material3.CardDefaults | ||||||||||||||||||||||||
| import androidx.compose.material3.HorizontalDivider | ||||||||||||||||||||||||
| import androidx.compose.material3.Text | ||||||||||||||||||||||||
| import androidx.compose.material3.VerticalDivider | ||||||||||||||||||||||||
| import androidx.compose.runtime.Composable | ||||||||||||||||||||||||
| import androidx.compose.runtime.remember | ||||||||||||||||||||||||
| import androidx.compose.ui.Alignment | ||||||||||||||||||||||||
| import androidx.compose.ui.Modifier | ||||||||||||||||||||||||
| import androidx.compose.ui.res.stringResource | ||||||||||||||||||||||||
| import androidx.compose.ui.text.style.TextAlign | ||||||||||||||||||||||||
| import androidx.compose.ui.tooling.preview.Preview | ||||||||||||||||||||||||
| import androidx.compose.ui.unit.dp | ||||||||||||||||||||||||
| import `in`.koreatech.koin.core.designsystem.theme.RebrandKoinTheme | ||||||||||||||||||||||||
| import `in`.koreatech.koin.feature.callvan.R | ||||||||||||||||||||||||
| import java.util.Locale | ||||||||||||||||||||||||
| import kotlinx.collections.immutable.toImmutableList | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Composable | ||||||||||||||||||||||||
| fun CallvanTimeField( | ||||||||||||||||||||||||
|
Check warning on line 34 in feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanTimeField.kt
|
||||||||||||||||||||||||
| formattedTime: String, | ||||||||||||||||||||||||
| isPickerVisible: Boolean, | ||||||||||||||||||||||||
| isAm: Boolean, | ||||||||||||||||||||||||
| selectedHour: Int, | ||||||||||||||||||||||||
| selectedMinute: Int, | ||||||||||||||||||||||||
| onFieldClick: () -> Unit = {}, | ||||||||||||||||||||||||
| onAmPmIndexChange: (Int) -> Unit = {}, | ||||||||||||||||||||||||
| onHourIndexChange: (Int) -> Unit = {}, | ||||||||||||||||||||||||
| onMinuteIndexChange: (Int) -> Unit = {}, | ||||||||||||||||||||||||
| onReset: () -> Unit = {}, | ||||||||||||||||||||||||
| onConfirm: () -> Unit = {} | ||||||||||||||||||||||||
KYM-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| val amPmText = stringResource(if (isAm) R.string.callvan_am else R.string.callvan_pm) | ||||||||||||||||||||||||
KYM-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Column(modifier = Modifier.fillMaxWidth()) { | ||||||||||||||||||||||||
| Column( | ||||||||||||||||||||||||
| modifier = Modifier | ||||||||||||||||||||||||
| .fillMaxWidth() | ||||||||||||||||||||||||
| .padding(horizontal = 24.dp, vertical = 12.dp), | ||||||||||||||||||||||||
| verticalArrangement = Arrangement.spacedBy(4.dp) | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| CallvanSectionHeader( | ||||||||||||||||||||||||
| label = stringResource(R.string.callvan_create_time_label), | ||||||||||||||||||||||||
| hint = stringResource(R.string.callvan_create_time_hint) | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| Row( | ||||||||||||||||||||||||
| modifier = Modifier | ||||||||||||||||||||||||
| .fillMaxWidth() | ||||||||||||||||||||||||
| .border(1.dp, RebrandKoinTheme.colors.neutral400, RoundedCornerShape(4.dp)) | ||||||||||||||||||||||||
| .clickable(onClick = onFieldClick), | ||||||||||||||||||||||||
| verticalAlignment = Alignment.CenterVertically | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
|
Contributor
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. [Major] 접근성 - 클릭 가능한 Row에 시맨틱 정보 누락 현재
Suggested change
Row(
modifier = Modifier
.fillMaxWidth()
.border(1.dp, RebrandKoinTheme.colors.neutral400, RoundedCornerShape(4.dp))
.semantics {
role = Role.Button
contentDescription = "$amPmText $formattedTime, 시간 선택"
}
.clickable(onClick = onFieldClick),
verticalAlignment = Alignment.CenterVertically
) {
|
||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||
| text = amPmText, | ||||||||||||||||||||||||
| style = RebrandKoinTheme.typography.regular14, | ||||||||||||||||||||||||
| color = RebrandKoinTheme.colors.neutral800, | ||||||||||||||||||||||||
| modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp) | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| VerticalDivider( | ||||||||||||||||||||||||
| modifier = Modifier.height(38.dp), | ||||||||||||||||||||||||
|
Contributor
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. [Minor] VerticalDivider 높이 하드코딩 - 접근성 설정 시 레이아웃 깨짐 가능성
Row(
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min) // Row 높이를 자식 최대 높이로
.border(...)
.clickable(onClick = onFieldClick),
verticalAlignment = Alignment.CenterVertically
) {
// ...
VerticalDivider(
modifier = Modifier.fillMaxHeight(), // 38.dp 대신
color = RebrandKoinTheme.colors.neutral400
)
// ...
}
|
||||||||||||||||||||||||
| color = RebrandKoinTheme.colors.neutral400 | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| Text( | ||||||||||||||||||||||||
| text = formattedTime, | ||||||||||||||||||||||||
|
Comment on lines
+77
to
+78
Contributor
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. [Minor] 빈 시간 문자열에 대한 placeholder 미처리
Suggested change
|
||||||||||||||||||||||||
| style = RebrandKoinTheme.typography.regular14, | ||||||||||||||||||||||||
| color = RebrandKoinTheme.colors.neutral800, | ||||||||||||||||||||||||
| modifier = Modifier | ||||||||||||||||||||||||
| .weight(1f) | ||||||||||||||||||||||||
| .padding(horizontal = 16.dp, vertical = 8.dp) | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| AnimatedVisibility( | ||||||||||||||||||||||||
| visible = isPickerVisible, | ||||||||||||||||||||||||
| enter = expandVertically(), | ||||||||||||||||||||||||
| exit = shrinkVertically() | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| CallvanTimePickerCard( | ||||||||||||||||||||||||
| isAm = isAm, | ||||||||||||||||||||||||
| selectedHour = selectedHour, | ||||||||||||||||||||||||
| selectedMinute = selectedMinute, | ||||||||||||||||||||||||
| onAmPmIndexChange = onAmPmIndexChange, | ||||||||||||||||||||||||
| onHourIndexChange = onHourIndexChange, | ||||||||||||||||||||||||
| onMinuteIndexChange = onMinuteIndexChange, | ||||||||||||||||||||||||
| onReset = onReset, | ||||||||||||||||||||||||
| onConfirm = onConfirm | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
KYM-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Comment on lines
+33
to
+104
Contributor
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. [Info] 잘 작성된 코드 — 칭찬 전반적인 설계가 훌륭합니다:
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Suppress("LongParameterList") | ||||||||||||||||||||||||
| @Composable | ||||||||||||||||||||||||
| private fun CallvanTimePickerCard( | ||||||||||||||||||||||||
|
Check warning on line 108 in feature/callvan/src/main/java/in/koreatech/koin/feature/callvan/ui/create/component/CallvanTimeField.kt
|
||||||||||||||||||||||||
| isAm: Boolean, | ||||||||||||||||||||||||
| selectedHour: Int, | ||||||||||||||||||||||||
| selectedMinute: Int, | ||||||||||||||||||||||||
| onAmPmIndexChange: (Int) -> Unit, | ||||||||||||||||||||||||
| onHourIndexChange: (Int) -> Unit, | ||||||||||||||||||||||||
| onMinuteIndexChange: (Int) -> Unit, | ||||||||||||||||||||||||
| onReset: () -> Unit, | ||||||||||||||||||||||||
| onConfirm: () -> Unit | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| val amLabel = stringResource(R.string.callvan_am) | ||||||||||||||||||||||||
| val pmLabel = stringResource(R.string.callvan_pm) | ||||||||||||||||||||||||
| val amPmItems = remember(amLabel, pmLabel) { | ||||||||||||||||||||||||
| listOf(amLabel, pmLabel).toImmutableList() | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
Contributor
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. [Info] 잘 작성된 코드 - val amPmItems = remember(amLabel, pmLabel) {
listOf(amLabel, pmLabel).toImmutableList()
}런타임 언어 변경(locale change) 시
KYM-P marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| val hourItems = remember { | ||||||||||||||||||||||||
| (1..12).map { it.toString() }.toImmutableList() | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| val minuteItems = remember { | ||||||||||||||||||||||||
| (0..59).map { String.format(Locale.ROOT, "%02d", it) }.toImmutableList() | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| val amPmIndex = if (isAm) 0 else 1 | ||||||||||||||||||||||||
| val hourIndex = (selectedHour - 1).coerceIn(0, 11) | ||||||||||||||||||||||||
| val minuteIndex = selectedMinute.coerceIn(0, 59) | ||||||||||||||||||||||||
|
Contributor
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. [Minor] val hourIndex = (selectedHour - 1).coerceIn(0, 11)
피커와 필드 헤더의 값이 불일치하는 상황이 발생합니다. 초기 상태를 // ViewModel 또는 UseCase에서:
val selectedHour: Int = savedHour.coerceIn(1, 12) |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Card( | ||||||||||||||||||||||||
| modifier = Modifier | ||||||||||||||||||||||||
| .fillMaxWidth() | ||||||||||||||||||||||||
| .padding(horizontal = 24.dp), | ||||||||||||||||||||||||
| shape = RoundedCornerShape(8.dp), | ||||||||||||||||||||||||
| colors = CardDefaults.cardColors(containerColor = RebrandKoinTheme.colors.neutral100), | ||||||||||||||||||||||||
| elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| Column( | ||||||||||||||||||||||||
| modifier = Modifier | ||||||||||||||||||||||||
| .fillMaxWidth() | ||||||||||||||||||||||||
| .padding(horizontal = 24.dp, vertical = 12.dp), | ||||||||||||||||||||||||
| verticalArrangement = Arrangement.spacedBy(8.dp) | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| Row( | ||||||||||||||||||||||||
| modifier = Modifier.fillMaxWidth() | ||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||
| CallvanScrollPicker( | ||||||||||||||||||||||||
| items = amPmItems, | ||||||||||||||||||||||||
| selectedIndex = amPmIndex, | ||||||||||||||||||||||||
| onIndexChange = onAmPmIndexChange, | ||||||||||||||||||||||||
| modifier = Modifier.weight(1f) | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| CallvanScrollPicker( | ||||||||||||||||||||||||
| items = hourItems, | ||||||||||||||||||||||||
| selectedIndex = hourIndex, | ||||||||||||||||||||||||
| onIndexChange = onHourIndexChange, | ||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||
| modifier = Modifier.weight(1f), | ||||||||||||||||||||||||
| textAlign = TextAlign.End | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| CallvanScrollPicker( | ||||||||||||||||||||||||
| items = minuteItems, | ||||||||||||||||||||||||
| selectedIndex = minuteIndex, | ||||||||||||||||||||||||
| onIndexChange = onMinuteIndexChange, | ||||||||||||||||||||||||
| modifier = Modifier.weight(1f), | ||||||||||||||||||||||||
| textAlign = TextAlign.End | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| HorizontalDivider(color = RebrandKoinTheme.colors.neutral200) | ||||||||||||||||||||||||
| CallvanPickerFooter(onReset = onReset, onConfirm = onConfirm) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Preview(showBackground = true) | ||||||||||||||||||||||||
| @Composable | ||||||||||||||||||||||||
| private fun CallvanTimeFieldPreview() { | ||||||||||||||||||||||||
| CallvanTimeField( | ||||||||||||||||||||||||
| formattedTime = "09:30", | ||||||||||||||||||||||||
| isPickerVisible = false, | ||||||||||||||||||||||||
| isAm = true, | ||||||||||||||||||||||||
| selectedHour = 9, | ||||||||||||||||||||||||
| selectedMinute = 30, | ||||||||||||||||||||||||
| onFieldClick = {}, | ||||||||||||||||||||||||
| onAmPmIndexChange = {}, | ||||||||||||||||||||||||
| onHourIndexChange = {}, | ||||||||||||||||||||||||
| onMinuteIndexChange = {}, | ||||||||||||||||||||||||
| onReset = {}, | ||||||||||||||||||||||||
| onConfirm = {} | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| @Preview(showBackground = true) | ||||||||||||||||||||||||
| @Composable | ||||||||||||||||||||||||
| private fun CallvanTimeFieldPickerVisiblePreview() { | ||||||||||||||||||||||||
| CallvanTimeField( | ||||||||||||||||||||||||
| formattedTime = "02:45", | ||||||||||||||||||||||||
| isPickerVisible = true, | ||||||||||||||||||||||||
| isAm = false, | ||||||||||||||||||||||||
| selectedHour = 2, | ||||||||||||||||||||||||
| selectedMinute = 45, | ||||||||||||||||||||||||
| onFieldClick = {}, | ||||||||||||||||||||||||
| onAmPmIndexChange = {}, | ||||||||||||||||||||||||
| onHourIndexChange = {}, | ||||||||||||||||||||||||
| onMinuteIndexChange = {}, | ||||||||||||||||||||||||
| onReset = {}, | ||||||||||||||||||||||||
| onConfirm = {} | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
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.
[Minor] Row 자체에도 수직 패딩 부재
Row에 수직 패딩이 없어, Card 내부에서
HorizontalDivider바로 아래에 버튼들이 붙어 보일 수 있습니다. 시각적 여백을 위해 Row에 패딩을 추가하는 것을 권장합니다.