Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UI/YAF-74] 알람 설정 시 알람 미루기를 설정할 수 있다 #46

Merged
merged 10 commits into from
Jan 19, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ sealed class AlarmAddEditContract {
val currentAmPm: String = "오전",
val currentHour: Int = 6,
val currentMinute: Int = 0,
val alarmMessage: String = "",
val days: Set<AlarmDay> = enumValues<AlarmDay>().toSet(),
val isWeekdaysChecked: Boolean = false,
val isWeekendsChecked: Boolean = false,
Expand Down
102 changes: 95 additions & 7 deletions feature/home/src/main/java/com/yapp/alarm/AlarmAddEditViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,31 +1,76 @@
package com.yapp.alarm

import androidx.lifecycle.viewModelScope
import com.yapp.ui.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class AlarmAddEditViewModel @Inject constructor() : BaseViewModel<AlarmAddEditContract.State, AlarmAddEditContract.SideEffect>(
initialState = AlarmAddEditContract.State(),
) {
private var debounceJob: Job? = null // 디바운싱을 위한 Job
Copy link
Member

Choose a reason for hiding this comment

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

p4
Job을 사용하신 이유가 궁금합니다이
mutableStateFlow로 하면 아마 스트림 연산으로 dobounce를 처리할 수 있을거 같아서

Copy link
Member Author

Choose a reason for hiding this comment

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

요거 디바운싱 처리했다가 알라미에서는 디바운싱 안했길래 제거했어요


fun processAction(action: AlarmAddEditContract.Action) {
when (action) {
is AlarmAddEditContract.Action.UpdateAlarmTime -> updateAlarmTime(action.amPm, action.hour, action.minute)
is AlarmAddEditContract.Action.ToggleWeekdaysChecked -> toggleWeekdaysChecked()
is AlarmAddEditContract.Action.ToggleWeekendsChecked -> toggleWeekendsChecked()
is AlarmAddEditContract.Action.ToggleDaySelection -> toggleDaySelection(action.day)
is AlarmAddEditContract.Action.ToggleDisableHolidayChecked -> toggleDisableHolidayChecked()
viewModelScope.launch {
Copy link
Member

Choose a reason for hiding this comment

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

p5
로컬 디비에 담아야되서 viewmodelscope.launch 넣으신 거죠??

Copy link
Member Author

Choose a reason for hiding this comment

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

의도한 것은 아닌데 Copilot 쓰다가 저렇게 됐네요....
나중에 DB 연동 작업 시에 알맞게 수정할게요

when (action) {
is AlarmAddEditContract.Action.UpdateAlarmTime -> updateAlarmTime(action.amPm, action.hour, action.minute)
is AlarmAddEditContract.Action.ToggleWeekdaysChecked -> toggleWeekdaysChecked()
is AlarmAddEditContract.Action.ToggleWeekendsChecked -> toggleWeekendsChecked()
is AlarmAddEditContract.Action.ToggleDaySelection -> toggleDaySelection(action.day)
is AlarmAddEditContract.Action.ToggleDisableHolidayChecked -> toggleDisableHolidayChecked()
}
}
}

private fun updateAlarmTime(amPm: String, hour: Int, minute: Int) {
private suspend fun updateAlarmTime(amPm: String, hour: Int, minute: Int) {
updateState {
copy(
currentAmPm = amPm,
currentHour = hour,
currentMinute = minute,
)
}
debounceUpdateAlarmMessage()
}

private fun debounceUpdateAlarmMessage() {
// 이전 Job 취소
debounceJob?.cancel()
debounceJob = viewModelScope.launch {
delay(200) // 200ms 대기
updateAlarmMessage() // 알람 메시지 업데이트
}
}

private fun updateAlarmMessage() {
Copy link
Member

Choose a reason for hiding this comment

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

p3
시간계산 쪽은 뭔가 domain layer에서 처리해야될 것 같은 아이들이 있는거 같아요.
아니면 유틸함수로 빼도 될듯?

Copy link
Member Author

Choose a reason for hiding this comment

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

시간 계산이 위에 n시간 n분 남았습니다 를 말씀하시는건가요?

Copy link
Member

Choose a reason for hiding this comment

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

분리 하자면

        // 남은 시간 계산
        val duration = java.time.Duration.between(now, nextAlarmDateTime)
        val totalMinutes = duration.toMinutes()
        val days = totalMinutes / (24 * 60)
        val hours = (totalMinutes % (24 * 60)) / 60
        val minutes = totalMinutes % 60

        // 출력 문구 생성
        val newMessage = when {
            days > 0 -> "${days}${hours}시간 후에 울려요"
            hours > 0 -> "${hours}시간 ${minutes}분 후에 울려요"
            else -> "${minutes}분 후에 울려요"
        }

this area.
사용하는지는 잘 모르지만 유틸이나 도메인으로 분리하면 다른 뷰에서도 참조 가능하고 우리가 나중에 테스트 코드를 짜게 된다면..? 더 이점이 있을듯 ㅋㅋㅋ

Copy link
Member Author

Choose a reason for hiding this comment

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

이부분은 재사용하는 로직이 아니라서 따로 domain으로 안 빼둘게요

val now = java.time.LocalDateTime.now()

// 설정된 알람 시간 계산
val alarmHour = convertTo24HourFormat(currentState.currentAmPm, currentState.currentHour)
val alarmTimeToday = now.toLocalDate().atTime(alarmHour, currentState.currentMinute)

// 선택된 요일 중 다음 알람 시간이 언제인지 계산
val nextAlarmDateTime = calculateNextAlarmDateTime(now, alarmTimeToday, currentState.selectedDays)

// 남은 시간 계산
val duration = java.time.Duration.between(now, nextAlarmDateTime)
val totalMinutes = duration.toMinutes()
val days = totalMinutes / (24 * 60)
val hours = (totalMinutes % (24 * 60)) / 60
val minutes = totalMinutes % 60

// 출력 문구 생성
val newMessage = when {
days > 0 -> "${days}일 ${hours}시간 후에 울려요"
hours > 0 -> "${hours}시간 ${minutes}분 후에 울려요"
else -> "${minutes}분 후에 울려요"
}

updateState { copy(alarmMessage = newMessage) }
}

private fun toggleWeekdaysChecked() {
Expand All @@ -36,12 +81,14 @@ class AlarmAddEditViewModel @Inject constructor() : BaseViewModel<AlarmAddEditCo
} else {
currentState.selectedDays - weekdays
}

updateState {
copy(
isWeekdaysChecked = isChecked,
selectedDays = updatedDays,
)
}
debounceUpdateAlarmMessage() // 디바운싱된 메시지 업데이트
}

private fun toggleWeekendsChecked() {
Expand All @@ -52,12 +99,14 @@ class AlarmAddEditViewModel @Inject constructor() : BaseViewModel<AlarmAddEditCo
} else {
currentState.selectedDays - weekends
}

updateState {
copy(
isWeekendsChecked = isChecked,
selectedDays = updatedDays,
)
}
debounceUpdateAlarmMessage() // 디바운싱된 메시지 업데이트
}

private fun toggleDaySelection(day: AlarmDay) {
Expand All @@ -68,13 +117,15 @@ class AlarmAddEditViewModel @Inject constructor() : BaseViewModel<AlarmAddEditCo
}
val weekdays = setOf(AlarmDay.MON, AlarmDay.TUE, AlarmDay.WED, AlarmDay.THU, AlarmDay.FRI)
val weekends = setOf(AlarmDay.SAT, AlarmDay.SUN)

updateState {
copy(
selectedDays = updatedDays,
isWeekdaysChecked = updatedDays.containsAll(weekdays),
isWeekendsChecked = updatedDays.containsAll(weekends),
)
}
debounceUpdateAlarmMessage() // 디바운싱된 메시지 업데이트
}

private fun toggleDisableHolidayChecked() {
Expand All @@ -83,5 +134,42 @@ class AlarmAddEditViewModel @Inject constructor() : BaseViewModel<AlarmAddEditCo
isDisableHolidayChecked = !currentState.isDisableHolidayChecked,
)
}
debounceUpdateAlarmMessage() // 디바운싱된 메시지 업데이트
}

