diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index ee099a0..c925b19 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -5,9 +5,6 @@ - - @@ -17,6 +14,9 @@ + + \ No newline at end of file diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm index e378200..9773a27 100644 Binary files a/app/release/baselineProfiles/0/app-release.dm and b/app/release/baselineProfiles/0/app-release.dm differ diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm index 5882dbe..4b8215a 100644 Binary files a/app/release/baselineProfiles/1/app-release.dm and b/app/release/baselineProfiles/1/app-release.dm differ diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/MainNavDisplay.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/MainNavDisplay.kt index 425665e..0b88856 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/MainNavDisplay.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/MainNavDisplay.kt @@ -13,6 +13,7 @@ import com.pixelized.headache.ui.navigation.destination.calendarChooserDestinati import com.pixelized.headache.ui.navigation.destination.eventDestinationEntry import com.pixelized.headache.ui.navigation.destination.homeDestinationEntry import com.pixelized.headache.ui.navigation.destination.monthSummaryDestinationEntry +import com.pixelized.headache.ui.navigation.destination.yearSummaryDestinationEntry val LocalNavigator = staticCompositionLocalOf { error("Local Navigation no yet ready") @@ -42,6 +43,7 @@ fun MainNavDisplay( calendarChooserDestinationEntry() eventDestinationEntry() monthSummaryDestinationEntry() + yearSummaryDestinationEntry() } ) } diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/MonthSummaryDestination.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/MonthSummaryDestination.kt index 88eeac3..2f4541a 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/MonthSummaryDestination.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/MonthSummaryDestination.kt @@ -3,7 +3,7 @@ package com.pixelized.headache.ui.navigation.destination import androidx.navigation3.runtime.EntryProviderBuilder import androidx.navigation3.runtime.entry import com.pixelized.headache.ui.navigation.Navigator -import com.pixelized.headache.ui.page.summary.MonthSummaryPage +import com.pixelized.headache.ui.page.summary.month.MonthSummaryPage data object MonthSummaryDestination diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/YearSummaryDestination.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/YearSummaryDestination.kt new file mode 100644 index 0000000..6fd30fb --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/YearSummaryDestination.kt @@ -0,0 +1,19 @@ +package com.pixelized.headache.ui.navigation.destination + +import androidx.navigation3.runtime.EntryProviderBuilder +import androidx.navigation3.runtime.entry +import com.pixelized.headache.ui.navigation.Navigator +import com.pixelized.headache.ui.page.summary.year.YearSummaryPage + + +data object YearSummaryDestination + +fun EntryProviderBuilder<*>.yearSummaryDestinationEntry() { + entry { + YearSummaryPage() + } +} + +fun Navigator.navigateToYearSummary() { + goTo(YearSummaryDestination) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/event/edit/EventEditBottomSheet.kt b/app/src/main/java/com/pixelized/headache/ui/page/event/edit/EventEditBottomSheet.kt index 71f4053..e0915d6 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/event/edit/EventEditBottomSheet.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/event/edit/EventEditBottomSheet.kt @@ -41,6 +41,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import com.pixelized.headache.R import com.pixelized.headache.ui.common.error.HandleErrorMessage import com.pixelized.headache.ui.theme.HeadacheTheme +import com.pixelized.headache.ui.theme.color.HeadacheColorPalette import com.pixelized.headache.utils.extention.capitalize import kotlinx.coroutines.launch import java.util.Date @@ -223,20 +224,60 @@ private class EventEditPreviewPreviewProvider : PreviewParameterProvider Unit, onEvent: () -> Unit, onMonthSummary: () -> Unit, + onYearSummary: () -> Unit, ) { Scaffold( modifier = modifier, @@ -87,6 +92,12 @@ private fun HomePageContent( onClick = onMonthSummary, ) } + item { + NavigationItem( + label = stringResource(R.string.year_summary_title), + onClick = onYearSummary, + ) + } } } ) diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryFactory.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryFactory.kt similarity index 88% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryFactory.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryFactory.kt index 82a0b40..eb624aa 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryFactory.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryFactory.kt @@ -1,13 +1,16 @@ -package com.pixelized.headache.ui.page.summary.item +package com.pixelized.headache.ui.page.summary.month import android.icu.util.Calendar import androidx.compose.ui.graphics.Color import com.pixelized.headache.repository.event.Event -import com.pixelized.headache.ui.page.summary.MonthSummaryPillItemUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryBoxUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryCell +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryItemUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryPillItemUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryTitleUio import com.pixelized.headache.utils.extention.event import javax.inject.Inject import kotlin.math.max -import kotlin.reflect.KClass class MonthSummaryFactory @Inject constructor() { private val calendar = Calendar.getInstance() @@ -86,11 +89,11 @@ class MonthSummaryFactory @Inject constructor() { date = calendar.apply { event = entry.key }.time, headacheRatio = entry.value.size.toFloat() / monthMaxDay, headacheAmount = entry.value.size, - headacheColor = Color.Red, + headacheColor = Color.Companion.Red, pillRatio = pillAmount.takeIf { it > 0 } ?.let { it.toFloat() / maxPillAmount.toFloat() }, pillAmount = pillAmount, - pillColor = Color.Blue, + pillColor = Color.Companion.Blue, ) } .sortedByDescending { it.date } diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryPage.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryPage.kt similarity index 93% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryPage.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryPage.kt index 3fa67d4..97fd466 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryPage.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryPage.kt @@ -1,4 +1,4 @@ -package com.pixelized.headache.ui.page.summary +package com.pixelized.headache.ui.page.summary.month import androidx.compose.animation.AnimatedContent import androidx.compose.animation.fadeIn @@ -44,13 +44,14 @@ import com.pixelized.headache.ui.navigation.LocalNavigator import com.pixelized.headache.ui.navigation.destination.navigateToEventPage import com.pixelized.headache.ui.page.event.edit.EventEditBottomSheet import com.pixelized.headache.ui.page.event.edit.EventEditBottomSheetViewModel -import com.pixelized.headache.ui.page.summary.item.MonthSummaryBox -import com.pixelized.headache.ui.page.summary.item.MonthSummaryBoxUio -import com.pixelized.headache.ui.page.summary.item.MonthSummaryCell -import com.pixelized.headache.ui.page.summary.item.MonthSummaryItem -import com.pixelized.headache.ui.page.summary.item.MonthSummaryItemUio -import com.pixelized.headache.ui.page.summary.item.MonthSummaryTitle -import com.pixelized.headache.ui.page.summary.item.MonthSummaryTitleUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryBox +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryBoxUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryCell +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryItem +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryItemUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryPillItemUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryTitle +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryTitleUio import com.pixelized.headache.ui.theme.HeadacheTheme import com.pixelized.headache.ui.theme.color.HeadacheColorPalette import java.util.Date diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryViewModel.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryViewModel.kt similarity index 85% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryViewModel.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryViewModel.kt index 611f980..86f038a 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryViewModel.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryViewModel.kt @@ -1,12 +1,10 @@ -package com.pixelized.headache.ui.page.summary +package com.pixelized.headache.ui.page.summary.month import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.pixelized.headache.repository.event.Event import com.pixelized.headache.repository.event.EventRepository -import com.pixelized.headache.ui.page.summary.item.MonthSummaryCell -import com.pixelized.headache.ui.page.summary.item.MonthSummaryFactory -import com.pixelized.headache.ui.page.summary.item.MonthSummaryTitleUio +import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryCell import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryBox.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryBox.kt similarity index 99% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryBox.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryBox.kt index c3ba0ba..dc4d060 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryBox.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryBox.kt @@ -1,4 +1,4 @@ -package com.pixelized.headache.ui.page.summary.item +package com.pixelized.headache.ui.page.summary.month.item import android.annotation.SuppressLint import android.icu.text.SimpleDateFormat diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryCell.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryCell.kt similarity index 68% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryCell.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryCell.kt index be6308a..863d793 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryCell.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryCell.kt @@ -1,4 +1,4 @@ -package com.pixelized.headache.ui.page.summary.item +package com.pixelized.headache.ui.page.summary.month.item import androidx.compose.runtime.Stable import java.util.Date diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryItem.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryItem.kt similarity index 94% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryItem.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryItem.kt index c228ad8..9adf1f0 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryItem.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryItem.kt @@ -1,4 +1,4 @@ -package com.pixelized.headache.ui.page.summary.item +package com.pixelized.headache.ui.page.summary.month.item import android.annotation.SuppressLint import android.icu.text.SimpleDateFormat @@ -14,14 +14,13 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp -import com.pixelized.headache.ui.page.summary.MonthSummaryPillItem -import com.pixelized.headache.ui.page.summary.MonthSummaryPillItemUio import com.pixelized.headache.ui.theme.HeadacheTheme import com.pixelized.headache.ui.theme.color.HeadacheColorPalette import com.pixelized.headache.utils.extention.capitalize @@ -68,11 +67,10 @@ fun MonthSummaryItem( Row( horizontalArrangement = Arrangement.spacedBy(space = spacing.width) ) { - formatter.format(item.date) Text( modifier = Modifier.alignByBaseline(), style = MaterialTheme.typography.titleLarge, - text = formatter.format(item.date).capitalize(), + text = remember(formatter, item.date) { formatter.format(item.date) }.capitalize(), ) Text( modifier = Modifier.alignByBaseline(), diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryPillItem.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryPillItem.kt similarity index 97% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryPillItem.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryPillItem.kt index f879986..f0c799e 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/MonthSummaryPillItem.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryPillItem.kt @@ -1,4 +1,4 @@ -package com.pixelized.headache.ui.page.summary +package com.pixelized.headache.ui.page.summary.month.item import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryTitle.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryTitle.kt similarity index 97% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryTitle.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryTitle.kt index 6bf9ea4..65cc3ec 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/item/MonthSummaryTitle.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryTitle.kt @@ -1,4 +1,4 @@ -package com.pixelized.headache.ui.page.summary.item +package com.pixelized.headache.ui.page.summary.month.item import android.annotation.SuppressLint import android.icu.text.SimpleDateFormat diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryFactory.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryFactory.kt new file mode 100644 index 0000000..088574f --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryFactory.kt @@ -0,0 +1,91 @@ +package com.pixelized.headache.ui.page.summary.year + +import android.icu.util.Calendar +import com.pixelized.headache.repository.event.Event +import com.pixelized.headache.ui.page.summary.year.item.YearSummaryDayUio +import com.pixelized.headache.ui.page.summary.year.item.YearSummaryMonthUio +import javax.inject.Inject + + +class YearSummaryFactory @Inject constructor() { + + fun convertToUio( + events: Collection, + ): List { + + val monthFirstDayCalendar = Calendar.getInstance().apply { + firstDayOfWeek = Calendar.MONDAY + setMinimalDaysInFirstWeek(1) + } + val monthLastDayCalendar = Calendar.getInstance().apply { + firstDayOfWeek = Calendar.MONDAY + setMinimalDaysInFirstWeek(1) + } + val currentDayCalendar = Calendar.getInstance().apply { + firstDayOfWeek = Calendar.MONDAY + setMinimalDaysInFirstWeek(1) + } + + return events + .fold(initial = hashMapOf>>()) { acc, event -> + acc.also { + val years = it.getOrPut(key = event.date.year) { hashMapOf() } + val months = years.getOrPut(key = event.date.month) { hashMapOf() } + months[event.date.day] = event + } + } + .map { yearEntry -> + YearUio( + year = yearEntry.key, + months = yearEntry.value.map { monthEntry -> + monthFirstDayCalendar.apply { + set(Calendar.YEAR, yearEntry.key) + set(Calendar.MONTH, monthEntry.key) + set(Calendar.DAY_OF_MONTH, 1) + set(Calendar.MILLISECONDS_IN_DAY, 1) + } + monthLastDayCalendar.apply { + time = monthFirstDayCalendar.time + add(Calendar.MONTH, 1) + add(Calendar.DAY_OF_MONTH, -1) + } + currentDayCalendar.apply { + time = monthFirstDayCalendar.time + } + val initial = MutableList( + size = monthLastDayCalendar.get(Calendar.WEEK_OF_MONTH), + ) { + mutableListOf() + } + val weeks = (1..monthLastDayCalendar.get(Calendar.DAY_OF_MONTH)) + .fold(initial = initial) { accumulator, dayNumber -> + val event: Event? = monthEntry.value.get(dayNumber) + val weekIndex = currentDayCalendar + .apply { + set(Calendar.YEAR, yearEntry.key) + set(Calendar.MONTH, monthEntry.key) + set(Calendar.DAY_OF_MONTH, dayNumber) + set(Calendar.MILLISECONDS_IN_DAY, 1) + } + .get(Calendar.WEEK_OF_MONTH) - 1 + val day = YearSummaryDayUio( + number = dayNumber, + headache = event != null, + pills = event?.pills?.map { it.color } ?: emptyList(), + ) + accumulator.also { acc -> + acc[weekIndex] = acc.get(index = weekIndex).also { week -> + week.add(day) + } + } + } + YearSummaryMonthUio( + date = monthFirstDayCalendar.time, + weeks = weeks, + ) + }, + ) + } + .sortedByDescending { it.year } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryPage.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryPage.kt new file mode 100644 index 0000000..5b48ec6 --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryPage.kt @@ -0,0 +1,277 @@ +package com.pixelized.headache.ui.page.summary.year + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +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.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.pixelized.headache.R +import com.pixelized.headache.ui.navigation.LocalNavigator +import com.pixelized.headache.ui.navigation.destination.navigateToEventPage +import com.pixelized.headache.ui.page.event.edit.EventEditBottomSheet +import com.pixelized.headache.ui.page.event.edit.EventEditBottomSheetViewModel +import com.pixelized.headache.ui.page.summary.year.item.YearSummaryDayDefault +import com.pixelized.headache.ui.page.summary.year.item.YearSummaryDayUio +import com.pixelized.headache.ui.page.summary.year.item.YearSummaryMonth +import com.pixelized.headache.ui.page.summary.year.item.YearSummaryMonthUio +import com.pixelized.headache.ui.theme.HeadacheTheme +import java.util.Calendar +import java.util.Date + +@Stable +data class YearUio( + val year: Int, + val months: List, +) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun YearSummaryPage( + viewModel: YearSummaryViewModel = hiltViewModel(), + editViewModel: EventEditBottomSheetViewModel = hiltViewModel(), +) { + val navigation = LocalNavigator.current + val uio = viewModel.events.collectAsStateWithLifecycle() + + YearSummaryContent( + modifier = Modifier + .systemBarsPadding() + .fillMaxSize(), + uio = uio, + onBack = { + navigation.popBackstack() + }, + onMonth = { + navigation.navigateToEventPage(date = it.date) + }, + onAddEvent = { + editViewModel.show() + }, + ) + + EventEditBottomSheet( + viewModel = editViewModel, + onDismissRequest = { editViewModel.dismiss() }, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun YearSummaryContent( + modifier: Modifier = Modifier, + uio: State>, + onBack: () -> Unit, + onMonth: (YearSummaryMonthUio) -> Unit, + onAddEvent: () -> Unit, +) { + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + navigationIcon = { + IconButton( + onClick = onBack, + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = null, + ) + } + }, + title = { + Text(text = stringResource(R.string.year_summary_title)) + }, + ) + }, + floatingActionButton = { + FloatingActionButton( + containerColor = MaterialTheme.colorScheme.primary, + shape = CircleShape, + onClick = onAddEvent, + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = null, + ) + } + }, + content = { paddingValues -> + LazyVerticalGrid( + modifier = Modifier.padding(paddingValues = paddingValues), + columns = GridCells.Adaptive( + minSize = YearSummaryDayDefault.size * 7, + ), + horizontalArrangement = Arrangement.SpaceBetween, + verticalArrangement = Arrangement.spacedBy(space = 4.dp), + contentPadding = PaddingValues( + start = 16.dp, + top = 16.dp, + end = 16.dp, + bottom = 16.dp + 16.dp + 56.dp, + ), + ) { + uio.value.forEachIndexed { index, (year, months) -> + item( + span = { GridItemSpan(maxCurrentLineSpan) }, + contentType = { "Title" }, + ) { + Text( + modifier = Modifier.padding( + top = when (index) { + 0 -> 0.dp + else -> 16.dp + } + ), + style = MaterialTheme.typography.displaySmall, + text = "$year", + ) + } + items( + items = months, + key = { it.date.time }, + contentType = { "Month" }, + ) { + YearSummaryMonth( + uio = it, + onMonth = onMonth, + ) + } + } + } + } + ) +} + +@Composable +@Preview +private fun YearSummaryPreview( + @PreviewParameter(YearPreviewProvider::class) preview: List, +) { + HeadacheTheme { + Surface { + YearSummaryContent( + uio = remember { mutableStateOf(preview) }, + onBack = { }, + onMonth = { }, + onAddEvent = { }, + ) + } + } +} + +private class YearPreviewProvider() : PreviewParameterProvider> { + + private fun year( + year: Int, + vararg months: YearSummaryMonthUio, + ) = YearUio( + year = year, + months = months.toList(), + ) + + private fun month( + month: Date, + vararg weeks: List, + ) = YearSummaryMonthUio( + date = month, + weeks = weeks.toList(), + ) + + private fun week( + vararg days: YearSummaryDayUio, + ) = days.toList() + + private fun day( + number: Int, + headache: Boolean = false, + pills: List = emptyList(), + ) = YearSummaryDayUio( + number = number, + headache = headache, + pills = pills, + ) + + override val values: Sequence> = sequenceOf( + listOf( + year( + year = 2025, + month( + month = Calendar.getInstance().apply { + set(Calendar.YEAR, 2025) + set(Calendar.MONTH, Calendar.JANUARY) + }.time, + week(day(1), day(2), day(3), day(4), day(5)), + week(day(6), day(7), day(8), day(9), day(10), day(11), day(12)), + week(day(13), day(14), day(15), day(16), day(17), day(18), day(19)), + week(day(20), day(21), day(22), day(23), day(24), day(25), day(26)), + week(day(27), day(28), day(29), day(30), day(31)), + ), + month( + month = Calendar.getInstance().apply { + set(Calendar.YEAR, 2025) + set(Calendar.MONTH, Calendar.FEBRUARY) + }.time, + week(day(1), day(2)), + week(day(3), day(4), day(5), day(6), day(7), day(8), day(9)), + week(day(10), day(11), day(12), day(13), day(14), day(15), day(16)), + week(day(17), day(18), day(19), day(20), day(21), day(22), day(23)), + week(day(24), day(25), day(26), day(27), day(28)), + ), + month( + month = Calendar.getInstance().apply { + set(Calendar.YEAR, 2025) + set(Calendar.MONTH, Calendar.MARCH) + }.time, + week(day(1), day(2)), + week(day(3), day(4), day(5), day(6), day(7), day(8), day(9)), + week(day(10), day(11), day(12), day(13), day(14), day(15), day(16)), + week(day(17), day(18), day(19), day(20), day(21), day(22), day(23)), + week(day(24), day(25), day(26), day(27), day(28), day(29), day(30)), + week(day(31)), + ), + month( + month = Calendar.getInstance().apply { + set(Calendar.YEAR, 2025) + set(Calendar.MONTH, Calendar.APRIL) + }.time, + week(day(1), day(2), day(3), day(4), day(5), day(6)), + week(day(7), day(8), day(9), day(10), day(11), day(12), day(13)), + week(day(14), day(15), day(16), day(17), day(18), day(19), day(20)), + week(day(21), day(22), day(23), day(24), day(25), day(26), day(27)), + week(day(28), day(29), day(30), day(31)), + ), + ) + ), + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryViewModel.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryViewModel.kt new file mode 100644 index 0000000..56ab4a5 --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryViewModel.kt @@ -0,0 +1,28 @@ +package com.pixelized.headache.ui.page.summary.year + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pixelized.headache.repository.event.EventRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + + +@HiltViewModel +class YearSummaryViewModel @Inject constructor( + eventRepository: EventRepository, + summaryFactory: YearSummaryFactory, +) : ViewModel() { + + val events: StateFlow> = eventRepository + .eventsListFlow() + .map(summaryFactory::convertToUio) + .stateIn( + scope = viewModelScope, + started = SharingStarted.Lazily, + initialValue = emptyList(), + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryDay.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryDay.kt new file mode 100644 index 0000000..7785374 --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryDay.kt @@ -0,0 +1,127 @@ +package com.pixelized.headache.ui.page.summary.year.item + +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.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +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.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.pixelized.headache.ui.theme.HeadacheTheme +import com.pixelized.headache.ui.theme.color.HeadacheColorPalette + +@Stable +object YearSummaryDayDefault { + @Stable + val size: Dp = 16.dp + + @Stable + val pill: Dp = 2.dp +} + +@Stable +data class YearSummaryDayUio( + val number: Int, + val headache: Boolean, + val pills: List, +) + +@Composable +fun YearSummaryDay( + modifier: Modifier = Modifier, + size: Dp = YearSummaryDayDefault.size, + pill: Dp = YearSummaryDayDefault.pill, + day: YearSummaryDayUio, +) { + Box( + modifier = Modifier + .size(size = size) + .then(other = modifier), + ) { + if (day.headache) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(vertical = pill + 1.dp) + .background(color = HeadacheColorPalette.Pill.Unknown) + + ) + } + Text( + modifier = Modifier.align(alignment = Alignment.Center), + style = MaterialTheme.typography.labelSmall, + text = "${day.number}", + ) + if (day.headache) { + Row( + modifier = Modifier.align(alignment = Alignment.BottomCenter), + horizontalArrangement = Arrangement.spacedBy(space = 1.dp), + ) { + day.pills.forEach { + Box( + modifier = Modifier + .background(color = it) + .size(size = pill) + ) + } + } + } + } +} + +@Composable +@Preview +private fun YearSummaryDayPreview( + @PreviewParameter(DayPreviewProvider::class) preview: YearSummaryDayUio, +) { + HeadacheTheme { + YearSummaryDay( + day = preview, + ) + } +} + +private class DayPreviewProvider() : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + YearSummaryDayUio( + number = 1, + headache = false, + pills = emptyList(), + ), + YearSummaryDayUio( + number = 2, + headache = true, + pills = emptyList(), + ), + YearSummaryDayUio( + number = 3, + headache = true, + pills = listOf( + HeadacheColorPalette.Pill.Eletriptan40, + ), + ), + YearSummaryDayUio( + number = 3, + headache = true, + pills = listOf( + HeadacheColorPalette.Pill.Eletriptan40, + HeadacheColorPalette.Pill.Ibuprofene400, + HeadacheColorPalette.Pill.Spifen400, + HeadacheColorPalette.Pill.Paracetamol1000, + ), + ), + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryMonth.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryMonth.kt new file mode 100644 index 0000000..eefd335 --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryMonth.kt @@ -0,0 +1,185 @@ +package com.pixelized.headache.ui.page.summary.year.item + +import android.annotation.SuppressLint +import android.icu.text.DateFormat +import android.icu.text.SimpleDateFormat +import android.icu.util.Calendar +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.ripple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +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.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import com.pixelized.headache.ui.theme.HeadacheTheme +import com.pixelized.headache.ui.theme.color.HeadacheColorPalette +import com.pixelized.headache.utils.extention.capitalize +import java.util.Date +import java.util.Locale + +@Stable +data object YearSummaryMonthDefault { + @SuppressLint("ConstantLocale") + @Stable + val formatter = SimpleDateFormat("MMMM", Locale.getDefault()) +} + +@Stable +data class YearSummaryMonthUio( + val date: Date, + val weeks: List>, +) + +@Composable +fun YearSummaryMonth( + modifier: Modifier = Modifier, + formatter: DateFormat = YearSummaryMonthDefault.formatter, + uio: YearSummaryMonthUio, + onMonth: (YearSummaryMonthUio) -> Unit, +) { + Column( + modifier = Modifier + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = ripple(bounded = false), + onClick = { onMonth(uio) } + ) + .then(other = modifier), + ) { + Text( + modifier = Modifier.align(alignment = Alignment.Start), + style = MaterialTheme.typography.titleMedium, + text = remember(uio.date) { formatter.format(uio.date) }.capitalize(), + ) + Column( + modifier = Modifier.height(height = remember { YearSummaryDayDefault.size * 6 }), + ) { + uio.weeks.forEachIndexed { index, week -> + Row( + modifier = Modifier.width(width = remember { YearSummaryDayDefault.size * 7 }), + horizontalArrangement = when (index) { + 0 -> Arrangement.End + else -> Arrangement.Start + }, + ) { + week.forEach { day -> + YearSummaryDay(day = day) + } + } + } + } + } +} + +@Composable +@Preview +private fun YearSummaryMonthPreview( + @PreviewParameter(MonthPreviewProvider::class) preview: YearSummaryMonthUio, +) { + HeadacheTheme { + Surface { + YearSummaryMonth( + uio = preview, + onMonth = { }, + ) + } + } +} + +private class MonthPreviewProvider() : PreviewParameterProvider { + + fun day( + number: Int, + headache: Boolean = false, + pills: List = emptyList(), + ): YearSummaryDayUio = YearSummaryDayUio( + number = number, + headache = headache, + pills = pills, + ) + + override val values: Sequence + get() = sequenceOf( + YearSummaryMonthUio( + date = Calendar.getInstance().apply { + set(Calendar.YEAR, 2025) + set(Calendar.MONTH, Calendar.JULY) + }.time, + weeks = listOf( + listOf( + day(number = 1), + day(number = 2), + day(number = 3), + day(number = 4), + ), + listOf( + day(number = 5), + day(number = 6, headache = true), + day(number = 7), + day(number = 8, headache = true), + day(number = 9), + day(number = 10), + day(number = 11), + ), + listOf( + day(number = 12), + day(number = 13), + day(number = 14), + day( + number = 15, + headache = true, + pills = listOf(HeadacheColorPalette.Pill.Spifen400), + ), + day(number = 16), + day(number = 17), + day(number = 18), + ), + listOf( + day( + number = 19, + headache = true, + pills = listOf(HeadacheColorPalette.Pill.Spifen400), + ), + day(number = 20), + day(number = 21), + day(number = 22, headache = true), + day(number = 23), + day(number = 24), + day(number = 25), + ), + listOf( + day( + number = 26, + headache = true, + pills = listOf( + HeadacheColorPalette.Pill.Spifen400, + HeadacheColorPalette.Pill.Eletriptan40, + ), + ), + day(number = 27, headache = true), + day(number = 28), + day(number = 29), + day( + number = 30, + headache = true, + pills = listOf(HeadacheColorPalette.Pill.Spifen400), + ), + day(number = 31), + ), + ), + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/theme/color/HeadacheColorPalette.kt b/app/src/main/java/com/pixelized/headache/ui/theme/color/HeadacheColorPalette.kt index 8332525..1dbb3b9 100644 --- a/app/src/main/java/com/pixelized/headache/ui/theme/color/HeadacheColorPalette.kt +++ b/app/src/main/java/com/pixelized/headache/ui/theme/color/HeadacheColorPalette.kt @@ -20,7 +20,7 @@ object HeadacheColorPalette { val Ibuprofene400 = Additional.Blue val Paracetamol1000 = Additional.Green val Spifen400 = Additional.Yellow - val Eletriptan40 = Additional.Pink + val Eletriptan40 = Additional.VeryLightPink } @Immutable diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 26f2570..e659fe7 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -8,5 +8,6 @@ Choix du calendrier Évennement migraineux - Suivi des migraines + Suivi mensuel + Suivi annuel \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9e124a5..6a220e4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -8,5 +8,6 @@ Choose your calendar Headache event - Headache summary + Monthly follow-up + Annual follow-up \ No newline at end of file