Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f54c3bf
✨ Feat: :feature:goal-editor 생성
dogmania Jan 28, 2026
f75d865
🔥 Remove: 불필요한 파일 삭제
dogmania Jan 28, 2026
354c384
🍱 Chore: 리소스 추가
dogmania Jan 28, 2026
2d4e4b7
♻️ Refactor: namespace 변경
dogmania Jan 28, 2026
40df7b9
✨ Feat: GoalEditorModule Koin 컨테이너 등록
dogmania Jan 28, 2026
36d8457
✨ Feat: GoalEditorNavGraph 구현
dogmania Jan 28, 2026
cfb702f
✨ Feat: EmojiPicker 구현
dogmania Jan 28, 2026
38cf736
✨ Feat: GoalEditorTopBar 구현
dogmania Jan 28, 2026
bd53b77
✨ Feat: GoalEditorUiState 구현
dogmania Jan 28, 2026
5024ce6
✨ Feat: RepeatType 정의
dogmania Jan 28, 2026
9a6f83d
✨ Feat: 프로젝트 공통 스위치 구현
dogmania Jan 28, 2026
f4ec675
✨ Feat: 프로젝트 공통 UnderlineTextField 구현
dogmania Jan 28, 2026
5cec265
✨ Feat: 빈공간 클릭 시 포커스 해제하는 유틸 확장 메서드 구현
dogmania Jan 28, 2026
871786a
✨ Feat: GoalEditorIntent 정의
dogmania Jan 28, 2026
dc870ca
✨ Feat: Intent, 상태 처리 로직 구현
dogmania Jan 28, 2026
ceeb84d
✨ Feat: GoalEditorSideEffect 구현
dogmania Jan 28, 2026
a98a03e
✨ Feat: GoalEditorScreen 기본 UI 구조 구현
dogmania Jan 28, 2026
74a16aa
✨ Feat: HomeScreen -> GoalEditorScreen 네비게이션 구현
dogmania Jan 28, 2026
14c1a5f
♻️ Refactor: 컴포넌트 분리
dogmania Jan 29, 2026
f714475
♻️ Refactor: nullable 제거
dogmania Jan 29, 2026
b276334
🍱 Chore: 리소스 추가
dogmania Jan 29, 2026
723377b
♻️ Refactor: ColorFilter 추가
dogmania Jan 29, 2026
c20590a
♻️ Refactor: Intent 이름 수정
dogmania Jan 29, 2026
c20fc1c
♻️ Refactor: endDateEnabled 옵션 추가
dogmania Jan 29, 2026
c09fd47
✨ Feat: 바텀시트 렌더링 및 상태 업데이트 로직 구현
dogmania Jan 29, 2026
fa6f692
✨ Feat: GoalEditorSideEffect 구현
dogmania Jan 29, 2026
ece6582
🍱 Chore: 이미지 리소스 추가
dogmania Jan 31, 2026
049f96f
✨ Feat: CommonDialog 구현
dogmania Jan 31, 2026
32c0ba0
✨ Feat: GoalIconType 정의
dogmania Jan 31, 2026
16c5bb0
🍱 Chore: string 리소스 추가
dogmania Jan 31, 2026
f081bf7
✨ Feat: GoalIconType -> Res Id 변환 확장 메서드 구현
dogmania Jan 31, 2026
da82dbb
✨ Feat: 키보드 완료 클릭 시 포커스 해제
dogmania Jan 31, 2026
d501934
♻️ Refactor: selectedIconId -> selectedIcon으로 변
dogmania Jan 31, 2026
f054f5b
♻️ Refactor: selectedIconId -> selectedIcon으로 변경
dogmania Jan 31, 2026
fac91eb
♻️ Refactor: selectedIconId -> selectedIcon으로 변경
dogmania Jan 31, 2026
56159c5
✨ Feat: 목표 아이콘 선택 다이얼로그 렌더링 로직 구현
dogmania Jan 31, 2026
067dbb8
♻️ Refactor: conflict 해결
dogmania Feb 4, 2026
1aedb5c
♻️ Refactor: 호출 파라미터명 추가
dogmania Feb 4, 2026
cc0f653
♻️ Refactor: 높이 고정
dogmania Feb 4, 2026
2783f89
Merge branch 'develop' of github.com:YAPP-Github/Twix-Android into fe…
dogmania Feb 4, 2026
4a7cf9e
♻️ Refactor: BackHandler enabled 옵션 추가
dogmania Feb 4, 2026
18d5ee2
🐛 Fix: Conflict 해결
dogmania Feb 5, 2026
76f73b0
♻️ Refactor: 하드코딩 stringResource로 이동
dogmania Feb 5, 2026
3834bb6
♻️ Refactor: 뷰모델에서 ResId를 전달하고 UI에서 값을 가져오도록 수정
dogmania Feb 5, 2026
9cd1e81
✨ Feat: trailingIcon 추가
dogmania Feb 5, 2026
c9e510a
♻️ Refactor: Lint 적용
dogmania Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ dependencies {
implementation(projects.feature.login)
implementation(projects.feature.main)
implementation(projects.feature.taskCertification)
implementation(projects.feature.goalEditor)

// Firebase
implementation(platform(libs.google.firebase.bom))
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/com/yapp/twix/di/FeatureModules.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.yapp.twix.di

import com.twix.goal_editor.di.goalEditorModule
import com.twix.home.di.homeModule
import com.twix.login.di.loginModule
import com.twix.main.di.mainModule
Expand All @@ -12,4 +13,5 @@ val featureModules: List<Module> =
mainModule,
homeModule,
taskCertificationModule,
goalEditorModule,
)
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ fun CommonBottomSheet(

if (!rendering) return

BackHandler { onDismissRequest() }
BackHandler(enabled = internalVisible) { onDismissRequest() }

BoxWithConstraints(
modifier =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.twix.designsystem.components.common

import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.twix.designsystem.theme.CommonColor
import com.twix.designsystem.theme.GrayColor
import kotlin.math.roundToInt

@Composable
fun CommonSwitch(
modifier: Modifier = Modifier,
checked: Boolean,
onClick: (Boolean) -> Unit,
) {
val density = LocalDensity.current
val minBound = with(density) { 0.dp.toPx() }
val maxBound = with(density) { 18.dp.toPx() }
val state by animateFloatAsState(
targetValue = if (checked) maxBound else minBound,
animationSpec = tween(durationMillis = 500),
label = "common switch",
)

Box(
modifier =
modifier
.size(width = 48.dp, height = 30.dp)
.clip(RoundedCornerShape(999.dp))
.background(if (checked) GrayColor.C500 else CommonColor.White)
.border(1.dp, GrayColor.C500, RoundedCornerShape(999.dp))
.clickable(onClick = { onClick(!checked) }),
contentAlignment = Alignment.CenterStart,
) {
Box(
modifier =
Modifier
.offset { IntOffset(state.roundToInt(), 0) }
.padding(4.dp)
.size(22.dp)
.clip(CircleShape)
.background(if (checked) CommonColor.White else GrayColor.C500),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package com.twix.designsystem.components.dialog

import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
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.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface
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.button.AppButton
import com.twix.designsystem.components.text.AppText
import com.twix.designsystem.theme.CommonColor
import com.twix.designsystem.theme.DimmedColor
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

@Composable
fun CommonDialog(
modifier: Modifier = Modifier,
visible: Boolean,
confirmText: String,
dismissText: String? = null,
onDismissRequest: () -> Unit,
content: @Composable ColumnScope.() -> Unit,
onConfirm: () -> Unit,
onDismiss: (() -> Unit)? = null,
) {
BackHandler(enabled = visible) { onDismissRequest() }

Box(
modifier =
Modifier
.fillMaxSize()
.then(modifier),
contentAlignment = Alignment.Center,
) {
DialogScrim(visible = visible, onDismissRequest = onDismissRequest)

DialogContent(
modifier =
Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth(),
visible = visible,
confirmText = confirmText,
dismissText = dismissText,
content = content,
onConfirm = onConfirm,
onDismiss = onDismiss,
)
}
}

@Composable
private fun DialogScrim(
visible: Boolean,
onDismissRequest: () -> Unit,
) {
val fadeDuration = 160

AnimatedVisibility(
visible = visible,
enter = fadeIn(animationSpec = tween(fadeDuration)),
exit = fadeOut(animationSpec = tween(fadeDuration)),
modifier = Modifier.fillMaxSize(),
) {
Box(
modifier =
Modifier
.fillMaxSize()
.background(DimmedColor.D070)
.noRippleClickable(onClick = onDismissRequest),
)
}
}

@Composable
private fun DialogContent(
modifier: Modifier,
visible: Boolean,
confirmText: String,
dismissText: String? = null,
content: @Composable ColumnScope.() -> Unit,
onConfirm: () -> Unit,
onDismiss: (() -> Unit)? = null,
) {
val fadeDuration = 160

AnimatedVisibility(
visible = visible,
enter = fadeIn(animationSpec = tween(fadeDuration)),
exit = fadeOut(animationSpec = tween(fadeDuration)),
) {
Surface(
shape = RoundedCornerShape(20.dp),
color = CommonColor.White,
modifier = modifier,
) {
Column(
modifier =
Modifier
.padding(horizontal = 20.dp)
.padding(top = 24.dp, bottom = 20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
content()

Spacer(Modifier.height(24.dp))

Row(
modifier =
Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
) {
if (dismissText != null && onDismiss != null) {
AppButton(
modifier = Modifier.weight(1f),
text = dismissText,
textColor = GrayColor.C500,
backgroundColor = CommonColor.White,
border = BorderStroke(1.dp, GrayColor.C500),
onClick = onDismiss,
)

Spacer(Modifier.width(8.dp))
}

AppButton(
modifier = Modifier.weight(1f),
text = confirmText,
onClick = onConfirm,
)
}
}
}
}
}

@Preview(showBackground = true, showSystemUi = true)
@Composable
private fun Preview() {
TwixTheme {
CommonDialog(
visible = true,
confirmText = "확인",
dismissText = "취소",
onDismissRequest = {},
onConfirm = {},
onDismiss = {},
content = {
AppText(
text = "타이틀 텍스트",
style = AppTextStyle.T1,
color = GrayColor.C500,
)

Spacer(Modifier.height(8.dp))

AppText(
text = "내용 텍스트",
style = AppTextStyle.B2,
color = GrayColor.C400,
)
},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.twix.designsystem.components.text_field

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
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.twix.designsystem.components.text.AppText
import com.twix.designsystem.theme.GrayColor
import com.twix.designsystem.theme.LocalAppTypography
import com.twix.designsystem.theme.toTextStyle
import com.twix.domain.model.enums.AppTextStyle

@Composable
fun UnderlineTextField(
Comment on lines +25 to +26
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

온보딩 닉네임 입력/날짜 선택 화면에서 trailing Icon이 필요한 경우가 있어서 추가해줄 수 있을까 ?

Copy link
Member Author

@dogmania dogmania Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일단 피그마 보고 대충 추가해봤는데 trailing 파라미터에 Image 컴포저블 넘겨서 디테일한 padding이나 클릭 이벤트 처리하면 될 것 같아요!

modifier: Modifier = Modifier,
value: String,
placeHolder: String = "",
textStyle: AppTextStyle = AppTextStyle.T2,
enabled: Boolean = true,
readOnly: Boolean = false,
singleLine: Boolean = true,
showTrailing: Boolean = false,
maxLines: Int = 1,
keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
keyboardActions: KeyboardActions = KeyboardActions.Default,
trailing: (@Composable () -> Unit)? = null,
onValueChange: (String) -> Unit,
) {
val typo = LocalAppTypography.current

Column(
modifier =
Modifier
.height(52.dp),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.Center,
) {
Spacer(Modifier.height(4.dp))

Box(
modifier =
modifier
.padding(horizontal = 8.dp, vertical = 10.dp),
contentAlignment = Alignment.CenterStart,
) {
val shouldShowTrailing = trailing != null && showTrailing && value.isNotBlank()

if (value.isBlank()) {
AppText(
text = placeHolder,
style = textStyle,
color = GrayColor.C200,
modifier =
Modifier
.align(Alignment.CenterStart),
)
}

Row(
modifier =
Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
BasicTextField(
value = value,
textStyle = textStyle.toTextStyle(typo).copy(color = GrayColor.C500),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
singleLine = singleLine,
maxLines = maxLines,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
)
Comment on lines +71 to +87
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

빈 값에서 입력 영역이 너무 좁아 탭이 어려울 수 있어요.
왜 문제가 되는지: BasicTextField에 크기 modifier가 없어 텍스트 길이만큼만 측정됩니다. 값이 비어 있으면 폭이 거의 0에 가까워져 포커스가 잘 안 잡힐 수 있습니다(접근성/UX 저하).
어떻게 개선할지: Modifier.weight(1f) 또는 fillMaxWidth()를 적용해 입력 영역과 탭 영역을 확보하세요.

🛠️ 수정 제안
 BasicTextField(
+    modifier = Modifier.weight(1f),
     value = value,
     textStyle = textStyle.toTextStyle(typo).copy(color = GrayColor.C500),
     onValueChange = onValueChange,
     enabled = enabled,
     readOnly = readOnly,
     singleLine = singleLine,
     maxLines = maxLines,
     keyboardOptions = keyboardOptions,
     keyboardActions = keyboardActions,
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Row(
modifier =
Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
BasicTextField(
value = value,
textStyle = textStyle.toTextStyle(typo).copy(color = GrayColor.C500),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
singleLine = singleLine,
maxLines = maxLines,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
)
Row(
modifier =
Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
BasicTextField(
modifier = Modifier.weight(1f),
value = value,
textStyle = textStyle.toTextStyle(typo).copy(color = GrayColor.C500),
onValueChange = onValueChange,
enabled = enabled,
readOnly = readOnly,
singleLine = singleLine,
maxLines = maxLines,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
)
🤖 Prompt for AI Agents
In
`@core/design-system/src/main/java/com/twix/designsystem/components/text_field/UnderlineTextField.kt`
around lines 71 - 87, The BasicTextField in UnderlineTextField is missing a size
modifier so when value is empty its measured width collapses and becomes hard to
tap; update the BasicTextField invocation (in UnderlineTextField) to include a
modifier such as Modifier.weight(1f) or Modifier.fillMaxWidth() (combined with
any existing modifiers) so the input area fills available horizontal space and
remains easily focusable/tappable.


if (shouldShowTrailing) {
Box(
modifier =
Modifier
.padding(start = 10.dp),
contentAlignment = Alignment.Center,
) {
trailing.invoke()
}
}
}
}

Spacer(Modifier.height(4.dp))

HorizontalDivider(thickness = 1.dp, color = GrayColor.C500, modifier = modifier)
}
}
Loading