private fun convertTo24HourFormat(amPm: String, hour: Int): Int {
return if (amPm == "오후" && hour != 12) {
hour + 12
} else if (amPm == "오전" && hour == 12) {
0
} else {
hour
}
}
Copy link
Member

Choose a reason for hiding this comment

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

p3

private fun convertTo24HourFormat(amPm: String, hour: Int): Int = when {
    amPm == "오후" && hour != 12 -> hour + 12
    amPm == "오전" && hour == 12 -> 0
    else -> hour
}

Copy link
Member Author

Choose a reason for hiding this comment

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

반영했습니다!


private fun calculateNextAlarmDateTime(
now: java.time.LocalDateTime,
alarmTimeToday: java.time.LocalDateTime,
selectedDays: Set<AlarmDay>,
): java.time.LocalDateTime {
if (selectedDays.isEmpty()) {
return if (alarmTimeToday.isBefore(now)) {
alarmTimeToday.plusDays(1)
} else {
alarmTimeToday
}
}

val currentDayOfWeek = now.dayOfWeek.value
val selectedDaysOfWeek = selectedDays.map { it.toDayOfWeek().value }.sorted()
val nextDay = selectedDaysOfWeek.firstOrNull { it > currentDayOfWeek }
?: selectedDaysOfWeek.first()
val daysToAdd = if (nextDay > currentDayOfWeek) {
nextDay - currentDayOfWeek
} else {
7 - (currentDayOfWeek - nextDay)
}
val nextAlarmDate = now.toLocalDate().plusDays(daysToAdd.toLong())
return nextAlarmDate.atTime(alarmTimeToday.toLocalTime())
}
}
12 changes: 12 additions & 0 deletions feature/home/src/main/java/com/yapp/alarm/AlarmDay.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,15 @@ enum class AlarmDay(
R.string.alarm_add_edit_saturday,
),
}

fun AlarmDay.toDayOfWeek(): java.time.DayOfWeek {
return when (this) {
AlarmDay.SUN -> java.time.DayOfWeek.SUNDAY
AlarmDay.MON -> java.time.DayOfWeek.MONDAY
AlarmDay.TUE -> java.time.DayOfWeek.TUESDAY
AlarmDay.WED -> java.time.DayOfWeek.WEDNESDAY
AlarmDay.THU -> java.time.DayOfWeek.THURSDAY
AlarmDay.FRI -> java.time.DayOfWeek.FRIDAY
AlarmDay.SAT -> java.time.DayOfWeek.SATURDAY
}
}