diff --git a/domain/daily/src/main/kotlin/com/titi/app/doamin/daily/usecase/HasDailyForCurrentMonthUseCase.kt b/domain/daily/src/main/kotlin/com/titi/app/doamin/daily/usecase/HasDailyForCurrentMonthUseCase.kt new file mode 100644 index 00000000..96cba154 --- /dev/null +++ b/domain/daily/src/main/kotlin/com/titi/app/doamin/daily/usecase/HasDailyForCurrentMonthUseCase.kt @@ -0,0 +1,41 @@ +package com.titi.app.doamin.daily.usecase + +import com.titi.app.data.daily.api.DailyRepository +import java.time.LocalDate +import java.time.ZoneId +import java.time.ZoneOffset +import java.time.ZonedDateTime +import javax.inject.Inject + +class HasDailyForCurrentMonthUseCase @Inject constructor( + private val dailyRepository: DailyRepository, +) { + suspend operator fun invoke(currentDate: LocalDate): List { + val startDay = 1 + val lastDay = currentDate.lengthOfMonth() + val hasDaily = mutableListOf() + + val dailies = dailyRepository.getDailies( + startDateTime = currentDate + .withDayOfMonth(startDay) + .atStartOfDay() + .atZone(ZoneOffset.systemDefault()) + .withZoneSameInstant(ZoneOffset.UTC) + .toString(), + endDateTime = currentDate + .withDayOfMonth(lastDay) + .atTime(23, 59, 59) + .atZone(ZoneId.systemDefault()) + .withZoneSameInstant(ZoneOffset.UTC) + .toString(), + ) + + dailies?.forEach { + val zonedDateTime = + ZonedDateTime.parse(it.day).withZoneSameInstant(ZoneId.systemDefault()) + hasDaily.add(zonedDateTime.toLocalDate()) + } + + return hasDaily.toList() + } +} diff --git a/feature/log/src/main/kotlin/com/titi/app/feature/log/model/LogUiState.kt b/feature/log/src/main/kotlin/com/titi/app/feature/log/model/LogUiState.kt index 0f561cac..0ac6c95a 100644 --- a/feature/log/src/main/kotlin/com/titi/app/feature/log/model/LogUiState.kt +++ b/feature/log/src/main/kotlin/com/titi/app/feature/log/model/LogUiState.kt @@ -87,6 +87,7 @@ data class HomeUiState( data class DailyUiState( val currentDate: LocalDate = LocalDate.now(), + val hasDailies: List = emptyList(), val dailyGraphData: DailyGraphData = DailyGraphData(), ) @@ -100,6 +101,7 @@ data class DailyGraphData( data class WeekUiState( val currentDate: LocalDate = LocalDate.now(), + val hasDailies: List = emptyList(), val weekGraphData: WeekGraphData = WeekGraphData(), ) diff --git a/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/DailyScreen.kt b/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/DailyScreen.kt index 844877e6..d77ee957 100644 --- a/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/DailyScreen.kt +++ b/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/DailyScreen.kt @@ -30,6 +30,7 @@ import androidx.compose.material3.Icon import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment @@ -73,12 +74,14 @@ fun DailyScreen( currentDate: LocalDate, totalTime: String, maxTime: String, + hasDailies: List, taskData: List, tdsColors: List, timeLines: List, timeTableData: List, onClickDate: (LocalDate) -> Unit, onClickGraphColor: (Int) -> Unit, + onCalendarLocalDateChanged: (LocalDate) -> Unit, ) { val scrollState = rememberScrollState() @@ -91,7 +94,9 @@ fun DailyScreen( modifier = Modifier.fillMaxWidth(), themeColor = tdsColors.first(), currentDate = currentDate, + hasDailies = hasDailies, onClickDate = onClickDate, + onCalendarLocalDateChanged = onCalendarLocalDateChanged, ) Spacer(modifier = Modifier.height(15.dp)) @@ -124,7 +129,9 @@ fun CalendarContent( modifier: Modifier = Modifier, themeColor: TdsColor, currentDate: LocalDate, + hasDailies: List, onClickDate: (LocalDate) -> Unit, + onCalendarLocalDateChanged: (LocalDate) -> Unit, ) { val scope = rememberCoroutineScope() @@ -141,6 +148,9 @@ fun CalendarContent( outDateStyle = OutDateStyle.EndOfGrid, ) + LaunchedEffect(state.firstVisibleMonth.yearMonth.atDay(1)) { + onCalendarLocalDateChanged(state.firstVisibleMonth.yearMonth.atDay(1)) + } Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically, @@ -214,6 +224,7 @@ fun CalendarContent( Day( day = day, isSelected = currentDate == day.date, + hasDaily = hasDailies.contains(day.date), themeColor = themeColor, ) { selectedDay -> if (currentDate != selectedDay.date) { @@ -248,6 +259,7 @@ fun CalendarContent( fun Day( day: CalendarDay, isSelected: Boolean, + hasDaily: Boolean, themeColor: TdsColor, onClickDate: (CalendarDay) -> Unit, ) { @@ -255,29 +267,50 @@ fun Day( modifier = Modifier .padding(horizontal = 5.dp) .aspectRatio(1f) - .clip(CircleShape) - .background( - color = if (isSelected) { - themeColor.getColor() - } else { - Color.Transparent - }, - ) .clickable( enabled = day.position == DayPosition.MonthDate, onClick = { onClickDate(day) }, ), - contentAlignment = Alignment.Center, ) { - Text( - text = day.date.dayOfMonth.toString(), - color = if (day.position == DayPosition.MonthDate) { - TdsColor.TEXT.getColor() - } else { - Color.Gray - }, - style = TdsTextStyle.SEMI_BOLD_TEXT_STYLE.getTextStyle(fontSize = 18.sp), - ) + Box( + modifier = Modifier + .fillMaxSize(0.75f) + .clip(CircleShape) + .background( + color = if (isSelected) { + themeColor.getColor() + } else { + Color.Transparent + }, + ).align(Alignment.Center), + contentAlignment = Alignment.Center, + ) { + Text( + text = day.date.dayOfMonth.toString(), + color = if (day.position == DayPosition.MonthDate) { + TdsColor.TEXT.getColor() + } else { + Color.Gray + }, + style = TdsTextStyle.SEMI_BOLD_TEXT_STYLE.getTextStyle(fontSize = 18.sp), + ) + } + + if (hasDaily) { + Box( + modifier = Modifier + .size(3.dp) + .clip(CircleShape) + .background( + color = if (isSelected) { + themeColor.getColor() + } else { + Color.Red + }, + ) + .align(Alignment.BottomCenter), + ) + } } } @@ -504,11 +537,13 @@ private fun DailyScreenPreview() { tdsColors = tdsColors, totalTime = "08:00:00", maxTime = "03:00:00", + hasDailies = emptyList(), timeLines = timeLines, timeTableData = timeTableData, currentDate = LocalDate.now(), onClickDate = {}, onClickGraphColor = {}, + onCalendarLocalDateChanged = {}, ) } } diff --git a/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/LogScreen.kt b/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/LogScreen.kt index 255d8346..5ec2cc61 100644 --- a/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/LogScreen.kt +++ b/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/LogScreen.kt @@ -89,6 +89,7 @@ fun LogScreen(viewModel: LogViewModel = mavericksViewModel()) { 1 -> DailyScreen( currentDate = uiState.dailyUiState.currentDate, + hasDailies = uiState.dailyUiState.hasDailies, totalTime = uiState.dailyUiState.dailyGraphData.totalTime, maxTime = uiState.dailyUiState.dailyGraphData.maxTime, taskData = uiState.dailyUiState.dailyGraphData.taskData, @@ -104,10 +105,14 @@ fun LogScreen(viewModel: LogViewModel = mavericksViewModel()) { graphColorUiState = uiState.graphColors, ) }, + onCalendarLocalDateChanged = { + viewModel.updateHasDailyAtDailyTab(it) + }, ) 2 -> WeekScreen( weekInformation = uiState.weekUiState.weekGraphData.weekInformation, + hasDailies = uiState.weekUiState.hasDailies, totalTime = uiState.weekUiState.weekGraphData.totalWeekTime, averageTime = uiState.weekUiState.weekGraphData.averageWeekTime, weekLineChardData = uiState.weekUiState.weekGraphData.weekLineChartData, @@ -124,6 +129,9 @@ fun LogScreen(viewModel: LogViewModel = mavericksViewModel()) { graphColorUiState = uiState.graphColors, ) }, + onCalendarLocalDateChanged = { + viewModel.updateHasDailyAtWeekTab(it) + }, ) } } diff --git a/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/LogViewModel.kt b/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/LogViewModel.kt index cc125a7e..544cf1e3 100644 --- a/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/LogViewModel.kt +++ b/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/LogViewModel.kt @@ -9,6 +9,7 @@ import com.titi.app.doamin.daily.usecase.GetAllDailiesTasksUseCase import com.titi.app.doamin.daily.usecase.GetCurrentDateDailyUseCase import com.titi.app.doamin.daily.usecase.GetMonthDailyUseCase import com.titi.app.doamin.daily.usecase.GetWeekDailyUseCase +import com.titi.app.doamin.daily.usecase.HasDailyForCurrentMonthUseCase import com.titi.app.domain.color.usecase.GetGraphColorsUseCase import com.titi.app.domain.color.usecase.UpdateGraphColorsUseCase import com.titi.app.feature.log.mapper.toDomainModel @@ -37,6 +38,7 @@ class LogViewModel @AssistedInject constructor( private val getMonthDailyUseCase: GetMonthDailyUseCase, private val getCurrentDateDailyUseCase: GetCurrentDateDailyUseCase, private val getWeekDailyUseCase: GetWeekDailyUseCase, + private val hasDailyForCurrentMonthUseCase: HasDailyForCurrentMonthUseCase, ) : MavericksViewModel(initialState) { init { @@ -111,6 +113,33 @@ class LogViewModel @AssistedInject constructor( } } + fun updateHasDailyAtDailyTab(date: LocalDate) { + viewModelScope.launch { + val state = awaitState() + if ( + state.weekUiState.currentDate.month == date.month && + state.weekUiState.hasDailies.isNotEmpty() + ) { + setState { + copy( + dailyUiState = dailyUiState.copy( + hasDailies = state.weekUiState.hasDailies, + ), + ) + } + } else { + val hasDailies = hasDailyForCurrentMonthUseCase(date) + setState { + copy( + dailyUiState = dailyUiState.copy( + hasDailies = hasDailies, + ), + ) + } + } + } + } + fun updateCurrentDateDaily(date: LocalDate) { viewModelScope.launch { getCurrentDateDailyUseCase(date) @@ -136,6 +165,33 @@ class LogViewModel @AssistedInject constructor( } } + fun updateHasDailyAtWeekTab(date: LocalDate) { + viewModelScope.launch { + val state = awaitState() + if ( + state.dailyUiState.currentDate.month == date.month && + state.dailyUiState.hasDailies.isNotEmpty() + ) { + setState { + copy( + weekUiState = weekUiState.copy( + hasDailies = state.dailyUiState.hasDailies, + ), + ) + } + } else { + val hasDailies = hasDailyForCurrentMonthUseCase(date) + setState { + copy( + weekUiState = weekUiState.copy( + hasDailies = hasDailies, + ), + ) + } + } + } + } + @AssistedFactory interface Factory : AssistedViewModelFactory { override fun create(state: LogUiState): LogViewModel diff --git a/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/WeekScreen.kt b/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/WeekScreen.kt index e12ffcb8..4f685c07 100644 --- a/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/WeekScreen.kt +++ b/feature/log/src/main/kotlin/com/titi/app/feature/log/ui/WeekScreen.kt @@ -24,6 +24,7 @@ import java.time.LocalDate fun WeekScreen( totalTime: String, averageTime: String, + hasDailies: List, weekLineChardData: List, weekInformation: Triple, tdsColors: List, @@ -32,6 +33,7 @@ fun WeekScreen( currentDate: LocalDate, onClickDate: (LocalDate) -> Unit, onClickGraphColor: (Int) -> Unit, + onCalendarLocalDateChanged: (LocalDate) -> Unit, ) { val scrollState = rememberScrollState() @@ -44,7 +46,9 @@ fun WeekScreen( modifier = Modifier.fillMaxWidth(), themeColor = tdsColors.first(), currentDate = currentDate, + hasDailies = hasDailies, onClickDate = onClickDate, + onCalendarLocalDateChanged = onCalendarLocalDateChanged, ) Spacer(modifier = Modifier.height(15.dp)) @@ -151,11 +155,13 @@ private fun WeekScreenPreview() { averageTime = "03:00:00", weekLineChardData = weekLineChardData, tdsColors = tdsColors, + hasDailies = emptyList(), topLevelTaskData = taskData, topLevelTaskTotal = "08:00:00", currentDate = LocalDate.now(), onClickDate = {}, onClickGraphColor = {}, + onCalendarLocalDateChanged = {}, ) } }