diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/button/AppRoundButton.kt b/core/design-system/src/main/java/com/twix/designsystem/components/button/AppRoundButton.kt index 09e80e72..a0a3e638 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/button/AppRoundButton.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/button/AppRoundButton.kt @@ -3,9 +3,7 @@ package com.twix.designsystem.components.button import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.width @@ -14,8 +12,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import com.twix.designsystem.R import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor @@ -24,54 +25,65 @@ import com.twix.domain.model.enums.AppTextStyle @Composable fun AppRoundButton( - text: String, - textColor: Color, - backgroundColor: Color, - modifier: Modifier = Modifier, - textStyle: AppTextStyle = AppTextStyle.T2, - borderColor: Color = GrayColor.C500, - hasBorder: Boolean = true, + contentHeight: Dp, + contentColor: Color, + contentBorderColor: Color, + contentBorderWidth: Dp, + shadowHeight: Dp, + shadowOffset: Dp, + modifier: Modifier, + content: @Composable () -> Unit, ) { - Box( - modifier = modifier, - ) { - if (hasBorder) { - Box( - modifier = - Modifier - .fillMaxSize() - .offset(y = 4.dp) - .background( - color = borderColor, - shape = RoundedCornerShape(100), - ), - ) - } + val shape = RoundedCornerShape(999.dp) + + Box(modifier = modifier) { + Box( + modifier = + Modifier + .fillMaxWidth() + .height(shadowHeight) + .offset(y = shadowOffset) + .background(color = contentBorderColor, shape = shape), + ) Box( modifier = Modifier - .fillMaxSize() - .background( - color = backgroundColor, - shape = RoundedCornerShape(100), - ).then( - if (hasBorder) { - Modifier.border( - width = 1.dp, - color = borderColor, - shape = RoundedCornerShape(100), - ) - } else { - Modifier - }, + .fillMaxWidth() + .height(contentHeight) + .background(color = contentColor, shape = shape) + .border( + color = contentBorderColor, + shape = shape, + width = contentBorderWidth, ), contentAlignment = Alignment.Center, + ) { + content() + } + } +} + +@Preview(showBackground = true) +@Composable +private fun WhiteAppRoundButtonPreview() { + TwixTheme { + AppRoundButton( + modifier = + Modifier + .width(150.dp) + .height(74.dp), + contentColor = CommonColor.White, + contentHeight = 68.dp, + contentBorderColor = GrayColor.C500, + contentBorderWidth = 1.6.dp, + shadowHeight = 70.dp, + shadowOffset = 4.dp, ) { AppText( - style = textStyle, - color = textColor, - text = text, + style = AppTextStyle.T2, + color = GrayColor.C500, + text = "버튼 이름", ) } } @@ -79,30 +91,75 @@ fun AppRoundButton( @Preview(showBackground = true) @Composable -fun AppRoundButtonPreview() { +private fun PokeAppRoundButtonPreview() { + TwixTheme { + AppRoundButton( + modifier = + Modifier + .width(64.dp) + .height(32.dp), + contentColor = CommonColor.White, + contentHeight = 28.dp, + contentBorderColor = GrayColor.C500, + contentBorderWidth = 1.dp, + shadowHeight = 31.dp, + shadowOffset = 1.dp, + ) { + AppText( + style = AppTextStyle.C2, + color = GrayColor.C500, + text = "찌르기!", + ) + } + } +} + +@Preview(showBackground = true, backgroundColor = 0xFF000000) +@Composable +private fun BlackAppRoundButtonPreview() { TwixTheme { - Column { - AppRoundButton( - modifier = - Modifier - .width(330.dp) - .height(68.dp), - text = "버튼임니다", - textColor = GrayColor.C500, - backgroundColor = CommonColor.White, + AppRoundButton( + modifier = + Modifier + .width(150.dp) + .height(74.dp), + contentColor = GrayColor.C500, + contentHeight = 68.dp, + contentBorderColor = CommonColor.White, + contentBorderWidth = 1.6.dp, + shadowHeight = 70.dp, + shadowOffset = 4.dp, + ) { + AppText( + style = AppTextStyle.T2, + color = CommonColor.White, + text = "버튼 이름", ) - Spacer(modifier = Modifier.height(10.dp)) - AppRoundButton( - modifier = - Modifier - .width(330.dp) - .height(68.dp), - text = "버튼임니다", - textColor = CommonColor.White, - backgroundColor = GrayColor.C500, - hasBorder = false, + } + } +} + +@Preview(showBackground = true) +@Composable +private fun LongAppRoundButtonPreview() { + TwixTheme { + AppRoundButton( + modifier = + Modifier + .width(330.dp) + .height(70.dp), + contentColor = CommonColor.White, + contentHeight = 68.dp, + contentBorderColor = GrayColor.C500, + contentBorderWidth = 1.6.dp, + shadowHeight = 70.dp, + shadowOffset = 4.dp, + ) { + AppText( + style = AppTextStyle.T2, + color = GrayColor.C500, + text = stringResource(R.string.photolog_editor_retake), ) - Spacer(modifier = Modifier.height(10.dp)) } } } diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentAnchorFrame.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentAnchorFrame.kt index cce5a1ea..d448aa1a 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentAnchorFrame.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentAnchorFrame.kt @@ -10,37 +10,48 @@ import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.ime +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp +import com.twix.designsystem.R import com.twix.designsystem.components.comment.model.CommentUiModel +import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.DimmedColor +import com.twix.designsystem.theme.GrayColor +import com.twix.domain.model.enums.AppTextStyle import com.twix.ui.extension.noRippleClickable /** - * 특정 UI 요소(Anchor) 하단에 [CommentBox]를 배치하고, 키보드 활성화 상태에 따라 위치를 동적으로 조정하는 프레임 컴포저블 + * 특정 UI 요소(Anchor) 하단에 [CommentTextField]를 배치하고, 키보드 활성화 상태에 따라 위치를 동적으로 조정하는 프레임 컴포저블 * * 이 컴포저블은 평상시에는 [anchorBottom] 좌표를 기준으로 배치 * 키보드가 올라와 코맨트창이 가려질 경우 키보드 바로 위로 위치를 자동으로 이동 * - * @param uiModel 댓글창의 상태(텍스트, 포커스 상태)를 담고 있는 데이터 모델 - * @param anchorBottom 댓글창 배치의 기준이 되는 상위 요소의 바닥(Bottom) Y 좌표 (px 단위) - * @param onCommentChanged 댓글 내용이 변경될 때 호출되는 콜백 - * @param onFocusChanged 댓글창의 포커스 상태가 변경될 때 호출되는 콜백 (포커스 시 배경 딤 처리 등에 사용) + * @param uiModel 코멘트창의 상태(텍스트, 포커스 상태)를 담고 있는 데이터 모델 + * @param anchorBottom 코멘트창 배치의 기준이 되는 상위 요소의 바닥(Bottom) Y 좌표 (px 단위). + * @param onCommentChanged 코멘트 내용이 변경될 때 호출되는 콜백 + * @param onFocusChanged 코멘트창의 포커스 상태가 변경될 때 호출되는 콜백 (포커스 시 배경 딤 처리 등에 사용) * @param modifier 레이아웃 수정을 위한 [Modifier] */ @Composable fun CommentAnchorFrame( uiModel: CommentUiModel, anchorBottom: Float, + paddingBottom: Dp, onCommentChanged: (String) -> Unit, onFocusChanged: (Boolean) -> Unit, modifier: Modifier = Modifier, @@ -49,15 +60,18 @@ fun CommentAnchorFrame( val density = LocalDensity.current val focusManager = LocalFocusManager.current + val imeBottom = WindowInsets.ime.getBottom(density) - val paddingBottom = with(density) { 24.dp.toPx() } + val navBottom = WindowInsets.navigationBars.getBottom(density) + + val commentTextFieldPaddingBottom = with(density) { paddingBottom.toPx() } + val guideTextPaddingBottom = with(density) { 8.dp.toPx() } - var commentBoxHeight by remember { mutableFloatStateOf(0f) } - val defaultY = anchorBottom - commentBoxHeight - paddingBottom + var commentTextFieldHeight by remember { mutableFloatStateOf(0f) } + var guideTextHeight by remember { mutableFloatStateOf(0f) } BoxWithConstraints(modifier = modifier.fillMaxSize()) { val screenHeight = constraints.maxHeight.toFloat() - val keyboardTop = screenHeight - imeBottom AnimatedVisibility( visible = uiModel.isFocused, @@ -72,34 +86,96 @@ fun CommentAnchorFrame( .noRippleClickable { focusManager.clearFocus() }, ) } - CommentBox( + + val commentTextFieldY = + calculateCommentFieldY( + anchorBottom = anchorBottom, + screenHeight = screenHeight, + imeBottom = imeBottom, + navBottom = navBottom, + textFieldHeight = commentTextFieldHeight, + paddingBottom = commentTextFieldPaddingBottom, + ) + + if (uiModel.isFocused) { + AppText( + text = stringResource(R.string.comment_condition_guide), + style = AppTextStyle.B2, + color = GrayColor.C100, + textAlign = TextAlign.Center, + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 20.dp) + .onSizeChanged { size -> guideTextHeight = size.height.toFloat() } + .offset { + // 코멘트 입력창 바로 위에서 텍스트 높이와 패딩만큼 올려서 배치 + val offsetY = commentTextFieldY - guideTextHeight - guideTextPaddingBottom + IntOffset(0, offsetY.toInt()) + }, + ) + } + + CommentTextField( uiModel = uiModel, - onCommentChanged = onCommentChanged, + onCommitComment = onCommentChanged, onFocusChanged = onFocusChanged, onHeightMeasured = { height -> - if (commentBoxHeight != height) commentBoxHeight = height + if (height != commentTextFieldHeight) commentTextFieldHeight = height }, modifier = Modifier .fillMaxWidth() - .offset { - /** - * 키보드가 활성화되었고, 댓글창이 키보드 위치보다 아래에 있어 가려지는 경우 - * */ - val targetY = - if (imeBottom > 0 && (defaultY + commentBoxHeight) > keyboardTop) { - /** - * 화면 전체 높이 - 키보드 높이 - 코멘트 높이 - * */ - (screenHeight - imeBottom - commentBoxHeight).toInt() - } else { - /** - * 키보드가 없거나, 댓글창이 키보드에 가려지지 않는 경우 - * */ - defaultY.toInt() - } - IntOffset(x = 0, y = targetY) - }, + .offset { IntOffset(x = 0, y = commentTextFieldY) }, ) } } + +/** + * 키보드(IME) 상태에 따라 코멘트 입력창의 최적 Y 좌표(px)를 계산합니다. + * + * 키보드가 올라오지 않은 경우에는 Anchor 기준 위치를 반환하고, + * 키보드가 코멘트창을 조금이라도 가리는 경우에는 키보드 바로 위로 위치를 이동합니다. + * + * 모든 파라미터와 반환값은 px 단위입니다. + * + * @param anchorBottom 코멘트창이 기준으로 삼는 앵커 요소의 하단 Y 좌표 (px) + * @param screenHeight 화면 전체 높이 (px). 키보드 상단 좌표 계산에 사용 + * @param imeBottom 키보드(IME) 인셋 높이 (px) + * @param navBottom 네비게이션 바 인셋 높이 (px) + * @param textFieldHeight 코멘트 입력창의 실측 높이 (px) + * @param paddingBottom 코멘트 입력창과 기준점 사이의 여백 (px) + * @return 코멘트 입력창을 배치할 Y 좌표 (px) + */ +private fun calculateCommentFieldY( + anchorBottom: Float, + screenHeight: Float, + imeBottom: Int, + navBottom: Int, + textFieldHeight: Float, + paddingBottom: Float, +): Int { + // 1. 키보드가 없을 때의 기본 위치: 앵커 하단에서 코멘트창 높이 + 패딩만큼 위 + val defaultY = anchorBottom - textFieldHeight - paddingBottom + + // 2. 네비게이션 바 인셋을 제외한 키보드 순수 높이 + // 제스처 내비게이션 환경에서는 navBottom이 0이므로 그대로 imeBottom이 사용됨 + // 버튼 내비게이션 환경에서는 navBottom만큼 차감하여 실제 키보드 높이만 반영 + val pureImeHeight = (imeBottom - navBottom).coerceAtLeast(0) + + // 3. 키보드 상단 Y 좌표: 화면 하단에서 키보드 높이만큼 올라간 지점 + val keyboardTop = screenHeight - pureImeHeight + + // 4. 키보드가 활성화된 경우 코멘트창을 키보드 바로 위에 배치할 Y 좌표 + val keyboardTopY = keyboardTop - textFieldHeight - paddingBottom + + // 판정: 키보드가 올라와 있고, 기본 위치의 코멘트창 하단이 키보드 상단을 침범하는 경우 + // 즉 키보드가 조금이라도 코멘트창을 가리면 키보드 위로 즉시 이동 + val isImeVisible = imeBottom > 0 && (defaultY + textFieldHeight + paddingBottom) > keyboardTop + + return if (isImeVisible) { + keyboardTopY.toInt() + } else { + defaultY.toInt() + } +} diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt deleted file mode 100644 index 1eaa61a6..00000000 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.twix.designsystem.components.comment - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.height -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import com.twix.designsystem.R -import com.twix.designsystem.components.comment.model.CommentUiModel -import com.twix.designsystem.components.text.AppText -import com.twix.designsystem.theme.GrayColor -import com.twix.domain.model.enums.AppTextStyle - -@Composable -fun CommentBox( - uiModel: CommentUiModel, - onCommentChanged: (String) -> Unit, - onFocusChanged: (Boolean) -> Unit, - onHeightMeasured: (Float) -> Unit, - modifier: Modifier = Modifier, -) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = - modifier.onSizeChanged { size -> - onHeightMeasured(size.height.toFloat()) - }, - ) { - AppText( - text = if (uiModel.isFocused) stringResource(R.string.comment_condition_guide) else "", - style = AppTextStyle.B2, - color = GrayColor.C100, - ) - - Spacer(modifier = Modifier.height(8.dp)) - - CommentTextField( - uiModel = uiModel, - onCommitComment = onCommentChanged, - onFocusChanged = onFocusChanged, - ) - } -} diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentCircle.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentCircle.kt deleted file mode 100644 index bcb9d2ce..00000000 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentCircle.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.twix.designsystem.components.comment - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.runtime.Composable -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.twix.designsystem.components.text.AppText -import com.twix.designsystem.theme.CommonColor -import com.twix.designsystem.theme.GrayColor -import com.twix.designsystem.theme.TwixTheme -import com.twix.domain.model.enums.AppTextStyle - -@Composable -internal fun CommentCircle( - text: String, - showPlaceholder: Boolean, - showCursor: Boolean, - modifier: Modifier = Modifier, -) { - Box( - contentAlignment = Alignment.Center, - modifier = - modifier - .size(64.dp) - .background(color = CommonColor.White, shape = CircleShape), - ) { - AppText( - text = text, - style = AppTextStyle.H1, - color = if (showPlaceholder) GrayColor.C200 else GrayColor.C500, - ) - - if (showCursor) CursorBar() - } -} - -@Composable -private fun CursorBar() { - Box( - modifier = - Modifier - .width(2.dp) - .height(28.dp) - .background(GrayColor.C500), - ) -} - -@Preview -@Composable -private fun CommentCirclePreview() { - TwixTheme { - CommentCircle(text = "1", showPlaceholder = false, showCursor = false) - } -} diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentFieldBackground.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentFieldBackground.kt new file mode 100644 index 00000000..2875eb18 --- /dev/null +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentFieldBackground.kt @@ -0,0 +1,175 @@ +package com.twix.designsystem.components.comment + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.PathOperation +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.twix.designsystem.theme.CommonColor +import com.twix.designsystem.theme.GrayColor +import com.twix.designsystem.theme.TwixTheme + +/** + * 코멘트 입력 UI의 배경이 되는 연결형 원형 도형을 그리는 컴포저블. + * + * 여러 개의 원을 가로 방향으로 배치한 뒤 [PathOperation.Union]으로 합쳐, + * 하나의 연결된 배경 도형처럼 렌더링한다. + * + * @param circleCount 배경을 구성하는 원의 개수. + * @param circleSize 각 원의 지름이자 전체 배경의 높이. + * @param circleCenterSpacing 인접한 원 중심점 사이의 거리. + * @param modifier 외부에서 전달받는 [Modifier]. + */ +@Composable +internal fun CommentFieldBackground( + circleCount: Int, + circleSize: Dp, + circleCenterSpacing: Dp, + modifier: Modifier = Modifier, +) { + /** + * Canvas 내부 draw scope는 px 단위를 사용하므로, + * 디자인에서 정의된 dp 값을 실제 그리기에 사용할 px 값으로 변환한다. + */ + val density = LocalDensity.current + + /** + * 피그마 기준 테두리 두께(1.6dp)를 px로 변환한 값. + */ + val strokeWidth = with(density) { 1.6.dp.toPx() } + + /** + * 원 중심 간 간격을 px로 변환한 값. + * 각 원의 x 시작 위치(left)를 계산할 때 사용한다. + */ + val circleSpacing = with(density) { circleCenterSpacing.toPx() } + + /** + * 전체 배경 도형의 가로 길이. + * + * 첫 번째 원의 전체 너비(circleSize)에 + * 나머지 원들이 중심 간격(circleCenterSpacing)만큼 오른쪽으로 이동하며 배치되므로 + * 아래 공식으로 전체 폭을 계산한다. + * + * e.g + * - circleSize = 64.dp + * - circleCenterSpacing = 50.dp + * - circleCount = 5 + * => totalWidth = 64 + 50 * 4 = 264.dp + */ + val totalWidth = circleSize + circleCenterSpacing * (circleCount - 1) + + Canvas( + modifier = + modifier + .width(totalWidth) + .height(circleSize), + ) { + /** + * Canvas의 높이를 원의 지름으로 사용한다. + * 이 컴포저블은 높이 == 원 지름인 정원(circular) 기준으로 동작한다. + */ + val circleDiameter = size.height + + /** + * 모든 원을 합쳐 만든 최종 Path. + * + * 처음에는 null이고, + * 원을 하나씩 만들면서 Union 연산으로 계속 누적한다. + */ + var unionPath: Path? = null + + repeat(circleCount) { index -> + /** + * 현재 원의 왼쪽 시작 x 좌표. + * + * 원의 중심 간격을 기준으로 각 원을 오른쪽으로 이동시킨다. + * 0번째 원 -> 0 + * 1번째 원 -> spacing + * 2번째 원 -> spacing * 2 + * ... + */ + val left = index * circleSpacing + + /** + * 현재 순서의 원 하나를 나타내는 Path. + * + * addOval(Rect)는 지정된 사각형에 맞는 타원을 추가한다. + */ + val ovalPath = + Path().apply { + addOval( + Rect( + left = left, + top = 0f, + right = left + circleDiameter, + bottom = circleDiameter, + ), + ) + } + + /** + * 누적된 Path와 현재 원 Path를 합집합(Union)으로 결합한다. + * + * - 첫 번째 원이면 unionPath가 없으므로 그대로 사용 + * - 두 번째 원부터는 기존 Path와 새 원을 합쳐 하나의 연결된 도형으로 만든다 + * + * 이 과정 덕분에 원과 원이 겹치는 내부 경계선은 제거되고, + * 최종적으로 외곽선만 남는 하나의 shape처럼 다룰 수 있다. + */ + unionPath = + if (unionPath == null) { + ovalPath + } else { + Path.combine( + operation = PathOperation.Union, + path1 = unionPath, + path2 = ovalPath, + ) + } + } + + /** + * circleCount가 0인 비정상 케이스를 방어한다. + * 정상 흐름에서는 null이 아니어야 한다. + */ + val finalPath = unionPath ?: return@Canvas + + /** + * 합쳐진 최종 배경 도형 내부를 흰색으로 채운다. + */ + drawPath( + path = finalPath, + color = CommonColor.White, + ) + + /** + * 최종 Path의 외곽선을 그린다. + */ + drawPath( + path = finalPath, + color = GrayColor.C500, + style = Stroke(strokeWidth), + ) + } +} + +@Preview(showBackground = true) +@Composable +private fun CommentFieldBackgroundPreview() { + TwixTheme { + CommentFieldBackground( + circleCount = 5, + circleSize = 40.dp, + circleCenterSpacing = 30.dp, + ) + } +} diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt index 8f3ca856..68dedfa7 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentTextField.kt @@ -1,8 +1,11 @@ package com.twix.designsystem.components.comment +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions @@ -14,14 +17,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange @@ -32,14 +34,16 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.twix.designsystem.R import com.twix.designsystem.components.comment.model.CommentUiModel +import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme +import com.twix.domain.model.enums.AppTextStyle import com.twix.ui.extension.noRippleClickable import com.twix.ui.keyboard.Keyboard import com.twix.ui.keyboard.keyboardAsState -val CIRCLE_PADDING_START: Dp = 50.dp -val CIRCLE_SIZE: Dp = 64.dp +private val CIRCLE_PADDING_START: Dp = 50.dp +private val CIRCLE_SIZE: Dp = 64.dp private val CIRCLE_GAP: Dp = CIRCLE_PADDING_START - CIRCLE_SIZE @Composable @@ -47,6 +51,7 @@ fun CommentTextField( uiModel: CommentUiModel, modifier: Modifier = Modifier, enabled: Boolean = true, + onHeightMeasured: (Float) -> Unit = {}, onCommitComment: (String) -> Unit = {}, onFocusChanged: (Boolean) -> Unit = {}, ) { @@ -98,19 +103,25 @@ fun CommentTextField( } Box( + contentAlignment = Alignment.Center, modifier = modifier - .noRippleClickable { + .onSizeChanged { size -> + onHeightMeasured(size.height.toFloat()) + }.noRippleClickable { focusRequester.requestFocus() }, ) { TextField( value = internalValue, onValueChange = { newValue -> - if (newValue.text.length <= CommentUiModel.COMMENT_COUNT) { + val filteredText = newValue.text.filterNot(Char::isWhitespace) + + if (filteredText.length <= CommentUiModel.COMMENT_COUNT) { internalValue = newValue.copy( - selection = TextRange(newValue.text.length), + text = filteredText, + selection = TextRange(filteredText.length), ) } }, @@ -128,49 +139,73 @@ fun CommentTextField( singleLine = true, ) - Row( - horizontalArrangement = Arrangement.spacedBy(CIRCLE_GAP), - modifier = - Modifier.drawWithCache { - val radius = size.height / 2 - val paddingStart = CIRCLE_PADDING_START.toPx() - - onDrawBehind { - repeat(CommentUiModel.COMMENT_COUNT) { index -> - val cx = radius + index * paddingStart - - drawCircle( - color = GrayColor.C500, - radius = radius, - center = Offset(cx, radius), - style = Stroke(2.dp.toPx()), - ) - } - } - }, + Box( + contentAlignment = Alignment.Center, ) { - repeat(CommentUiModel.COMMENT_COUNT) { index -> - val char = - if (uiModel.isFocused || internalValue.text.isNotEmpty()) { - internalValue.text.getOrNull(index)?.toString() - } else { - stringResource(R.string.comment_text_field_placeholder)[index].toString() - }.orEmpty() - - CommentCircle( - text = char, - showPlaceholder = !uiModel.isFocused && internalValue.text.isEmpty(), - showCursor = uiModel.isFocused && index == internalValue.text.length, - modifier = - Modifier.noRippleClickable { - focusRequester.requestFocus() - }, - ) + CommentFieldBackground( + circleCount = CommentUiModel.COMMENT_COUNT, + circleSize = CIRCLE_SIZE, + circleCenterSpacing = CIRCLE_PADDING_START, + ) + + Row( + horizontalArrangement = Arrangement.spacedBy(CIRCLE_GAP), + ) { + repeat(CommentUiModel.COMMENT_COUNT) { index -> + val char = + if (uiModel.isFocused || internalValue.text.isNotEmpty()) { + internalValue.text.getOrNull(index)?.toString() + } else { + stringResource(R.string.comment_text_field_placeholder)[index].toString() + }.orEmpty() + + CommentText( + text = char, + showPlaceholder = !uiModel.isFocused && internalValue.text.isEmpty(), + showCursor = uiModel.isFocused && index == internalValue.text.length, + modifier = + Modifier.noRippleClickable { + focusRequester.requestFocus() + }, + ) + } } } } } +@Composable +private fun CommentText( + text: String, + showPlaceholder: Boolean, + showCursor: Boolean, + modifier: Modifier = Modifier, +) { + Box( + contentAlignment = Alignment.Center, + modifier = modifier.size(CIRCLE_SIZE), + ) { + AppText( + text = text, + style = AppTextStyle.H1, + color = if (showPlaceholder) GrayColor.C200 else GrayColor.C500, + ) + + if (showCursor) CursorBar() + } +} + +@Composable +private fun CursorBar() { + Box( + modifier = + Modifier + .width(2.dp) + .height(28.dp) + .background(GrayColor.C500), + ) +} + @Preview(showBackground = true) @Composable private fun CommentTextFieldPreview() { @@ -180,6 +215,7 @@ private fun CommentTextFieldPreview() { CommentTextField( uiModel = CommentUiModel(text, isFocused), onFocusChanged = { isFocused = it }, + onHeightMeasured = {}, ) } } diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/comment/model/CommentUiModel.kt b/core/design-system/src/main/java/com/twix/designsystem/components/comment/model/CommentUiModel.kt index 284c0bad..21277408 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/comment/model/CommentUiModel.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/comment/model/CommentUiModel.kt @@ -12,8 +12,8 @@ data class CommentUiModel( val canUpload: Boolean get() = - value.isEmpty() || - value.isNotEmpty() && + value.isBlank() || + value.isNotBlank() && hasMaxCommentLength fun updateComment(newComment: String): CommentUiModel = copy(value = newComment) diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt index 482eda29..2bfa2de4 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/BackgroundCard.kt @@ -43,7 +43,7 @@ fun BackgroundCard( AppText( text = uploadedAt, style = AppTextStyle.B4, - color = GrayColor.C500, + color = GrayColor.C300, modifier = Modifier .padding(end = 36.dp, top = 14.dp) @@ -61,10 +61,19 @@ fun BackgroundCard( .width(150.dp) .height(74.dp) .noRippleClickable { onClickAction() }, - text = actionLabel, - textColor = GrayColor.C500, - backgroundColor = CommonColor.White, - ) + contentColor = CommonColor.White, + contentHeight = 68.dp, + contentBorderColor = GrayColor.C500, + contentBorderWidth = 1.6.dp, + shadowHeight = 70.dp, + shadowOffset = 4.dp, + ) { + AppText( + style = AppTextStyle.T2, + color = GrayColor.C500, + text = actionLabel, + ) + } } Image( diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/photolog/ForegroundCard.kt b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/ForegroundCard.kt index 38303e28..b3b3f833 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/photolog/ForegroundCard.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/ForegroundCard.kt @@ -2,6 +2,7 @@ package com.twix.designsystem.components.photolog import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import com.twix.designsystem.R import com.twix.designsystem.components.text.AppText @@ -31,7 +32,7 @@ fun ForegroundCard( AppText( text = when (currentShow) { - BetweenUs.ME -> stringResource(R.string.keep_it_up) + BetweenUs.ME -> stringResource(R.string.photolog_detail_upload_guide) BetweenUs.PARTNER -> stringResource(R.string.partner_not_photolog).format( nickName, @@ -39,6 +40,7 @@ fun ForegroundCard( }, style = AppTextStyle.H2, color = GrayColor.C500, + textAlign = TextAlign.Center, ) } } @@ -49,7 +51,7 @@ fun ForegroundCard( private fun ForegroundCardPreview() { TwixTheme { ForegroundCard( - isCertificated = true, + isCertificated = false, nickName = "닉네임", imageUrl = "https://picsum.photos/200/300", comment = null, diff --git a/core/design-system/src/main/java/com/twix/designsystem/components/photolog/PhotologCard.kt b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/PhotologCard.kt index c89f068b..848297b3 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/components/photolog/PhotologCard.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/components/photolog/PhotologCard.kt @@ -28,6 +28,8 @@ fun PhotologCard( rotation: Float = 0f, content: @Composable BoxScope.() -> Unit = {}, ) { + val shape = RoundedCornerShape(20.dp) + Box( modifier = modifier @@ -35,11 +37,11 @@ fun PhotologCard( .fillMaxWidth() .aspectRatio(1f) .rotate(rotation) - .clip(shape = RoundedCornerShape(12.dp)) + .clip(shape = shape) .border( width = 1.6.dp, color = borderColor, - shape = RoundedCornerShape(12.dp), + shape = shape, ).background(background), contentAlignment = Alignment.Center, content = content, @@ -48,7 +50,7 @@ fun PhotologCard( @Preview @Composable -fun PhotologCardPreview() { +private fun PhotologCardPreview() { TwixTheme { PhotologCard( rotation = 0f, diff --git a/core/design-system/src/main/java/com/twix/designsystem/theme/Colors.kt b/core/design-system/src/main/java/com/twix/designsystem/theme/Colors.kt index 197f0057..7c97bb29 100644 --- a/core/design-system/src/main/java/com/twix/designsystem/theme/Colors.kt +++ b/core/design-system/src/main/java/com/twix/designsystem/theme/Colors.kt @@ -13,6 +13,7 @@ object GrayColor { object CommonColor { val White = Color(0xFFFFFFFF) + val Black = Color(0xFF000000) } object DimmedColor { diff --git a/core/design-system/src/main/res/drawable/ic_camera_torch_off.xml b/core/design-system/src/main/res/drawable/ic_camera_torch_off.xml index db90d65e..8a781105 100644 --- a/core/design-system/src/main/res/drawable/ic_camera_torch_off.xml +++ b/core/design-system/src/main/res/drawable/ic_camera_torch_off.xml @@ -1,22 +1,18 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - diff --git a/core/design-system/src/main/res/drawable/ic_camera_torch_on.xml b/core/design-system/src/main/res/drawable/ic_camera_torch_on.xml index c3949a31..6758d592 100644 --- a/core/design-system/src/main/res/drawable/ic_camera_torch_on.xml +++ b/core/design-system/src/main/res/drawable/ic_camera_torch_on.xml @@ -1,14 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - + android:pathData="M12.393,3.289C12.556,3.04 12.87,2.937 13.15,3.04C13.43,3.143 13.601,3.427 13.563,3.723L12.752,9.913H17.906C18.138,9.913 18.351,10.037 18.465,10.239C18.578,10.441 18.573,10.688 18.452,10.886L12.117,21.254C11.963,21.506 11.658,21.62 11.377,21.531C11.097,21.442 10.912,21.173 10.931,20.879L11.34,14.648H6.14C5.905,14.648 5.689,14.519 5.577,14.313C5.466,14.106 5.475,13.854 5.604,13.657L12.393,3.289Z" + android:fillColor="#ffffff"/> diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index abf6eb67..c77e0576 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -114,6 +114,16 @@ 인증샷 촬영을 위해서 카메라 권한이 필요해요. 알림 목록 조회에 실패했습니다. 찌르기에 실패했습니다. + 리액션 요청에 실패했어요. + 인증샷 등록에 실패했습니다. + 이미지 변환에 실패했습니다. + 인증샷 촬영에 실패했습니다. 다시 시도해 주세요. + 인증샷 조회에 실패했습니다. + 코멘트가 수정 되었어요. + 코멘트가 수정되지 않았어요. + 코멘트 수정에 실패했어요. + 인증샷 수정에 실패했어요. + 코멘트는 5글자로 입력해주세요! %s\n목표를 이루셨나요? @@ -131,27 +141,16 @@ 업로드 - 이미지 캡처에 실패했습니다. 다시 시도해 주세요. - 이미지를 불러오는 데 실패했습니다. 다시 시도해 주세요. + 업로드하기 인증샷 찍기 - 인증샷 등록에 실패했습니다. - 이미지 변환에 실패했습니다. 인증샷 업로드 중... 잠시만 기다려 주세요. - 코멘트는 5글자로 입력해주세요! - - 인증샷 조회에 실패했습니다. - 인증샷 수정에 실패했어요. + + 인증샷을\n올려보세요! 다시 찍기 - 코멘트가 수정 되었어요. - 코멘트가 수정되지 않았어요. - 코멘트 수정에 실패했어요. - 리액션 요청에 실패했어요. - - 인증샷 촬영을 위해서 카메라 권한이 필요해요. Google로 시작하기 diff --git a/feature/main/src/main/java/com/twix/home/component/GoalVerifications.kt b/feature/main/src/main/java/com/twix/home/component/GoalVerifications.kt index e1b25212..9364325e 100644 --- a/feature/main/src/main/java/com/twix/home/component/GoalVerifications.kt +++ b/feature/main/src/main/java/com/twix/home/component/GoalVerifications.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable @@ -89,17 +90,25 @@ private fun EmptyContent( contentDescription = null, modifier = Modifier.size(width = 85.dp, height = 53.dp), ) - AppRoundButton( - text = stringResource(R.string.action_poke_emphasized), - textColor = GrayColor.C500, - textStyle = AppTextStyle.C2, - backgroundColor = CommonColor.White, modifier = Modifier - .size(width = 64.dp, height = 28.dp) + .width(64.dp) + .height(32.dp) .noRippleClickable { onClick() }, - ) + contentColor = CommonColor.White, + contentHeight = 28.dp, + contentBorderColor = GrayColor.C500, + contentBorderWidth = 1.dp, + shadowHeight = 31.dp, + shadowOffset = 1.dp, + ) { + AppText( + style = AppTextStyle.C2, + color = GrayColor.C500, + text = stringResource(R.string.action_poke_emphasized), + ) + } } } else { Column( diff --git a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/PhotologCaptureScreen.kt b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/PhotologCaptureScreen.kt index ef71b8fc..5880ec52 100644 --- a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/PhotologCaptureScreen.kt +++ b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/PhotologCaptureScreen.kt @@ -130,7 +130,7 @@ fun PhotologCaptureRoute( onToggleCameraClick = { viewModel.dispatch(PhotologCaptureIntent.ToggleLens) }, - onClickFlash = { + onClickTorch = { viewModel.dispatch(PhotologCaptureIntent.ToggleTorch) }, onClickGallery = { @@ -159,7 +159,7 @@ private fun PhotologCaptureScreen( onClickClose: () -> Unit, onCaptureClick: () -> Unit, onToggleCameraClick: () -> Unit, - onClickFlash: () -> Unit, + onClickTorch: () -> Unit, onClickGallery: () -> Unit, onClickRefresh: () -> Unit, onClickUpload: () -> Unit, @@ -200,7 +200,7 @@ private fun PhotologCaptureScreen( capture = uiState.capture, previewRequest = cameraPreview, torch = uiState.torch, - onClickFlash = onClickFlash, + onClickTorch = onClickTorch, onPositioned = { previewBoxBottom = it }, ) @@ -219,6 +219,7 @@ private fun PhotologCaptureScreen( CommentAnchorFrame( uiModel = uiState.comment, anchorBottom = previewBoxBottom, + paddingBottom = 28.dp, onCommentChanged = onCommentChanged, onFocusChanged = onFocusChanged, ) @@ -235,7 +236,7 @@ fun PhotologCaptureScreenPreview() { onClickClose = {}, onCaptureClick = {}, onToggleCameraClick = {}, - onClickFlash = {}, + onClickTorch = {}, onClickGallery = {}, onClickRefresh = {}, onClickUpload = {}, diff --git a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/PhotologCaptureViewModel.kt b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/PhotologCaptureViewModel.kt index da1c8d1e..1297034d 100644 --- a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/PhotologCaptureViewModel.kt +++ b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/PhotologCaptureViewModel.kt @@ -61,7 +61,7 @@ class PhotologCaptureViewModel( private fun takePicture(uri: Uri?) { uri?.let { reducePicture(it) } ?: showToast( - R.string.photolog_image_capture_fail, + R.string.toast_image_capture_fail, ToastType.ERROR, ) } @@ -112,7 +112,7 @@ class PhotologCaptureViewModel( if (imageBytes != null) { upload(imageBytes) } else { - showToast(R.string.photolog_image_translate_fail, ToastType.ERROR) + showToast(R.string.toast_image_translate_fail, ToastType.ERROR) } } } @@ -140,7 +140,7 @@ class PhotologCaptureViewModel( onSuccess = { fileName -> handleUploadPhotologSuccess(fileName) }, onError = { reduce { copy(isLoading = false) } - showToast(R.string.photolog_upload_fail, ToastType.ERROR) + showToast(R.string.toast_photolog_upload_fail, ToastType.ERROR) }, ) } @@ -169,7 +169,7 @@ class PhotologCaptureViewModel( }, onSuccess = { handleUploadPhotologSuccess() }, onError = { - showToast(R.string.photolog_upload_fail, ToastType.ERROR) + showToast(R.string.toast_photolog_upload_fail, ToastType.ERROR) }, ) } @@ -198,7 +198,7 @@ class PhotologCaptureViewModel( }, onSuccess = { handleModifyPhotologSuccess() }, onError = { - showToast(R.string.photolog_modify_fail, ToastType.ERROR) + showToast(R.string.toast_photolog_modify_fail, ToastType.ERROR) }, ) } diff --git a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraControlBar.kt b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraControlBar.kt index 5b5b9deb..80b2d0bd 100644 --- a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraControlBar.kt +++ b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraControlBar.kt @@ -2,7 +2,6 @@ package com.twix.photolog.capture.component import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -27,6 +26,7 @@ import androidx.compose.ui.unit.dp import androidx.core.net.toUri import com.twix.designsystem.R import com.twix.designsystem.components.button.AppRoundButton +import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme @@ -119,40 +119,40 @@ private fun ImageCapturedBar( onClickUpload: () -> Unit, modifier: Modifier = Modifier, ) { - Box( + Row( modifier = modifier .fillMaxWidth() .padding(horizontal = 58.dp), + verticalAlignment = Alignment.CenterVertically, ) { Image( imageVector = ImageVector.vectorResource(R.drawable.ic_camera_retake), contentDescription = null, modifier = Modifier - .size(52.dp) - .align(Alignment.CenterStart) .noRippleClickable(onClick = onClickRefresh), ) - Row( + Spacer(modifier = Modifier.width(12.dp)) + + AppRoundButton( modifier = Modifier - .align(Alignment.Center), + .width(150.dp) + .height(74.dp) + .noRippleClickable(onClick = onClickUpload), + contentColor = GrayColor.C500, + contentHeight = 68.dp, + contentBorderColor = CommonColor.White, + contentBorderWidth = 1.6.dp, + shadowHeight = 70.dp, + shadowOffset = 4.dp, ) { - Spacer(modifier = Modifier.width(12.dp)) - - AppRoundButton( - borderColor = CommonColor.White, - backgroundColor = GrayColor.C500, + AppText( + style = AppTextStyle.T2, + color = CommonColor.White, text = stringResource(R.string.photolog_upload), - textStyle = AppTextStyle.T2, - textColor = CommonColor.White, - modifier = - Modifier - .width(150.dp) - .height(74.dp) - .noRippleClickable(onClick = onClickUpload), ) } } diff --git a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraPreviewBox.kt b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraPreviewBox.kt index 68668ea2..5639fa7b 100644 --- a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraPreviewBox.kt +++ b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraPreviewBox.kt @@ -1,31 +1,27 @@ package com.twix.photolog.capture.component import androidx.camera.compose.CameraXViewfinder -import androidx.compose.foundation.Image import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.boundsInParent import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage -import com.twix.designsystem.R import com.twix.designsystem.theme.GrayColor import com.twix.designsystem.theme.TwixTheme import com.twix.photolog.capture.model.CaptureStatus import com.twix.photolog.capture.model.TorchStatus import com.twix.photolog.capture.model.camera.CameraPreview -import com.twix.ui.extension.noRippleClickable @Composable fun CameraPreviewBox( @@ -33,14 +29,15 @@ fun CameraPreviewBox( capture: CaptureStatus, previewRequest: CameraPreview?, torch: TorchStatus, - onClickFlash: () -> Unit, + onClickTorch: () -> Unit, onPositioned: (Float) -> Unit, modifier: Modifier = Modifier, ) { Box( modifier = modifier - .size(375.66.dp) + .fillMaxWidth() + .aspectRatio(1f) .padding(horizontal = 5.dp) .onGloballyPositioned { coordinates -> onPositioned(coordinates.boundsInParent().bottom) @@ -53,7 +50,11 @@ fun CameraPreviewBox( CameraSurface(capture, previewRequest) if (showTorch) { - TorchIcon(torch, onClickFlash) + TorchButton( + torch = torch, + onClickTorch = onClickTorch, + modifier = Modifier.padding(top = 31.dp, start = 30.dp), + ) } } } @@ -84,27 +85,6 @@ private fun CameraSurface( } } -@Composable -private fun TorchIcon( - torch: TorchStatus, - onClickFlash: () -> Unit, -) { - val torchIcon = - when (torch) { - TorchStatus.On -> ImageVector.vectorResource(id = R.drawable.ic_camera_torch_on) - TorchStatus.Off -> ImageVector.vectorResource(id = R.drawable.ic_camera_torch_off) - } - - Image( - imageVector = torchIcon, - contentDescription = null, - modifier = - Modifier - .noRippleClickable(onClick = onClickFlash) - .padding(start = 30.33.dp, top = 31.82.dp), - ) -} - @Preview @Composable fun CameraPreviewBoxNotCapturedPreview() { @@ -114,7 +94,7 @@ fun CameraPreviewBoxNotCapturedPreview() { showTorch = true, torch = TorchStatus.Off, previewRequest = null, - onClickFlash = {}, + onClickTorch = {}, onPositioned = {}, ) } diff --git a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CommentErrorText.kt b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CommentErrorText.kt index 4d1bc90e..6e2e7b40 100644 --- a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CommentErrorText.kt +++ b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CommentErrorText.kt @@ -30,7 +30,7 @@ fun CommentErrorText(modifier: Modifier = Modifier) { .clip(RoundedCornerShape(12.dp)), ) { AppText( - text = stringResource(R.string.comment_error_message), + text = stringResource(R.string.toast_comment_length_guide), style = AppTextStyle.B1, color = CommonColor.White, modifier = Modifier.align(Alignment.Center), diff --git a/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/TorchButton.kt b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/TorchButton.kt new file mode 100644 index 00000000..a407ee6c --- /dev/null +++ b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/TorchButton.kt @@ -0,0 +1,78 @@ +package com.twix.photolog.capture.component + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.dropShadow +import androidx.compose.ui.graphics.shadow.Shadow +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp +import com.twix.designsystem.R +import com.twix.designsystem.theme.CommonColor +import com.twix.designsystem.theme.TwixTheme +import com.twix.photolog.capture.model.TorchStatus +import com.twix.ui.extension.noRippleClickable + +@Composable +internal fun TorchButton( + torch: TorchStatus, + onClickTorch: () -> Unit, + modifier: Modifier = Modifier, +) { + val torchIcon = + when (torch) { + TorchStatus.On -> R.drawable.ic_camera_torch_on + TorchStatus.Off -> R.drawable.ic_camera_torch_off + } + + Box( + modifier = + modifier + .size(44.dp) + .dropShadow( + shape = CircleShape, + shadow = + Shadow( + radius = 10.67.dp, + spread = 0.dp, + color = CommonColor.Black.copy(alpha = 0.3f), + offset = DpOffset(0.dp, 0.dp), + ), + ).background( + color = CommonColor.White.copy(alpha = 0.1f), + shape = CircleShape, + ).noRippleClickable(onClick = onClickTorch), + contentAlignment = Alignment.Center, + ) { + Icon( + painter = painterResource(torchIcon), + contentDescription = "Flash Icon", + tint = CommonColor.White, + ) + } +} + +@Preview(showBackground = true, backgroundColor = 0xFFD9D9D9) +@Composable +private fun TorchButtonPreview() { + TwixTheme { + Column { + TorchButton( + torch = TorchStatus.On, + onClickTorch = {}, + ) + TorchButton( + torch = TorchStatus.Off, + onClickTorch = {}, + ) + } + } +} diff --git a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetail.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetailScreen.kt similarity index 89% rename from feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetail.kt rename to feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetailScreen.kt index a232fde8..e3f1d108 100644 --- a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetail.kt +++ b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetailScreen.kt @@ -9,10 +9,15 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity @@ -28,6 +33,7 @@ import com.twix.designsystem.components.toast.model.ToastType import com.twix.designsystem.extension.showCameraPermissionToastWithNavigateToSettingAction import com.twix.designsystem.theme.CommonColor import com.twix.designsystem.theme.TwixTheme +import com.twix.domain.model.enums.BetweenUs import com.twix.domain.model.enums.GoalReactionType import com.twix.photolog.detail.component.PhotologCardContent import com.twix.photolog.detail.component.PhotologDetailTopBar @@ -161,9 +167,12 @@ fun PhotologDetailScreen( onPoke: () -> Unit, onSwipe: () -> Unit, ) { + val scrollState = rememberScrollState() + Column( Modifier .fillMaxSize() + .verticalScroll(scrollState) .background(color = CommonColor.White), ) { PhotologDetailTopBar( @@ -199,14 +208,25 @@ private fun PhotologDetailScreenPreview( uiState: PhotologDetailUiState, ) { TwixTheme { + var previewState by remember { mutableStateOf(uiState) } PhotologDetailScreen( - uiState = uiState, + uiState = previewState, onBack = {}, onClickModify = {}, onClickReaction = {}, onClickUpload = {}, onPoke = {}, - onSwipe = {}, + onSwipe = { + previewState = + previewState.copy( + currentShow = + if (previewState.currentShow == BetweenUs.ME) { + BetweenUs.PARTNER + } else { + BetweenUs.ME + }, + ) + }, ) } } diff --git a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetailViewModel.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetailViewModel.kt index 3c4cebdc..441189dd 100644 --- a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetailViewModel.kt +++ b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/PhotologDetailViewModel.kt @@ -79,7 +79,7 @@ class PhotologDetailViewModel( } }, onError = { - showToast(R.string.photolog_detail_fetch_photolog_fail, ToastType.ERROR) + showToast(R.string.toast_photolog_detail_fetch_fail, ToastType.ERROR) }, onFinally = { reduce { copy(isLoading = true) } }, ) @@ -106,7 +106,7 @@ class PhotologDetailViewModel( onSuccess = {}, onError = { rollbackReaction() - showToast(R.string.photolog_detail_reaction_fail, ToastType.ERROR) + showToast(R.string.toast_reaction_fail, ToastType.ERROR) }, ) } diff --git a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/PhotologCardContent.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/PhotologCardContent.kt index d8768c55..1c6bd0d2 100644 --- a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/PhotologCardContent.kt +++ b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/PhotologCardContent.kt @@ -34,7 +34,7 @@ internal fun PhotologCardContent( uploadedAt = uiState.displayedGoalUpdateAt, actionLabel = when (uiState.currentShow) { - BetweenUs.ME -> stringResource(R.string.photolog_take_picture) + BetweenUs.ME -> stringResource(R.string.photolog_picture_upload) BetweenUs.PARTNER -> stringResource(R.string.action_poke) }, rotation = if (uiState.isDisplayedMyPhotolog) -8f else 0f, diff --git a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/PhotologDetailTopBar.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/PhotologDetailTopBar.kt index 4e14cc9c..23fd40c7 100644 --- a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/PhotologDetailTopBar.kt +++ b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/PhotologDetailTopBar.kt @@ -49,7 +49,7 @@ internal fun PhotologDetailTopBar( modifier = Modifier .fillMaxSize() - .background(GrayColor.C100) + .background(GrayColor.C050) .noRippleClickable { onClickModify() }, contentAlignment = Alignment.Center, ) { diff --git a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/reaction/ReactionBar.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/reaction/ReactionBar.kt index 3d62265c..49284f18 100644 --- a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/reaction/ReactionBar.kt +++ b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/reaction/ReactionBar.kt @@ -6,8 +6,10 @@ import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.VerticalDivider @@ -31,21 +33,20 @@ fun ReactionBar( selectedReaction: GoalReactionType? = null, ) { val shape = RoundedCornerShape(999.dp) - val height = 68.dp - val shadowOffset = 10.dp - val shadowStart = 13.dp Box( modifier = modifier .fillMaxWidth() - .height(height + shadowOffset), + .height(77.dp), ) { Box( modifier = Modifier - .matchParentSize() - .padding(start = shadowStart, top = shadowOffset) + .fillMaxWidth() + .height(67.dp) + .padding(start = 1.dp) + .offset(y = 10.dp) .background(GrayColor.C200, shape), ) @@ -53,14 +54,22 @@ fun ReactionBar( modifier = Modifier .fillMaxWidth() - .height(height) + .height(68.dp) .border(width = 1.dp, color = GrayColor.C500, shape = shape) .background(GrayColor.C100, shape) .clip(shape), verticalAlignment = Alignment.CenterVertically, ) { + val lastIndex = ReactionUiModel.entries.lastIndex + ReactionUiModel.entries.forEachIndexed { index, reaction -> val isSelected = reaction.type == selectedReaction + val paddingModifier = + when (index) { + 0 -> Modifier.padding(start = 11.dp, end = 7.dp) + lastIndex -> Modifier.padding(start = 7.dp, end = 11.dp) + else -> Modifier.padding(horizontal = 9.dp) + } Box( modifier = @@ -69,16 +78,24 @@ fun ReactionBar( .fillMaxHeight() .background( if (isSelected) GrayColor.C300 else GrayColor.C100, - ).noRippleClickable(onClick = { onSelectReaction(reaction.type) }), + ).noRippleClickable(onClick = { onSelectReaction(reaction.type) }) + .then(paddingModifier), contentAlignment = Alignment.Center, ) { - Image( - imageVector = ImageVector.vectorResource(reaction.imageResources), - contentDescription = null, - ) + Box( + modifier = + Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center, + ) { + Image( + imageVector = ImageVector.vectorResource(reaction.imageResources), + contentDescription = null, + ) + } } - if (index < 4) { + if (index < lastIndex) { VerticalDivider( modifier = Modifier.fillMaxHeight(), color = GrayColor.C500, diff --git a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/swipe/SwipeCardSpec.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/swipe/SwipeCardSpec.kt index 3ad087cd..99cb0caa 100644 --- a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/swipe/SwipeCardSpec.kt +++ b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/swipe/SwipeCardSpec.kt @@ -8,19 +8,13 @@ import androidx.compose.ui.unit.dp */ data class SwipeCardSpec( /** dismiss 판정 거리(dp) */ - val dismissThresholdDp: Dp = 60.dp, - /** 화면 밖으로 날아가는 거리(px) */ - val dismissDistancePx: Float = 1000f, + val dismissThreshold: Dp = 80.dp, /** dismiss 애니메이션 시간(ms) */ val dismissDuration: Int = 150, /** 회전 계산 비율 */ val rotationFactor: Float = 28f, /** 최대 회전 각도 */ - val maxRotation: Float = 8f, + val maxRotation: Float = 16f, /** 복귀 시 위치 비율 */ val reappearOffsetRatio: Float = 0.2f, - /** spring damping */ - val springDamping: Float = 0.84f, - /** spring stiffness */ - val springStiffness: Float = 300f, ) diff --git a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/swipe/SwipeableCard.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/swipe/SwipeableCard.kt index f5483dca..23e6654b 100644 --- a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/swipe/SwipeableCard.kt +++ b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/component/swipe/SwipeableCard.kt @@ -1,10 +1,9 @@ package com.twix.photolog.detail.component.swipe import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.spring -import androidx.compose.animation.core.tween import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.offset import androidx.compose.runtime.Composable import androidx.compose.runtime.remember @@ -14,7 +13,6 @@ import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.IntOffset -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch import kotlin.math.abs import kotlin.math.roundToInt @@ -26,10 +24,8 @@ import kotlin.math.roundToInt * 1. 사용자가 카드를 드래그 * 2. threshold 이상 이동 시 → 카드 dismiss * 3. 화면 밖으로 날아간 뒤 onSwipe 호출 - * 4. 반대편에서 다시 등장 (spring 복귀) + * 4. 반대편에서 다시 등장 * - * ## 커스터마이징 - * 모든 애니메이션/거리 값은 [SwipeCardSpec] 으로 조절 가능 */ @Composable fun SwipeableCard( @@ -41,6 +37,7 @@ fun SwipeableCard( ) { val coroutineScope = rememberCoroutineScope() val density = LocalDensity.current + val threshold = with(density) { spec.dismissThreshold.toPx() } /** * 카드 상태 값 @@ -55,108 +52,70 @@ fun SwipeableCard( (offsetX.value / spec.rotationFactor) .coerceIn(-spec.maxRotation, spec.maxRotation) - Box( - modifier = - modifier - /** - * 카드 위치 이동 - */ - .offset { - IntOffset( - offsetX.value.roundToInt(), - 0, - ) - } - /** - * 회전 + 투명도 적용 - */ - .graphicsLayer { - rotationZ = rotation - alpha = opacity.value - }.pointerInput(isDisplayingMyPhoto) { - detectDragGestures( - /** - * 드래그 중 - * → 위치 즉시 반영 (snap) - */ - onDrag = { change, dragAmount -> - change.consume() + BoxWithConstraints(modifier = modifier) { + val dismissDistance = constraints.maxWidth * 1.3f - coroutineScope.launch { - offsetX.snapTo(offsetX.value + dragAmount.x) - } - }, - /** - * 드래그 종료 시 처리 - */ - onDragEnd = { - val thresholdPx = with(density) { spec.dismissThresholdDp.toPx() } - val shouldDismiss = abs(offsetX.value) > thresholdPx - - if (shouldDismiss) { + Box( + modifier = + modifier + /** + * 카드 위치 이동 + */ + .offset { + IntOffset( + offsetX.value.roundToInt(), + 0, + ) + } + /** + * 회전 + 투명도 적용 + */ + .graphicsLayer { + rotationZ = rotation + alpha = opacity.value + }.pointerInput(isDisplayingMyPhoto) { + detectDragGestures( + /** + * 드래그 중 + * → 위치 즉시 반영 (snap) + */ + onDrag = { _, dragAmount -> coroutineScope.launch { - /** - * 화면 밖으로 날리기 - */ - val targetX = - if (offsetX.value > 0) { - spec.dismissDistancePx - } else { - -spec.dismissDistancePx - } - - val targetY = 0f - - /** - * dismiss 애니메이션 완료 대기 - */ - coroutineScope { - launch { offsetX.animateTo(targetX, tween(spec.dismissDuration)) } - launch { opacity.animateTo(0f, tween(spec.dismissDuration)) } - } - - /** - * 데이터 교체 - */ - onSwipe() + offsetX.snapTo(offsetX.value + dragAmount.x) + } + }, + /** + * 드래그 종료 시 처리 + */ + onDragEnd = { + val shouldDismiss = abs(offsetX.value) > threshold + coroutineScope.launch { + if (shouldDismiss) { + onSwipe() - /** - * 반대편 위치 세팅 - */ - val reappearStartX = - if (isDisplayingMyPhoto) { - spec.dismissDistancePx * spec.reappearOffsetRatio - } else { - -spec.dismissDistancePx * spec.reappearOffsetRatio - } - offsetX.snapTo(reappearStartX) + /** + * 반대편 위치 세팅 + */ + val reappearStartX = + if (isDisplayingMyPhoto) { + dismissDistance * spec.reappearOffsetRatio + } else { + -dismissDistance * spec.reappearOffsetRatio + } + offsetX.snapTo(reappearStartX) - /** - * 스프링 복귀 - */ - launch { - offsetX.animateTo( - 0f, - spring( - dampingRatio = spec.springDamping, - stiffness = spec.springStiffness, - ), - ) - } - launch { - opacity.animateTo(1f, spring(spec.springDamping)) + launch { offsetX.animateTo(0f) } + launch { opacity.animateTo(1f) } + } else { + // threshold 미만 → 제자리 복귀 + launch { offsetX.animateTo(0f) } } } - } else { - // threshold 미만 → 제자리 복귀 - coroutineScope.launch { - launch { offsetX.animateTo(0f, spring()) } - } - } - }, - ) - }, - ) { - content() + }, + ) + }, + ) { + content() + } } } diff --git a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/preview/PhotologDetailPreviewProvider.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/preview/PhotologDetailPreviewProvider.kt index a4d06649..8b8f2389 100644 --- a/feature/photolog/detail/src/main/java/com/twix/photolog/detail/preview/PhotologDetailPreviewProvider.kt +++ b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/preview/PhotologDetailPreviewProvider.kt @@ -29,6 +29,7 @@ class PhotologDetailPreviewProvider : PreviewParameterProvider { toastManager.tryShow( ToastData(sideEffect.message, ToastType.SUCCESS), @@ -157,7 +165,9 @@ fun PhotologEditorScreen( .background(color = CommonColor.White) .noRippleClickable { focusManager.clearFocus() }, ) { - Column { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { PhotologEditorTopBar( title = uiState.goalName, onBack = onBack, @@ -196,12 +206,37 @@ fun PhotologEditorScreen( CommentAnchorFrame( uiModel = uiState.comment, anchorBottom = photologBottom, + paddingBottom = 24.dp, onCommentChanged = onCommentChanged, onFocusChanged = onFocusChanged, ) } } +@Composable +private fun RetakeButton(onClickRetake: () -> Unit) { + AppRoundButton( + modifier = + Modifier + .fillMaxWidth() + .height(70.dp) + .noRippleClickable(onClick = onClickRetake) + .padding(horizontal = 30.dp), + contentColor = CommonColor.White, + contentHeight = 68.dp, + contentBorderColor = GrayColor.C500, + contentBorderWidth = 1.6.dp, + shadowHeight = 70.dp, + shadowOffset = 4.dp, + ) { + AppText( + style = AppTextStyle.T2, + color = GrayColor.C500, + text = stringResource(R.string.photolog_editor_retake), + ) + } +} + @Preview(showBackground = true) @Composable private fun PhotologEditorScreenPreview() { diff --git a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorViewModel.kt b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorViewModel.kt index b7f0002d..6ccf737c 100644 --- a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorViewModel.kt +++ b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorViewModel.kt @@ -55,19 +55,19 @@ class PhotologEditorViewModel( private fun modifyComment() { if (currentState.comment.canUpload.not()) { - showToast(R.string.comment_error_message, ToastType.ERROR) + showToast(R.string.toast_comment_length_guide, ToastType.ERROR) } else if (currentState.isCommentNotChanged) { - showToast(R.string.photolog_editor_not_modified, ToastType.ERROR) + showToast(R.string.toast_comment_not_modified, ToastType.ERROR) } else { launchResult( block = { launchModifyComment() }, onSuccess = { detailRefreshBus.notifyChanged(PhotologRefreshBus.Publisher.EDITOR) goalRefreshBus.notifyGoalListChanged() - showToast(R.string.photolog_editor_modify_success, ToastType.SUCCESS) + showToast(R.string.toast_comment_modify_success, ToastType.SUCCESS) }, onError = { - showToast(R.string.photolog_editor_modify_fail, ToastType.ERROR) + showToast(R.string.toast_comment_modify_fail, ToastType.ERROR) }, ) } @@ -89,7 +89,7 @@ class PhotologEditorViewModel( block = { photologRepository.fetchPhotologs(argTargetDate, argGoalId) }, onSuccess = { reduce { it.toEditorUiState(argGoalId, argTargetDate) } }, onError = { - showToast(R.string.photolog_detail_fetch_photolog_fail, ToastType.ERROR) + showToast(R.string.toast_photolog_detail_fetch_fail, ToastType.ERROR) }, ) } diff --git a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/component/RetakeButton.kt b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/component/RetakeButton.kt deleted file mode 100644 index 7ea34426..00000000 --- a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/component/RetakeButton.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.twix.photolog.editor.component - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.twix.designsystem.R -import com.twix.designsystem.components.button.AppRoundButton -import com.twix.designsystem.theme.CommonColor -import com.twix.designsystem.theme.GrayColor -import com.twix.designsystem.theme.TwixTheme -import com.twix.ui.extension.noRippleClickable - -@Composable -internal fun RetakeButton( - onClickRetake: () -> Unit, - modifier: Modifier = Modifier, -) { - AppRoundButton( - text = stringResource(R.string.photolog_editor_retake), - textColor = GrayColor.C500, - backgroundColor = CommonColor.White, - modifier = - modifier - .fillMaxWidth() - .height(68.dp) - .padding(horizontal = 30.dp) - .noRippleClickable { onClickRetake() }, - ) -} - -@Preview -@Composable -private fun RetakeButtonPreview() { - TwixTheme { - RetakeButton(onClickRetake = {}) - } -}