From 35b374d29ae5e337c0728157020a6797de59e3e5 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 7 Mar 2026 17:06:07 +0900 Subject: [PATCH 01/32] =?UTF-8?q?=E2=9C=A8=20Feat:=20CommentTextField.kt?= =?UTF-8?q?=20=ED=8C=A8=EB=94=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/comment/CommentAnchorFrame.kt | 134 ++++++++++++++---- .../components/comment/CommentBox.kt | 47 ------ 2 files changed, 104 insertions(+), 77 deletions(-) delete mode 100644 core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentBox.kt 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..9207e356 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,31 +10,40 @@ 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.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 @@ -49,15 +58,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) { 28.dp.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 +84,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, - ) - } -} From 7cbd7bd18e8abe171a87b136014fbdde052c5463 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 7 Mar 2026 17:44:15 +0900 Subject: [PATCH 02/32] =?UTF-8?q?=E2=9C=A8=20Feat:=20`CommentTextField`=20?= =?UTF-8?q?`onHeightMeasured`=20=EC=BD=9C=EB=B0=B1=EC=9D=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=98=EC=97=AC=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=86=92=EC=9D=B4=20=EB=B3=80=ED=99=94=EB=A5=BC=20?= =?UTF-8?q?=EA=B0=90=EC=A7=80=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../designsystem/components/comment/CommentTextField.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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..0e7db11f 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 @@ -14,6 +14,7 @@ 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 @@ -22,6 +23,7 @@ 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 @@ -47,6 +49,7 @@ fun CommentTextField( uiModel: CommentUiModel, modifier: Modifier = Modifier, enabled: Boolean = true, + onHeightMeasured: (Float) -> Unit = {}, onCommitComment: (String) -> Unit = {}, onFocusChanged: (Boolean) -> Unit = {}, ) { @@ -98,9 +101,12 @@ fun CommentTextField( } Box( + contentAlignment = Alignment.Center, modifier = modifier - .noRippleClickable { + .onSizeChanged { size -> + onHeightMeasured(size.height.toFloat()) + }.noRippleClickable { focusRequester.requestFocus() }, ) { @@ -180,6 +186,7 @@ private fun CommentTextFieldPreview() { CommentTextField( uiModel = CommentUiModel(text, isFocused), onFocusChanged = { isFocused = it }, + onHeightMeasured = {}, ) } } From 08802c174d665a143613ec0275dfdbfd7a423628 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 7 Mar 2026 19:39:03 +0900 Subject: [PATCH 03/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`CameraC?= =?UTF-8?q?ontrolBar`=20=EB=82=B4=EB=B6=80=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capture/component/CameraControlBar.kt | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) 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..2e40102a 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 @@ -119,42 +118,35 @@ 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( + borderColor = CommonColor.White, + backgroundColor = GrayColor.C500, + text = stringResource(R.string.photolog_upload), + textStyle = AppTextStyle.T2, + textColor = CommonColor.White, modifier = Modifier - .align(Alignment.Center), - ) { - Spacer(modifier = Modifier.width(12.dp)) - - AppRoundButton( - borderColor = CommonColor.White, - backgroundColor = GrayColor.C500, - text = stringResource(R.string.photolog_upload), - textStyle = AppTextStyle.T2, - textColor = CommonColor.White, - modifier = - Modifier - .width(150.dp) - .height(74.dp) - .noRippleClickable(onClick = onClickUpload), - ) - } + .width(150.dp) + .height(74.dp) + .noRippleClickable(onClick = onClickUpload), + ) } } From 153a8f54ee31b9938da61ef2a6e6fac66f6575a5 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sat, 7 Mar 2026 21:06:31 +0900 Subject: [PATCH 04/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`AppRoun?= =?UTF-8?q?dButton`=20=ED=85=8C=EB=91=90=EB=A6=AC=20=EB=91=90=EA=BB=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(1.dp=20->=201.6.dp)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/button/AppRoundButton.kt | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) 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..96240b34 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 @@ -32,19 +32,15 @@ fun AppRoundButton( borderColor: Color = GrayColor.C500, hasBorder: Boolean = true, ) { - Box( - modifier = modifier, - ) { + val shape = RoundedCornerShape(999.dp) + Box(modifier = modifier) { if (hasBorder) { Box( modifier = Modifier .fillMaxSize() .offset(y = 4.dp) - .background( - color = borderColor, - shape = RoundedCornerShape(100), - ), + .background(color = borderColor, shape = shape), ) } @@ -52,19 +48,11 @@ fun AppRoundButton( modifier = Modifier .fillMaxSize() - .background( - color = backgroundColor, - shape = RoundedCornerShape(100), - ).then( - if (hasBorder) { - Modifier.border( - width = 1.dp, - color = borderColor, - shape = RoundedCornerShape(100), - ) - } else { - Modifier - }, + .background(color = backgroundColor, shape = shape) + .border( + color = borderColor, + shape = shape, + width = 1.6.dp, ), contentAlignment = Alignment.Center, ) { @@ -79,14 +67,14 @@ fun AppRoundButton( @Preview(showBackground = true) @Composable -fun AppRoundButtonPreview() { +private fun AppRoundButtonPreview() { TwixTheme { Column { AppRoundButton( modifier = Modifier - .width(330.dp) - .height(68.dp), + .width(150.dp) + .height(74.dp), text = "버튼임니다", textColor = GrayColor.C500, backgroundColor = CommonColor.White, From a203dd79dcc6a7ee8a225cb0a5eed49a3f395fb2 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 01:11:18 +0900 Subject: [PATCH 05/32] =?UTF-8?q?=E2=9C=A8=20Feat:=20`AppRoundButton`=20?= =?UTF-8?q?=EA=B3=B5=EC=9A=A9=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B3=A0=EB=8F=84=ED=99=94=20=EB=B0=8F=20UI=20=EC=9D=BC?= =?UTF-8?q?=EA=B4=80=EC=84=B1=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/button/AppRoundButton.kt | 136 ++++++++++++------ .../components/photolog/BackgroundCard.kt | 17 ++- .../twix/home/component/GoalVerifications.kt | 23 ++- .../capture/component/CameraControlBar.kt | 22 ++- .../photolog/editor/PhotologEditorRoute.kt | 35 ++++- .../photolog/editor/component/RetakeButton.kt | 42 ------ 6 files changed, 166 insertions(+), 109 deletions(-) delete mode 100644 feature/photolog/editor/src/main/java/com/twix/photolog/editor/component/RetakeButton.kt 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 96240b34..c8272243 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 @@ -15,6 +13,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.twix.designsystem.components.text.AppText import com.twix.designsystem.theme.CommonColor @@ -24,42 +23,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, ) { val shape = RoundedCornerShape(999.dp) + Box(modifier = modifier) { - if (hasBorder) { - Box( - modifier = - Modifier - .fillMaxSize() - .offset(y = 4.dp) - .background(color = borderColor, shape = shape), - ) - } + Box( + modifier = + Modifier + .fillMaxWidth() + .height(shadowHeight) + .offset(y = shadowOffset) + .background(color = contentBorderColor, shape = shape), + ) Box( modifier = Modifier - .fillMaxSize() - .background(color = backgroundColor, shape = shape) + .fillMaxWidth() + .height(contentHeight) + .background(color = contentColor, shape = shape) .border( - color = borderColor, + color = contentBorderColor, shape = shape, - width = 1.6.dp, + 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 = "버튼 이름", ) } } @@ -67,30 +89,50 @@ fun AppRoundButton( @Preview(showBackground = true) @Composable -private fun AppRoundButtonPreview() { +private fun PokeAppRoundButtonPreview() { TwixTheme { - Column { - AppRoundButton( - modifier = - Modifier - .width(150.dp) - .height(74.dp), - text = "버튼임니다", - textColor = GrayColor.C500, - backgroundColor = CommonColor.White, + 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 = "찌르기!", ) - 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, backgroundColor = 0xFF000000) +@Composable +private fun BlackAppRoundButtonPreview() { + TwixTheme { + 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)) } } } 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..f2fce995 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 @@ -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/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/component/CameraControlBar.kt b/feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/CameraControlBar.kt index 2e40102a..ca55e0e2 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 @@ -26,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 @@ -136,17 +137,24 @@ private fun ImageCapturedBar( Spacer(modifier = Modifier.width(12.dp)) AppRoundButton( - borderColor = CommonColor.White, - backgroundColor = GrayColor.C500, - text = stringResource(R.string.photolog_upload), - textStyle = AppTextStyle.T2, - textColor = CommonColor.White, modifier = Modifier .width(150.dp) - .height(74.dp) + .height(68.dp) .noRippleClickable(onClick = onClickUpload), - ) + 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 = stringResource(R.string.photolog_upload), + ) + } } } diff --git a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt index 8d7c48f1..061e2dcc 100644 --- a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt +++ b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt @@ -9,6 +9,7 @@ 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.layout.width import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf @@ -16,12 +17,14 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.boundsInParent import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.app.ActivityCompat @@ -30,16 +33,19 @@ import coil3.compose.AsyncImage import coil3.request.ImageRequest import coil3.request.crossfade import com.twix.designsystem.R +import com.twix.designsystem.components.button.AppRoundButton import com.twix.designsystem.components.comment.CommentAnchorFrame import com.twix.designsystem.components.photolog.PhotologCard +import com.twix.designsystem.components.text.AppText import com.twix.designsystem.components.toast.ToastManager import com.twix.designsystem.components.toast.model.ToastData 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.GrayColor import com.twix.designsystem.theme.TwixTheme +import com.twix.domain.model.enums.AppTextStyle import com.twix.photolog.editor.component.PhotologEditorTopBar -import com.twix.photolog.editor.component.RetakeButton import com.twix.photolog.editor.contract.PhotologEditorIntent import com.twix.photolog.editor.contract.PhotologEditorSideEffect import com.twix.photolog.editor.contract.PhotologEditorUiState @@ -157,7 +163,9 @@ fun PhotologEditorScreen( .background(color = CommonColor.White) .noRippleClickable { focusManager.clearFocus() }, ) { - Column { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { PhotologEditorTopBar( title = uiState.goalName, onBack = onBack, @@ -202,6 +210,29 @@ fun PhotologEditorScreen( } } +@Composable +private fun RetakeButton(onClickRetake: () -> Unit) { + AppRoundButton( + modifier = + Modifier + .width(330.dp) + .height(68.dp) + .noRippleClickable(onClick = onClickRetake), + 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/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 = {}) - } -} From f8f2e528ec083bbf88550cce52816bbdc49e2faa Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 01:32:17 +0900 Subject: [PATCH 06/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20"?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=83=B7=20=EC=B0=8D=EA=B8=B0"=EC=97=90?= =?UTF-8?q?=EC=84=9C=20"=EC=97=85=EB=A1=9C=EB=93=9C=ED=95=98=EA=B8=B0"?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EC=97=AC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EA=B0=80=EC=9D=B4=EB=93=9C=20=EB=AC=B8?= =?UTF-8?q?=EA=B5=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/design-system/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index abf6eb67..b7ff4471 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -133,7 +133,7 @@ 업로드 이미지 캡처에 실패했습니다. 다시 시도해 주세요. 이미지를 불러오는 데 실패했습니다. 다시 시도해 주세요. - 인증샷 찍기 + 업로드하기 인증샷 등록에 실패했습니다. 이미지 변환에 실패했습니다. 인증샷 업로드 중... From 47611dd46dfd70ce2bd3d2b78fcfb171fde28251 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 01:43:33 +0900 Subject: [PATCH 07/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`Comment?= =?UTF-8?q?TextField`=20=EB=B0=B0=EA=B2=BD=20=EA=B7=B8=EB=A6=AC=EA=B8=B0?= =?UTF-8?q?=20=EB=B0=A9=EC=8B=9D=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/comment/CommentCircle.kt | 61 ------ .../comment/CommentFieldBackground.kt | 175 ++++++++++++++++++ .../components/comment/CommentTextField.kt | 110 ++++++----- 3 files changed, 243 insertions(+), 103 deletions(-) delete mode 100644 core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentCircle.kt create mode 100644 core/design-system/src/main/java/com/twix/designsystem/components/comment/CommentFieldBackground.kt 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 0e7db11f..9cb32911 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 @@ -17,12 +20,9 @@ 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 @@ -34,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 @@ -134,49 +136,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() { From a24d38daf3085ea87af8158cc0a5ef7a3c597ee0 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 11:24:49 +0900 Subject: [PATCH 08/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=83=B7=20=ED=8E=B8=EC=A7=91=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=9E=AC=EC=B4=AC=EC=98=81=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EB=86=92=EC=9D=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/twix/photolog/editor/PhotologEditorRoute.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt index 061e2dcc..32b1aede 100644 --- a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt +++ b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt @@ -216,7 +216,7 @@ private fun RetakeButton(onClickRetake: () -> Unit) { modifier = Modifier .width(330.dp) - .height(68.dp) + .height(70.dp) .noRippleClickable(onClick = onClickRetake), contentColor = CommonColor.White, contentHeight = 68.dp, From 07b2fcb099097f816e9ff51999b32c8c59a2e686 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 11:26:06 +0900 Subject: [PATCH 09/32] =?UTF-8?q?=E2=9C=A8=20Feat:=20`AppRoundButton`=20?= =?UTF-8?q?=EB=A1=B1=20=EB=B2=84=ED=8A=BC=20=ED=94=84=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/button/AppRoundButton.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) 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 c8272243..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 @@ -12,9 +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 @@ -136,3 +138,28 @@ private fun BlackAppRoundButtonPreview() { } } } + +@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), + ) + } + } +} From 26e2cf3c5f40af720ac302ba09b587296307f94a Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 11:47:37 +0900 Subject: [PATCH 10/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`CameraC?= =?UTF-8?q?ontrolBar`=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=20=EC=98=81=EC=97=AD=20=EB=86=92=EC=9D=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/photolog/capture/component/CameraControlBar.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ca55e0e2..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 @@ -140,7 +140,7 @@ private fun ImageCapturedBar( modifier = Modifier .width(150.dp) - .height(68.dp) + .height(74.dp) .noRippleClickable(onClick = onClickUpload), contentColor = GrayColor.C500, contentHeight = 68.dp, From fbbecc0b82724b187093ee33685819d84f5c95eb Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 13:24:46 +0900 Subject: [PATCH 11/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=ED=8F=AC=ED=86=A0=EB=A1=9C=EA=B7=B8=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B4=80=EB=A0=A8=20=EB=AC=B8=EC=9E=90=EC=97=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/design-system/src/main/res/values/strings.xml | 3 ++- .../com/twix/photolog/detail/component/PhotologCardContent.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index b7ff4471..06aa81c1 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -133,7 +133,8 @@ 업로드 이미지 캡처에 실패했습니다. 다시 시도해 주세요. 이미지를 불러오는 데 실패했습니다. 다시 시도해 주세요. - 업로드하기 + 업로드하기 + 인증샷 찍기 인증샷 등록에 실패했습니다. 이미지 변환에 실패했습니다. 인증샷 업로드 중... 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, From a7f88b4e73b915eb9aecb7bf5cf3024e9eb74d05 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 14:25:44 +0900 Subject: [PATCH 12/32] =?UTF-8?q?=E2=9C=A8=20Feat:=20`CommonColor`?= =?UTF-8?q?=EC=97=90=20`Black`=20=EC=83=89=EC=83=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/twix/designsystem/theme/Colors.kt | 1 + 1 file changed, 1 insertion(+) 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 { From 8b1c40888c65a86a4bce6804dfdf680cafdc1269 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 14:26:44 +0900 Subject: [PATCH 13/32] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=B9=B4=EB=A9=94?= =?UTF-8?q?=EB=9D=BC=20=ED=94=8C=EB=9E=98=EC=8B=9C(Torch)=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20UI=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `TorchButton` 컴포넌트 신규 구현 및 `CameraPreviewBox` 내 적용 - 플래시 관련 이벤트 명칭 변경 (`onClickFlash` -> `onClickTorch`) - 카메라 플래시 On/Off 아이콘 리소스 업데이트 (24dp 규격으로 조정 및 스타일 수정) - `CameraPreviewBox` 내 `TorchIcon`을 신규 구현한 `TorchButton`으로 대체하여 디자인 통일성 강화 --- .../main/res/drawable/ic_camera_torch_off.xml | 18 ++--- .../main/res/drawable/ic_camera_torch_on.xml | 17 ++-- .../photolog/capture/PhotologCaptureScreen.kt | 8 +- .../capture/component/CameraPreviewBox.kt | 36 ++------- .../photolog/capture/component/TorchButton.kt | 78 +++++++++++++++++++ 5 files changed, 102 insertions(+), 55 deletions(-) create mode 100644 feature/photolog/capture/src/main/java/com/twix/photolog/capture/component/TorchButton.kt 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/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..0d11ea98 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 }, ) @@ -235,7 +235,7 @@ fun PhotologCaptureScreenPreview() { onClickClose = {}, onCaptureClick = {}, onToggleCameraClick = {}, - onClickFlash = {}, + onClickTorch = {}, onClickGallery = {}, onClickRefresh = {}, 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..b43a5e68 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,7 +1,6 @@ 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.fillMaxSize @@ -11,21 +10,17 @@ 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,7 +28,7 @@ fun CameraPreviewBox( capture: CaptureStatus, previewRequest: CameraPreview?, torch: TorchStatus, - onClickFlash: () -> Unit, + onClickTorch: () -> Unit, onPositioned: (Float) -> Unit, modifier: Modifier = Modifier, ) { @@ -53,7 +48,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 +83,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 +92,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/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 = {}, + ) + } + } +} From e4944f65ccdf9a00f6d6ac7def765ebd1ebea3df Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 15:01:58 +0900 Subject: [PATCH 14/32] =?UTF-8?q?=F0=9F=94=A5=20Chore:=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=B9=B4?= =?UTF-8?q?=EB=A9=94=EB=9D=BC=20=EA=B6=8C=ED=95=9C=20=EC=95=88=EB=82=B4=20?= =?UTF-8?q?=EB=AC=B8=EC=9E=90=EC=97=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/design-system/src/main/res/values/strings.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 06aa81c1..7b4e0a3e 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -152,8 +152,6 @@ 코멘트 수정에 실패했어요. 리액션 요청에 실패했어요. - 인증샷 촬영을 위해서 카메라 권한이 필요해요. - Google로 시작하기 카카오로 시작하기 From 1da365534b2ef7e2c6c69080b3fa07a6344abdab Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 15:11:02 +0900 Subject: [PATCH 15/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8A=B8=20=EA=B4=80=EB=A0=A8=20=EB=AC=B8?= =?UTF-8?q?=EC=9E=90=EC=97=B4=20=EB=A6=AC=EC=86=8C=EC=8A=A4=20=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EB=B0=8D=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20=EB=B0=8F=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/values/strings.xml | 24 +++++++++---------- .../capture/PhotologCaptureViewModel.kt | 10 ++++---- .../capture/component/CommentErrorText.kt | 2 +- .../detail/PhotologDetailViewModel.kt | 4 ++-- .../preview/PhotologDetailPreviewProvider.kt | 2 ++ .../editor/PhotologEditorViewModel.kt | 10 ++++---- 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/core/design-system/src/main/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index 7b4e0a3e..d797a0c7 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,26 +141,16 @@ 업로드 - 이미지 캡처에 실패했습니다. 다시 시도해 주세요. - 이미지를 불러오는 데 실패했습니다. 다시 시도해 주세요. 업로드하기 인증샷 찍기 - 인증샷 등록에 실패했습니다. - 이미지 변환에 실패했습니다. 인증샷 업로드 중... 잠시만 기다려 주세요. - 코멘트는 5글자로 입력해주세요! - - 인증샷 조회에 실패했습니다. - 인증샷 수정에 실패했어요. + + 다시 찍기 다시 찍기 - 코멘트가 수정 되었어요. - 코멘트가 수정되지 않았어요. - 코멘트 수정에 실패했어요. - 리액션 요청에 실패했어요. Google로 시작하기 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/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/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/preview/PhotologDetailPreviewProvider.kt b/feature/photolog/detail/src/main/java/com/twix/photolog/detail/preview/PhotologDetailPreviewProvider.kt index a4d06649..21d95a50 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 Date: Sun, 8 Mar 2026 15:22:54 +0900 Subject: [PATCH 16/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=82=B4=20=EC=82=AC=EC=A7=84=EC=9D=B4=20=EC=97=86=EC=9D=84=20?= =?UTF-8?q?=EB=95=8C=20=ED=85=8D=EC=8A=A4=ED=8A=B8=EA=B0=80=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=EC=83=B7=EC=9D=84=20=EC=98=AC=EB=A0=A4=EB=B3=B4?= =?UTF-8?q?=EC=84=B8=EC=9A=94!=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../twix/designsystem/components/photolog/ForegroundCard.kt | 6 ++++-- core/design-system/src/main/res/values/strings.xml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) 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/res/values/strings.xml b/core/design-system/src/main/res/values/strings.xml index d797a0c7..c77e0576 100644 --- a/core/design-system/src/main/res/values/strings.xml +++ b/core/design-system/src/main/res/values/strings.xml @@ -147,7 +147,7 @@ 잠시만 기다려 주세요. - 다시 찍기 + 인증샷을\n올려보세요! 다시 찍기 From b6e49a28a3d8843902e3e78eb6bb30ecd2de19ac Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 15:24:13 +0900 Subject: [PATCH 17/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=83=B7=20=EC=88=98=EC=A0=95=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=B0=B0=EA=B2=BD=EC=83=89=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/photolog/detail/component/PhotologDetailTopBar.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, ) { From 9740f5c5c4646e890ca935406aa6d8444a668f12 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 15:25:28 +0900 Subject: [PATCH 18/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=83=B7=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/designsystem/components/photolog/BackgroundCard.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f2fce995..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) From 4c2fa5c5bc1bb15639bbd27b44e27a1f53f282b9 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 15:28:13 +0900 Subject: [PATCH 19/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=9D=B8=EC=A6=9D=EC=83=B7=20=ED=94=84=EB=A0=88=EC=9E=84=20rad?= =?UTF-8?q?ius=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../twix/designsystem/components/photolog/PhotologCard.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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..6cc354ad 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, From c7952fec8b754029567774422e0b08623dfc42e4 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 16:12:10 +0900 Subject: [PATCH 20/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=A6=AC=EC=95=A1=EC=85=98=EB=B0=94=20shadow=20=ED=81=AC?= =?UTF-8?q?=EA=B8=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/component/reaction/ReactionBar.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) 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..f104bcd7 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 @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight 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 +32,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,7 +53,7 @@ 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), From c0273a4a763d9d16276b08babd304726559cee61 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 18:26:45 +0900 Subject: [PATCH 21/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20Reaction?= =?UTF-8?q?Bar`=20=EC=95=84=EC=9D=B4=ED=85=9C=20=EA=B0=84=EA=B2=A9=20?= =?UTF-8?q?=EB=B0=8F=20=ED=8C=A8=EB=94=A9=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/component/reaction/ReactionBar.kt | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) 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 f104bcd7..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,6 +6,7 @@ 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 @@ -59,8 +60,16 @@ fun ReactionBar( .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, From d9699ce7b4e86b47b4cc4e82db36caa1db88158b Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 18:57:14 +0900 Subject: [PATCH 22/32] =?UTF-8?q?=F0=9F=8E=A8=20Style:=20PhotologDetailPre?= =?UTF-8?q?viewProvider=20=EC=BD=94=EB=93=9C=20=ED=8F=AC=EB=A7=B7=ED=8C=85?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../photolog/detail/preview/PhotologDetailPreviewProvider.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 21d95a50..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,7 +29,7 @@ class PhotologDetailPreviewProvider : PreviewParameterProvider Date: Sun, 8 Mar 2026 19:30:54 +0900 Subject: [PATCH 23/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EC=9E=85=EB=A0=A5=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EA=B3=B5=EB=B0=B1=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `CommentUiModel`: 댓글 업로드 가능 여부 판단 시 `isEmpty` 대신 `isBlank`를 사용하여 공백만 있는 경우를 필터링하도록 수정 - `CommentTextField`: 입력 값에서 모든 공백 문자를 제거(filterNot)하고 글자 수 제한을 체크하도록 변경하여 비정상적인 입력 방지 --- .../designsystem/components/comment/CommentTextField.kt | 7 +++++-- .../components/comment/model/CommentUiModel.kt | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) 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 9cb32911..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 @@ -115,10 +115,13 @@ fun CommentTextField( 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), ) } }, 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) From 5a88f493d231cb0a206d4622b1a9451ad34f5460 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 19:51:41 +0900 Subject: [PATCH 24/32] =?UTF-8?q?=E2=9C=A8=20Feat:=20`CommentAnchorFrame`?= =?UTF-8?q?=20=ED=95=98=EB=8B=A8=20=ED=8C=A8=EB=94=A9=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../designsystem/components/comment/CommentAnchorFrame.kt | 4 +++- .../com/twix/photolog/capture/PhotologCaptureScreen.kt | 1 + .../java/com/twix/photolog/editor/PhotologEditorRoute.kt | 8 ++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) 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 9207e356..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 @@ -24,6 +24,7 @@ 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 @@ -50,6 +51,7 @@ import com.twix.ui.extension.noRippleClickable fun CommentAnchorFrame( uiModel: CommentUiModel, anchorBottom: Float, + paddingBottom: Dp, onCommentChanged: (String) -> Unit, onFocusChanged: (Boolean) -> Unit, modifier: Modifier = Modifier, @@ -62,7 +64,7 @@ fun CommentAnchorFrame( val imeBottom = WindowInsets.ime.getBottom(density) val navBottom = WindowInsets.navigationBars.getBottom(density) - val commentTextFieldPaddingBottom = with(density) { 28.dp.toPx() } + val commentTextFieldPaddingBottom = with(density) { paddingBottom.toPx() } val guideTextPaddingBottom = with(density) { 8.dp.toPx() } var commentTextFieldHeight by remember { mutableFloatStateOf(0f) } 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 0d11ea98..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 @@ -219,6 +219,7 @@ private fun PhotologCaptureScreen( CommentAnchorFrame( uiModel = uiState.comment, anchorBottom = previewBoxBottom, + paddingBottom = 28.dp, onCommentChanged = onCommentChanged, onFocusChanged = onFocusChanged, ) diff --git a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt index 32b1aede..18e797a2 100644 --- a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt +++ b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt @@ -8,8 +8,9 @@ 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.width +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf @@ -35,6 +36,7 @@ import coil3.request.crossfade import com.twix.designsystem.R import com.twix.designsystem.components.button.AppRoundButton import com.twix.designsystem.components.comment.CommentAnchorFrame +import com.twix.designsystem.components.comment.model.CommentUiModel import com.twix.designsystem.components.photolog.PhotologCard import com.twix.designsystem.components.text.AppText import com.twix.designsystem.components.toast.ToastManager @@ -79,6 +81,7 @@ fun PhotologEditorRoute( sideEffect.type, ), ) + is PhotologEditorSideEffect.ShowPokeToast -> { toastManager.tryShow( ToastData(sideEffect.message, ToastType.SUCCESS), @@ -172,7 +175,7 @@ fun PhotologEditorScreen( onClickSave = onClickSave, ) - Spacer(Modifier.height(103.dp)) + Spacer(Modifier.height(101.dp)) PhotologCard( modifier = @@ -204,6 +207,7 @@ fun PhotologEditorScreen( CommentAnchorFrame( uiModel = uiState.comment, anchorBottom = photologBottom, + paddingBottom = 24.dp, onCommentChanged = onCommentChanged, onFocusChanged = onFocusChanged, ) From 50f3f6c5fab29caa9716a9af4a986b03df15f640 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 19:52:17 +0900 Subject: [PATCH 25/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=EC=B0=8D=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EB=84=88=EB=B9=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/twix/photolog/editor/PhotologEditorRoute.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt index 18e797a2..022856b7 100644 --- a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt +++ b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt @@ -219,9 +219,10 @@ private fun RetakeButton(onClickRetake: () -> Unit) { AppRoundButton( modifier = Modifier - .width(330.dp) + .fillMaxWidth() .height(70.dp) - .noRippleClickable(onClick = onClickRetake), + .noRippleClickable(onClick = onClickRetake) + .padding(horizontal = 30.dp), contentColor = CommonColor.White, contentHeight = 68.dp, contentBorderColor = GrayColor.C500, From e7cde7c1415097737afea4c35bdd586f998bfedb Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 19:52:25 +0900 Subject: [PATCH 26/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`Photolo?= =?UTF-8?q?gCardPreview`=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C=EC=96=B4?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/designsystem/components/photolog/PhotologCard.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6cc354ad..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 @@ -50,7 +50,7 @@ fun PhotologCard( @Preview @Composable -fun PhotologCardPreview() { +private fun PhotologCardPreview() { TwixTheme { PhotologCard( rotation = 0f, From 995cf52c6de0600f230343ca1efa30a59d217bf1 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 19:53:38 +0900 Subject: [PATCH 27/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`Photolo?= =?UTF-8?q?gEditorRoute`=20=EB=82=B4=20`Spacer`=20=EB=86=92=EC=9D=B4?= =?UTF-8?q?=EB=A5=BC=20`101.dp`=EC=97=90=EC=84=9C=20`103.dp`=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/twix/photolog/editor/PhotologEditorRoute.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt index 022856b7..cb563f92 100644 --- a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt +++ b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt @@ -175,7 +175,7 @@ fun PhotologEditorScreen( onClickSave = onClickSave, ) - Spacer(Modifier.height(101.dp)) + Spacer(Modifier.height(103.dp)) PhotologCard( modifier = From 39511e88db99a2d7f27a76f43171935012d049e5 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Sun, 8 Mar 2026 19:57:14 +0900 Subject: [PATCH 28/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20`Co?= =?UTF-8?q?mmentUiModel`=20=EC=9E=84=ED=8F=AC=ED=8A=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/twix/photolog/editor/PhotologEditorRoute.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt index cb563f92..2a873885 100644 --- a/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt +++ b/feature/photolog/editor/src/main/java/com/twix/photolog/editor/PhotologEditorRoute.kt @@ -36,7 +36,6 @@ import coil3.request.crossfade import com.twix.designsystem.R import com.twix.designsystem.components.button.AppRoundButton import com.twix.designsystem.components.comment.CommentAnchorFrame -import com.twix.designsystem.components.comment.model.CommentUiModel import com.twix.designsystem.components.photolog.PhotologCard import com.twix.designsystem.components.text.AppText import com.twix.designsystem.components.toast.ToastManager From eb0f518578ddd928261e2ccc5715bdbdab2ba6d8 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Wed, 11 Mar 2026 16:46:51 +0900 Subject: [PATCH 29/32] =?UTF-8?q?=E2=9C=A8=20Refactor:=20`SwipeableCard`?= =?UTF-8?q?=20=EC=95=A0=EB=8B=88=EB=A9=94=EC=9D=B4=EC=85=98=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=84=EC=86=8C=ED=99=94=20=EB=B0=8F=20=EC=88=98?= =?UTF-8?q?=EC=B9=98=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `SwipeCardSpec`에서 사용되지 않는 `springDamping`, `springStiffness` 제거 및 변수명 정리 (`dismissThresholdDp` -> `dismissThreshold` 등) - 카드 스와이프 판정 임계값(Threshold)을 60.dp에서 150.dp로 상향 - 드래그 종료 시의 애니메이션 로직을 단순화하여 코드 가독성 개선 - `onDrag` 시 불필요한 `change.consume()` 호출 제거 --- .../detail/component/swipe/SwipeCardSpec.kt | 8 +-- .../detail/component/swipe/SwipeableCard.kt | 68 ++++--------------- 2 files changed, 14 insertions(+), 62 deletions(-) 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..c2d185f0 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,9 +8,9 @@ import androidx.compose.ui.unit.dp */ data class SwipeCardSpec( /** dismiss 판정 거리(dp) */ - val dismissThresholdDp: Dp = 60.dp, + val dismissThreshold: Dp = 150.dp, /** 화면 밖으로 날아가는 거리(px) */ - val dismissDistancePx: Float = 1000f, + val dismissDistance: Float = 1000f, /** dismiss 애니메이션 시간(ms) */ val dismissDuration: Int = 150, /** 회전 계산 비율 */ @@ -19,8 +19,4 @@ data class SwipeCardSpec( val maxRotation: Float = 8f, /** 복귀 시 위치 비율 */ 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..358611f0 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,8 +1,6 @@ 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.offset @@ -14,7 +12,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 @@ -41,6 +38,7 @@ fun SwipeableCard( ) { val coroutineScope = rememberCoroutineScope() val density = LocalDensity.current + val threshold = with(density) { spec.dismissThreshold.toPx() } /** * 카드 상태 값 @@ -79,9 +77,7 @@ fun SwipeableCard( * 드래그 중 * → 위치 즉시 반영 (snap) */ - onDrag = { change, dragAmount -> - change.consume() - + onDrag = { _, dragAmount -> coroutineScope.launch { offsetX.snapTo(offsetX.value + dragAmount.x) } @@ -90,34 +86,9 @@ fun SwipeableCard( * 드래그 종료 시 처리 */ onDragEnd = { - val thresholdPx = with(density) { spec.dismissThresholdDp.toPx() } - val shouldDismiss = abs(offsetX.value) > thresholdPx - - if (shouldDismiss) { - 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)) } - } - - /** - * 데이터 교체 - */ + val shouldDismiss = abs(offsetX.value) > threshold + coroutineScope.launch { + if (shouldDismiss) { onSwipe() /** @@ -125,32 +96,17 @@ fun SwipeableCard( */ val reappearStartX = if (isDisplayingMyPhoto) { - spec.dismissDistancePx * spec.reappearOffsetRatio + spec.dismissDistance * spec.reappearOffsetRatio } else { - -spec.dismissDistancePx * spec.reappearOffsetRatio + -spec.dismissDistance * spec.reappearOffsetRatio } offsetX.snapTo(reappearStartX) - /** - * 스프링 복귀 - */ - launch { - offsetX.animateTo( - 0f, - spring( - dampingRatio = spec.springDamping, - stiffness = spec.springStiffness, - ), - ) - } - launch { - opacity.animateTo(1f, spring(spec.springDamping)) - } - } - } else { - // threshold 미만 → 제자리 복귀 - coroutineScope.launch { - launch { offsetX.animateTo(0f, spring()) } + launch { offsetX.animateTo(0f) } + launch { opacity.animateTo(1f) } + } else { + // threshold 미만 → 제자리 복귀 + launch { offsetX.animateTo(0f) } } } }, From de3c01f1560172a04bd452870855d28fb570e990 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Wed, 11 Mar 2026 16:57:50 +0900 Subject: [PATCH 30/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`dismiss?= =?UTF-8?q?Distance`=EB=A5=BC=20=ED=99=94=EB=A9=B4=20=EB=84=88=EB=B9=84?= =?UTF-8?q?=EC=97=90=20=EB=B9=84=EB=A1=80=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=8F=99=EC=A0=81=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `dismissThreshold` 하향(150dp → 80dp) 및 `maxRotation` 상향(8f → 16f)을 통해 스와이프 감도 개선 - `SwipeCardSpec`에서 불필요해진 `dismissDistance` 필드 제거 --- .../detail/component/swipe/SwipeCardSpec.kt | 6 +- .../detail/component/swipe/SwipeableCard.kt | 125 +++++++++--------- 2 files changed, 66 insertions(+), 65 deletions(-) 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 c2d185f0..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,15 +8,13 @@ import androidx.compose.ui.unit.dp */ data class SwipeCardSpec( /** dismiss 판정 거리(dp) */ - val dismissThreshold: Dp = 150.dp, - /** 화면 밖으로 날아가는 거리(px) */ - val dismissDistance: 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, ) 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 358611f0..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 @@ -3,6 +3,7 @@ package com.twix.photolog.detail.component.swipe import androidx.compose.animation.core.Animatable 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 @@ -23,10 +24,8 @@ import kotlin.math.roundToInt * 1. 사용자가 카드를 드래그 * 2. threshold 이상 이동 시 → 카드 dismiss * 3. 화면 밖으로 날아간 뒤 onSwipe 호출 - * 4. 반대편에서 다시 등장 (spring 복귀) + * 4. 반대편에서 다시 등장 * - * ## 커스터마이징 - * 모든 애니메이션/거리 값은 [SwipeCardSpec] 으로 조절 가능 */ @Composable fun SwipeableCard( @@ -53,66 +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 = { _, dragAmount -> - coroutineScope.launch { - offsetX.snapTo(offsetX.value + dragAmount.x) - } - }, - /** - * 드래그 종료 시 처리 - */ - onDragEnd = { - val shouldDismiss = abs(offsetX.value) > threshold - coroutineScope.launch { - if (shouldDismiss) { - onSwipe() + BoxWithConstraints(modifier = modifier) { + val dismissDistance = constraints.maxWidth * 1.3f - /** - * 반대편 위치 세팅 - */ - val reappearStartX = - if (isDisplayingMyPhoto) { - spec.dismissDistance * spec.reappearOffsetRatio - } else { - -spec.dismissDistance * spec.reappearOffsetRatio - } - offsetX.snapTo(reappearStartX) + Box( + modifier = + modifier + /** + * 카드 위치 이동 + */ + .offset { + IntOffset( + offsetX.value.roundToInt(), + 0, + ) + } + /** + * 회전 + 투명도 적용 + */ + .graphicsLayer { + rotationZ = rotation + alpha = opacity.value + }.pointerInput(isDisplayingMyPhoto) { + detectDragGestures( + /** + * 드래그 중 + * → 위치 즉시 반영 (snap) + */ + onDrag = { _, dragAmount -> + coroutineScope.launch { + offsetX.snapTo(offsetX.value + dragAmount.x) + } + }, + /** + * 드래그 종료 시 처리 + */ + onDragEnd = { + val shouldDismiss = abs(offsetX.value) > threshold + coroutineScope.launch { + if (shouldDismiss) { + onSwipe() + + /** + * 반대편 위치 세팅 + */ + val reappearStartX = + if (isDisplayingMyPhoto) { + dismissDistance * spec.reappearOffsetRatio + } else { + -dismissDistance * spec.reappearOffsetRatio + } + offsetX.snapTo(reappearStartX) - launch { offsetX.animateTo(0f) } - launch { opacity.animateTo(1f) } - } else { - // threshold 미만 → 제자리 복귀 - launch { offsetX.animateTo(0f) } + launch { offsetX.animateTo(0f) } + launch { opacity.animateTo(1f) } + } else { + // threshold 미만 → 제자리 복귀 + launch { offsetX.animateTo(0f) } + } } - } - }, - ) - }, - ) { - content() + }, + ) + }, + ) { + content() + } } } From 78fc526ce02414a303567e15a0e8bfd024a3f967 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Wed, 11 Mar 2026 17:15:25 +0900 Subject: [PATCH 31/32] =?UTF-8?q?=E2=9C=A8=20Feat:=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=EC=83=B7=20=EC=83=81=EC=84=B8=20=ED=99=94=EB=A9=B4=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tologDetail.kt => PhotologDetailScreen.kt} | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) rename feature/photolog/detail/src/main/java/com/twix/photolog/detail/{PhotologDetail.kt => PhotologDetailScreen.kt} (89%) 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 + }, + ) + }, ) } } From 082ae06b8eca59d42db053f005c7c01f683eb816 Mon Sep 17 00:00:00 2001 From: chanho0908 Date: Wed, 11 Mar 2026 17:16:37 +0900 Subject: [PATCH 32/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor:=20`CameraP?= =?UTF-8?q?reviewBox`=20=EB=B0=98=EC=9D=91=ED=98=95=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/twix/photolog/capture/component/CameraPreviewBox.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 b43a5e68..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 @@ -3,9 +3,10 @@ package com.twix.photolog.capture.component import androidx.camera.compose.CameraXViewfinder 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 @@ -35,7 +36,8 @@ fun CameraPreviewBox( Box( modifier = modifier - .size(375.66.dp) + .fillMaxWidth() + .aspectRatio(1f) .padding(horizontal = 5.dp) .onGloballyPositioned { coordinates -> onPositioned(coordinates.boundsInParent().bottom)