diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextArea.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextArea.kt index 06af1699..60ec6ecb 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextArea.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextArea.kt @@ -14,13 +14,17 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldLabel @@ -46,18 +50,28 @@ fun PrezelTextArea( keyboardActions: KeyboardActions = KeyboardActions.Default, ) { var focused by remember { mutableStateOf(false) } + var textFieldValue by remember { mutableStateOf(TextFieldValue(text = value, selection = TextRange(value.length))) } + + LaunchedEffect(value) { + if (value != textFieldValue.text) { + textFieldValue = TextFieldValue(text = value, selection = TextRange(value.length)) + } + } val style = rememberPrezelTextFieldState( - value = value, + value = textFieldValue.text, enabled = enabled, focused = focused, ).let { state -> PrezelTextFieldStyle(state = state, status = status) } PrezelTextArea( - value = value, + value = textFieldValue, onValueChange = { newValue -> - val applied = applyPrezelTextInputPolicy(newValue, maxLength) - if (applied != value) onValueChange(applied) + val applied = applyPrezelTextInputPolicy(currentValue = textFieldValue, newValue = newValue, maxLength = maxLength) + if (applied != textFieldValue) { + textFieldValue = applied + if (applied.text != value) onValueChange(applied.text) + } }, placeholder = placeholder, style = style, @@ -75,8 +89,8 @@ fun PrezelTextArea( @Composable private fun PrezelTextArea( - value: String, - onValueChange: (String) -> Unit, + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, placeholder: String, maxLength: Int, style: PrezelTextFieldStyle, @@ -110,13 +124,13 @@ private fun PrezelTextArea( decorationBox = { innerTextField -> PrezelTextAreaDecorationBox( innerTextField = innerTextField, - showPlaceholder = !focused && value.isEmpty(), + showPlaceholder = !focused && value.text.isEmpty(), placeholder = placeholder, state = style, + showCounter = showCount, counter = { if (showCount) { - Spacer(modifier = Modifier.height(PrezelTheme.spacing.V16)) - Counter(currentLength = value.length, maxLength = maxLength, state = style) + Counter(currentLength = value.text.length, maxLength = maxLength, state = style) } }, modifier = Modifier.heightIn(min = 72.dp), @@ -154,6 +168,7 @@ private fun PrezelTextAreaDecorationBox( counter: @Composable () -> Unit, placeholder: String, state: PrezelTextFieldStyle, + showCounter: Boolean, modifier: Modifier = Modifier, ) { Surface( @@ -163,20 +178,25 @@ private fun PrezelTextAreaDecorationBox( border = state.borderStroke(), contentColor = state.textColor(), ) { - Column( + Box( modifier = Modifier .fillMaxWidth() .padding(PrezelTheme.spacing.V12), - verticalArrangement = Arrangement.SpaceBetween, ) { - Box { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = if (showCounter) PrezelTheme.spacing.V24 else 0.dp), + ) { innerTextField() if (showPlaceholder) { PrezelTextFieldPlaceholder(placeholder = placeholder) } } - counter() + Box(modifier = Modifier.align(Alignment.BottomEnd)) { + counter() + } } } } @@ -304,7 +324,7 @@ private fun PrezelTextAreaPreviewItem( focused: Boolean = false, ) { PrezelTextArea( - value = value, + value = TextFieldValue(text = value, selection = TextRange(value.length)), onValueChange = {}, placeholder = "Placeholder", label = label, diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextField.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextField.kt index 942f23d9..d627b18d 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextField.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextField.kt @@ -18,6 +18,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -27,6 +28,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextRange +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldLabel import com.team.prezel.core.designsystem.component.textfield.component.PrezelTextFieldPlaceholder @@ -52,18 +55,28 @@ fun PrezelTextField( keyboardActions: KeyboardActions = KeyboardActions.Default, ) { var focused by remember { mutableStateOf(false) } + var textFieldValue by remember { mutableStateOf(TextFieldValue(text = value, selection = TextRange(value.length))) } + + LaunchedEffect(value) { + if (value != textFieldValue.text) { + textFieldValue = TextFieldValue(text = value, selection = TextRange(value.length)) + } + } val state = rememberPrezelTextFieldState( - value = value, + value = textFieldValue.text, enabled = enabled, focused = focused, ).let { state -> PrezelTextFieldStyle(state = state, status = status) } PrezelTextField( - value = value, + value = textFieldValue, onValueChange = { newValue -> - val applied = applyPrezelTextInputPolicy(newValue, maxLength) - if (applied != value) onValueChange(applied) + val applied = applyPrezelTextInputPolicy(currentValue = textFieldValue, newValue = newValue, maxLength = maxLength) + if (applied != textFieldValue) { + textFieldValue = applied + if (applied.text != value) onValueChange(applied.text) + } }, placeholder = placeholder, style = state, @@ -80,8 +93,8 @@ fun PrezelTextField( @Composable private fun PrezelTextField( - value: String, - onValueChange: (String) -> Unit, + value: TextFieldValue, + onValueChange: (TextFieldValue) -> Unit, placeholder: String, style: PrezelTextFieldStyle, focused: Boolean, @@ -115,7 +128,7 @@ private fun PrezelTextField( decorationBox = { innerTextField -> PrezelTextFieldDecorationBox( innerTextField = innerTextField, - showPlaceholder = !focused && value.isEmpty(), + showPlaceholder = !focused && value.text.isEmpty(), placeholder = placeholder, trailingIcon = trailingIcon, state = style, @@ -296,7 +309,7 @@ private fun PreviewTextFieldItem( focused: Boolean = false, ) { PrezelTextField( - value = value, + value = TextFieldValue(text = value, selection = TextRange(value.length)), onValueChange = {}, placeholder = "Placeholder", label = label, diff --git a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextFieldState.kt b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextFieldState.kt index 190a1e84..b36b3219 100644 --- a/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextFieldState.kt +++ b/Prezel/core/designsystem/src/main/java/com/team/prezel/core/designsystem/component/textfield/PrezelTextFieldState.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import com.team.prezel.core.designsystem.foundation.color.PrezelColors import com.team.prezel.core.designsystem.theme.PrezelTheme @@ -223,11 +224,11 @@ internal fun rememberPrezelTextFieldState( ) internal fun applyPrezelTextInputPolicy( - value: String, + currentValue: TextFieldValue, + newValue: TextFieldValue, maxLength: Int, -): String { - require(maxLength >= 0) { "maxLength must be >= 0" } - return value - .replace("\n", "") - .take(maxLength) +): TextFieldValue { + require(maxLength >= 0) { "maxLength는 0 이상이어야 합니다." } + + return if (newValue.text.length > maxLength) currentValue else newValue } diff --git a/Prezel/feature/feedback/impl/src/main/java/com/team/prezel/feature/feedback/impl/FeedbackScreen.kt b/Prezel/feature/feedback/impl/src/main/java/com/team/prezel/feature/feedback/impl/FeedbackScreen.kt index 04bf67ee..d684b315 100644 --- a/Prezel/feature/feedback/impl/src/main/java/com/team/prezel/feature/feedback/impl/FeedbackScreen.kt +++ b/Prezel/feature/feedback/impl/src/main/java/com/team/prezel/feature/feedback/impl/FeedbackScreen.kt @@ -17,11 +17,13 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringArrayResource import androidx.compose.ui.res.stringResource import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -174,6 +176,8 @@ private fun FeedbackContent( modifier: Modifier = Modifier, ) { val focusManager = LocalFocusManager.current + val placeholders = stringArrayResource(R.array.feature_feedback_impl_placeholders) + val placeholder = remember(placeholders.contentHashCode()) { placeholders.random() } Column( modifier = modifier @@ -193,7 +197,7 @@ private fun FeedbackContent( PrezelTextArea( value = content, onValueChange = onContentChanged, - placeholder = stringResource(R.string.feature_feedback_impl_placeholder), + placeholder = placeholder, maxLength = 200, showCount = true, ) diff --git a/Prezel/feature/feedback/impl/src/main/res/values/strings.xml b/Prezel/feature/feedback/impl/src/main/res/values/strings.xml index c5db6ad0..c5d5555c 100644 --- a/Prezel/feature/feedback/impl/src/main/res/values/strings.xml +++ b/Prezel/feature/feedback/impl/src/main/res/values/strings.xml @@ -2,7 +2,11 @@ 셀프 피드백 닫기 \'%1$s\'는 어떠셨나요? - 다음 발표에는 어떤 부분을 신경쓰고 싶나요? + + 발표를 마친 지금, 어떤 기분이 드나요? + 다음 발표에서는 무엇을 조금 더 신경 써보고 싶나요? + 이번 발표에서 만족스러웠던 부분은 무엇이었나요? + 저장하기 셀프 피드백을 불러오지 못했어요. 해당 데이터에 접근할 권한이 없습니다. diff --git a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt index e8a84bf3..43358c53 100644 --- a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt +++ b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/body/PresentationSheet.kt @@ -30,7 +30,13 @@ internal fun PresentationSheet( modifier = modifier, contentPadding = PaddingValues(vertical = PrezelTheme.spacing.V32, horizontal = PrezelTheme.spacing.V20), ) { - HomeBottomSheetTitle(title = stringResource(R.string.feature_home_impl_bottom_sheet_content_title, presentation.practiceCount)) + HomeBottomSheetTitle( + title = if (presentation.practiceCount == 0) { + stringResource(R.string.feature_home_impl_empty_sheet_practice_card_title) + } else { + stringResource(R.string.feature_home_impl_bottom_sheet_content_title, presentation.practiceCount) + }, + ) PracticeCard( dDay = presentation.practiceRecords.endDate, items = presentation.practiceRecords.practices, diff --git a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/title/PracticeActionCard.kt b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/title/PracticeActionCard.kt index 6547f2cd..c2d3957c 100644 --- a/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/title/PracticeActionCard.kt +++ b/Prezel/feature/home/impl/src/main/java/com/team/prezel/feature/home/impl/main/component/title/PracticeActionCard.kt @@ -32,29 +32,29 @@ internal fun PracticeActionCard( modifier: Modifier = Modifier, onClick: () -> Unit, ) { - PrezelTouchArea( - onClick = onClick, - shape = PrezelTheme.shapes.V8, + Column( + modifier = modifier + .fillMaxWidth() + .clip(shape = PrezelTheme.shapes.V8) + .border( + width = PrezelTheme.stroke.V1, + shape = PrezelTheme.shapes.V8, + color = PrezelTheme.colors.borderSmall, + ).background(color = PrezelTheme.colors.bgRegular) + .padding(horizontal = PrezelTheme.spacing.V16, vertical = PrezelTheme.spacing.V12), ) { - Column( - modifier = modifier - .fillMaxWidth() - .clip(shape = PrezelTheme.shapes.V8) - .border( - width = PrezelTheme.stroke.V1, - shape = PrezelTheme.shapes.V8, - color = PrezelTheme.colors.borderSmall, - ).background(color = PrezelTheme.colors.bgRegular) - .padding(horizontal = PrezelTheme.spacing.V16, vertical = PrezelTheme.spacing.V12), - ) { - Text( - text = title, - color = titleColor, - style = PrezelTheme.typography.body2Bold, - ) + Text( + text = title, + color = titleColor, + style = PrezelTheme.typography.body2Bold, + ) - Spacer(modifier = Modifier.height(PrezelTheme.spacing.V6)) + Spacer(modifier = Modifier.height(PrezelTheme.spacing.V6)) + PrezelTouchArea( + onClick = onClick, + shape = PrezelTheme.shapes.V4, + ) { Row(verticalAlignment = Alignment.CenterVertically) { Text( text = actionText,