Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import com.threegap.bitnagil.presentation.screen.routinewrite.component.template
import com.threegap.bitnagil.presentation.screen.routinewrite.contract.RoutineWriteSideEffect
import com.threegap.bitnagil.presentation.screen.routinewrite.contract.RoutineWriteState
import com.threegap.bitnagil.presentation.screen.routinewrite.model.Day
import com.threegap.bitnagil.presentation.screen.routinewrite.model.ExpandableSection
import com.threegap.bitnagil.presentation.screen.routinewrite.model.RepeatType
import com.threegap.bitnagil.presentation.screen.routinewrite.model.RoutineWriteType
import com.threegap.bitnagil.presentation.screen.routinewrite.model.Time
Expand Down Expand Up @@ -184,7 +185,7 @@ private fun RoutineWriteScreen(
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
ExpandableContent(
expand = state.subRoutineUiExpanded,
expand = state.expandedSection == ExpandableSection.SUB_ROUTINE,
required = false,
iconResourceId = R.drawable.img_subroutines,
title = "세부루틴",
Expand Down Expand Up @@ -230,7 +231,7 @@ private fun RoutineWriteScreen(
}

ExpandableContent(
expand = state.repeatDaysUiExpanded,
expand = state.expandedSection == ExpandableSection.REPEAT_DAYS,
required = true,
iconResourceId = R.drawable.img_repeat_days,
title = "반복 요일",
Expand Down Expand Up @@ -286,7 +287,7 @@ private fun RoutineWriteScreen(
}

ExpandableContent(
expand = state.periodUiExpanded,
expand = state.expandedSection == ExpandableSection.PERIOD,
required = true,
iconResourceId = R.drawable.img_routine_period,
title = "목표 기간",
Expand Down Expand Up @@ -315,7 +316,7 @@ private fun RoutineWriteScreen(
}

ExpandableContent(
expand = state.startTimeUiExpanded,
expand = state.expandedSection == ExpandableSection.START_TIME,
required = true,
iconResourceId = R.drawable.img_start_time,
title = "시간",
Expand Down Expand Up @@ -371,7 +372,7 @@ private fun getSubRoutinePlaceHolder(index: Int): String {
fun RoutineWriteScreenPreview() {
BitnagilTheme {
RoutineWriteScreen(
state = RoutineWriteState.INIT.copy(periodUiExpanded = true, startTimeUiExpanded = true),
state = RoutineWriteState.INIT.copy(expandedSection = ExpandableSection.PERIOD),
setRoutineName = {},
setSubRoutineName = { _, _ -> },
selectRepeatTime = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.threegap.bitnagil.presentation.screen.routinewrite.contract.RoutineWr
import com.threegap.bitnagil.presentation.screen.routinewrite.contract.RoutineWriteState
import com.threegap.bitnagil.presentation.screen.routinewrite.model.Date
import com.threegap.bitnagil.presentation.screen.routinewrite.model.Day
import com.threegap.bitnagil.presentation.screen.routinewrite.model.ExpandableSection
import com.threegap.bitnagil.presentation.screen.routinewrite.model.RepeatType
import com.threegap.bitnagil.presentation.screen.routinewrite.model.RoutineWriteType
import com.threegap.bitnagil.presentation.screen.routinewrite.model.SelectableDay
Expand Down Expand Up @@ -299,37 +300,49 @@ class RoutineWriteViewModel @AssistedInject constructor(
}

fun toggleSubRoutineUiExpanded() = intent {
val currentSubRoutineUiExpanded = state.subRoutineUiExpanded
reduce {
state.copy(
subRoutineUiExpanded = !currentSubRoutineUiExpanded,
expandedSection = if (state.expandedSection == ExpandableSection.SUB_ROUTINE) {
ExpandableSection.NONE
} else {
ExpandableSection.SUB_ROUTINE
},
)
}
}

fun toggleRepeatDaysUiExpanded() = intent {
val currentRepeatDaysUiExpanded = state.repeatDaysUiExpanded
reduce {
state.copy(
repeatDaysUiExpanded = !currentRepeatDaysUiExpanded,
expandedSection = if (state.expandedSection == ExpandableSection.REPEAT_DAYS) {
ExpandableSection.NONE
} else {
ExpandableSection.REPEAT_DAYS
},
)
}
}

fun togglePeriodUiExpanded() = intent {
val currentPeriodUiExpanded = state.periodUiExpanded
reduce {
state.copy(
periodUiExpanded = !currentPeriodUiExpanded,
expandedSection = if (state.expandedSection == ExpandableSection.PERIOD) {
ExpandableSection.NONE
} else {
ExpandableSection.PERIOD
},
)
}
}

fun toggleStartTimeUiExpanded() = intent {
val currentStartTimeUiExpanded = state.startTimeUiExpanded
reduce {
state.copy(
startTimeUiExpanded = !currentStartTimeUiExpanded,
expandedSection = if (state.expandedSection == ExpandableSection.START_TIME) {
ExpandableSection.NONE
} else {
ExpandableSection.START_TIME
},
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
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.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.threegap.bitnagil.designsystem.BitnagilTheme
Expand All @@ -29,13 +33,17 @@ fun NameField(
modifier: Modifier = Modifier,
placeholder: String = "",
) {
val focusManager = LocalFocusManager.current

BasicTextField(
value = value,
onValueChange = onValueChange,
modifier = modifier
.fillMaxWidth(),
singleLine = true,
textStyle = BitnagilTheme.typography.title3SemiBold,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
decorationBox = { innerTextField ->
Column {
Row(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fun ExpandableContent(
}
if (!expand) {
Text(
text = if (showValueText) valueText ?: placeHolder else placeHolder,
text = if (showValueText) valueText else placeHolder,
style = if (showValueText) mainTextStyle else subTextStyle,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
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.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.threegap.bitnagil.designsystem.BitnagilTheme
Expand All @@ -28,6 +32,8 @@ fun SubRoutineField(
onValueChange: (String) -> Unit,
enabled: Boolean,
) {
val focusManager = LocalFocusManager.current

Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
Expand All @@ -47,6 +53,8 @@ fun SubRoutineField(
singleLine = true,
enabled = enabled,
textStyle = BitnagilTheme.typography.body2Medium.copy(color = if (enabled) BitnagilTheme.colors.coolGray30 else BitnagilTheme.colors.coolGray90),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
decorationBox = { innerTextField ->
Column {
Box {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,19 +98,19 @@ private fun DatePickerBottomSheetContent(
val lastDaysOfPrevMonth = remember(currentYear, currentMonth) {
CalendarUtils.lastDaysOfPrevMonth(currentYear, currentMonth)
}
val firstDaysOfNextMonth = remember(currentYear, currentMonth) {
CalendarUtils.firstDaysOfNextMonth(currentYear, currentMonth)
}
val currentDaysOfMonth = remember(currentYear, currentMonth) {
CalendarUtils.getDayAmountOfMonth(currentYear, currentMonth)
}

val prevMonthButtonEnabled by remember(availableStartDate) {
val totalCells = 42
val firstDaysOfNextMonthCount = totalCells - lastDaysOfPrevMonth.size - currentDaysOfMonth

val prevMonthButtonEnabled by remember(availableStartDate, currentYear, currentMonth) {
derivedStateOf {
(availableStartDate == null) || (availableStartDate.year < currentYear) || (availableStartDate.month < currentMonth)
}
}
val nextMonthButtonEnabled by remember(availableEndDate) {
val nextMonthButtonEnabled by remember(availableEndDate, currentYear, currentMonth) {
derivedStateOf {
(availableEndDate == null) || (availableEndDate.year > currentYear) || (availableEndDate.month > currentMonth)
}
Comment on lines +108 to 116
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

연/월 비교를 분리하면 경계 연도에서 화살표 상태가 틀어질 수 있습니다.

현재 식은 yearmonth를 독립적으로 비교해서, 시작/종료 가능 월과 현재 표시 월이 서로 다른 연도에 있을 때 잘못 true가 될 수 있습니다. 예를 들어 시작 가능 월이 2027-01이고 현재가 2026-12면 이전 버튼이 켜집니다. 연/월을 한 쌍으로 비교해 주세요.

🔧 예시 수정
     val prevMonthButtonEnabled by remember(availableStartDate, currentYear, currentMonth) {
         derivedStateOf {
-            (availableStartDate == null) || (availableStartDate.year < currentYear) || (availableStartDate.month < currentMonth)
+            availableStartDate == null ||
+                availableStartDate.year < currentYear ||
+                (availableStartDate.year == currentYear && availableStartDate.month < currentMonth)
         }
     }
     val nextMonthButtonEnabled by remember(availableEndDate, currentYear, currentMonth) {
         derivedStateOf {
-            (availableEndDate == null) || (availableEndDate.year > currentYear) || (availableEndDate.month > currentMonth)
+            availableEndDate == null ||
+                availableEndDate.year > currentYear ||
+                (availableEndDate.year == currentYear && availableEndDate.month > currentMonth)
         }
     }
📝 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
val prevMonthButtonEnabled by remember(availableStartDate, currentYear, currentMonth) {
derivedStateOf {
(availableStartDate == null) || (availableStartDate.year < currentYear) || (availableStartDate.month < currentMonth)
}
}
val nextMonthButtonEnabled by remember(availableEndDate) {
val nextMonthButtonEnabled by remember(availableEndDate, currentYear, currentMonth) {
derivedStateOf {
(availableEndDate == null) || (availableEndDate.year > currentYear) || (availableEndDate.month > currentMonth)
}
val prevMonthButtonEnabled by remember(availableStartDate, currentYear, currentMonth) {
derivedStateOf {
availableStartDate == null ||
availableStartDate.year < currentYear ||
(availableStartDate.year == currentYear && availableStartDate.month < currentMonth)
}
}
val nextMonthButtonEnabled by remember(availableEndDate, currentYear, currentMonth) {
derivedStateOf {
availableEndDate == null ||
availableEndDate.year > currentYear ||
(availableEndDate.year == currentYear && availableEndDate.month > currentMonth)
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@presentation/src/main/java/com/threegap/bitnagil/presentation/screen/routinewrite/component/template/datepickerbottomsheet/DatePickerBottomSheet.kt`
around lines 108 - 116, The prev/next arrow enabled checks incorrectly compare
year and month separately (in prevMonthButtonEnabled and
nextMonthButtonEnabled), which misfires across year boundaries; change each
derivedStateOf to compare combined year+month pairs (e.g., construct a
YearMonth-like pair or compute a single ordinal like year * 12 + month) using
availableStartDate/currentYear/currentMonth and
availableEndDate/currentYear/currentMonth so the whole (year,month) is compared
atomically rather than comparing year and month independently.

Expand Down Expand Up @@ -186,13 +186,31 @@ private fun DatePickerBottomSheetContent(
}

itemsIndexed(lastDaysOfPrevMonth) { _, day ->
val prevMonth = if (currentMonth == 1) 12 else currentMonth - 1
val prevYear = if (currentMonth == 1) currentYear - 1 else currentYear
val prevDate = Date(prevYear, prevMonth, day)
val available = prevDate.checkInRange(availableStartDate, availableEndDate)

Box(
modifier = Modifier.aspectRatio(1f),
modifier = Modifier
.aspectRatio(1f)
.clickableWithoutRipple {
if (!available) return@clickableWithoutRipple
if (currentMonth == 1) {
currentMonth = 12
currentYear -= 1
} else {
currentMonth -= 1
}
selectedDate = prevDate
},
contentAlignment = Alignment.Center,
) {
Text(
"$day",
style = BitnagilTheme.typography.subtitle1Regular.copy(color = BitnagilTheme.colors.coolGray80),
style = BitnagilTheme.typography.subtitle1Regular.copy(
color = if (available) BitnagilTheme.colors.coolGray80 else BitnagilTheme.colors.coolGray95,
),
)
}
}
Expand All @@ -202,13 +220,16 @@ private fun DatePickerBottomSheetContent(
val currentDate = Date(year = currentYear, month = currentMonth, day = index + 1)
val available = currentDate.checkInRange(startDate = availableStartDate, endDate = availableEndDate)
Box(
modifier = Modifier.aspectRatio(1f).background(
color = if (selected) { BitnagilTheme.colors.orange50 } else { Color.Transparent },
shape = if (selected) { RoundedCornerShape(12.dp) } else { RectangleShape },
).clickableWithoutRipple {
if (!available) return@clickableWithoutRipple
selectedDate = currentDate
},
modifier = Modifier
.aspectRatio(1f)
.background(
color = if (selected) BitnagilTheme.colors.orange50 else Color.Transparent,
shape = if (selected) RoundedCornerShape(12.dp) else RectangleShape,
)
.clickableWithoutRipple {
if (!available) return@clickableWithoutRipple
selectedDate = currentDate
},
contentAlignment = Alignment.Center,
) {
Text(
Expand All @@ -224,14 +245,33 @@ private fun DatePickerBottomSheetContent(
}
}

itemsIndexed(firstDaysOfNextMonth) { _, day ->
items(firstDaysOfNextMonthCount) { index ->
val day = index + 1
val nextMonth = if (currentMonth == 12) 1 else currentMonth + 1
val nextYear = if (currentMonth == 12) currentYear + 1 else currentYear
val nextDate = Date(nextYear, nextMonth, day)
val available = nextDate.checkInRange(availableStartDate, availableEndDate)

Box(
modifier = Modifier.aspectRatio(1f),
modifier = Modifier
.aspectRatio(1f)
.clickableWithoutRipple {
if (!available) return@clickableWithoutRipple
if (currentMonth == 12) {
currentMonth = 1
currentYear += 1
} else {
currentMonth += 1
}
selectedDate = nextDate
},
contentAlignment = Alignment.Center,
) {
Text(
"$day",
style = BitnagilTheme.typography.subtitle1Regular.copy(color = BitnagilTheme.colors.coolGray80),
style = BitnagilTheme.typography.subtitle1Regular.copy(
color = if (available) BitnagilTheme.colors.coolGray80 else BitnagilTheme.colors.coolGray95,
),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.os.Parcelable
import com.threegap.bitnagil.domain.recommendroutine.model.RecommendCategory
import com.threegap.bitnagil.presentation.screen.routinewrite.model.Date
import com.threegap.bitnagil.presentation.screen.routinewrite.model.Day
import com.threegap.bitnagil.presentation.screen.routinewrite.model.ExpandableSection
import com.threegap.bitnagil.presentation.screen.routinewrite.model.RepeatType
import com.threegap.bitnagil.presentation.screen.routinewrite.model.RoutineWriteType
import com.threegap.bitnagil.presentation.screen.routinewrite.model.SelectableDay
Expand All @@ -26,10 +27,7 @@ data class RoutineWriteState(
val showStartDatePickerBottomSheet: Boolean,
val showEndDatePickerBottomSheet: Boolean,
val routineWriteType: RoutineWriteType,
val subRoutineUiExpanded: Boolean,
val repeatDaysUiExpanded: Boolean,
val periodUiExpanded: Boolean,
val startTimeUiExpanded: Boolean,
val expandedSection: ExpandableSection,
val recommendedRoutineType: RecommendCategory?,
) : Parcelable {
companion object {
Expand Down Expand Up @@ -77,10 +75,7 @@ data class RoutineWriteState(
showEndDatePickerBottomSheet = false,
showTimePickerBottomSheet = false,
routineWriteType = RoutineWriteType.Add,
subRoutineUiExpanded = false,
repeatDaysUiExpanded = false,
periodUiExpanded = false,
startTimeUiExpanded = false,
expandedSection = ExpandableSection.NONE,
startDate = Date.now(),
endDate = Date.now(),
recommendedRoutineType = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.threegap.bitnagil.presentation.screen.routinewrite.model

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
enum class ExpandableSection : Parcelable {
SUB_ROUTINE, REPEAT_DAYS, PERIOD, START_TIME, NONE
}
Loading