Optimize the month cell display in the year summary.
This commit is contained in:
parent
7d2508a4de
commit
470c9ce706
19 changed files with 914 additions and 907 deletions
|
|
@ -3,7 +3,7 @@ package com.pixelized.headache.ui.navigation.destination
|
||||||
import androidx.navigation3.runtime.EntryProviderBuilder
|
import androidx.navigation3.runtime.EntryProviderBuilder
|
||||||
import androidx.navigation3.runtime.entry
|
import androidx.navigation3.runtime.entry
|
||||||
import com.pixelized.headache.ui.navigation.Navigator
|
import com.pixelized.headache.ui.navigation.Navigator
|
||||||
import com.pixelized.headache.ui.page.summary.month.MonthSummaryPage
|
import com.pixelized.headache.ui.page.summary.monthly.MonthSummaryPage
|
||||||
|
|
||||||
data object MonthSummaryDestination
|
data object MonthSummaryDestination
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package com.pixelized.headache.ui.navigation.destination
|
||||||
import androidx.navigation3.runtime.EntryProviderBuilder
|
import androidx.navigation3.runtime.EntryProviderBuilder
|
||||||
import androidx.navigation3.runtime.entry
|
import androidx.navigation3.runtime.entry
|
||||||
import com.pixelized.headache.ui.navigation.Navigator
|
import com.pixelized.headache.ui.navigation.Navigator
|
||||||
import com.pixelized.headache.ui.page.summary.year.YearSummaryPage
|
import com.pixelized.headache.ui.page.summary.yearly.YearSummaryPage
|
||||||
|
|
||||||
|
|
||||||
data object YearSummaryDestination
|
data object YearSummaryDestination
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
package com.pixelized.headache.ui.page.summary.month
|
package com.pixelized.headache.ui.page.summary.monthly
|
||||||
|
|
||||||
import android.icu.util.Calendar
|
import android.icu.util.Calendar
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import com.pixelized.headache.repository.event.Event
|
import com.pixelized.headache.repository.event.Event
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryBoxUio
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryBoxUio
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryCell
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryCell
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryItemUio
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryItemUio
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryPillItemUio
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryPillItemUio
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryTitleUio
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryTitleUio
|
||||||
import com.pixelized.headache.utils.extention.event
|
import com.pixelized.headache.utils.extention.event
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.headache.ui.page.summary.month
|
package com.pixelized.headache.ui.page.summary.monthly
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
|
|
@ -45,14 +45,14 @@ import com.pixelized.headache.ui.navigation.LocalNavigator
|
||||||
import com.pixelized.headache.ui.navigation.destination.navigateToEventPage
|
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.EventEditBottomSheet
|
||||||
import com.pixelized.headache.ui.page.event.edit.EventEditBottomSheetViewModel
|
import com.pixelized.headache.ui.page.event.edit.EventEditBottomSheetViewModel
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryBox
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryBox
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryBoxUio
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryBoxUio
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryCell
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryCell
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryItem
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryItem
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryItemUio
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryItemUio
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryPillItemUio
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryPillItemUio
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryTitle
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryTitle
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryTitleUio
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryTitleUio
|
||||||
import com.pixelized.headache.ui.theme.HeadacheTheme
|
import com.pixelized.headache.ui.theme.HeadacheTheme
|
||||||
import com.pixelized.headache.ui.theme.color.HeadacheColorPalette
|
import com.pixelized.headache.ui.theme.color.HeadacheColorPalette
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package com.pixelized.headache.ui.page.summary.month
|
package com.pixelized.headache.ui.page.summary.monthly
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.headache.repository.event.Event
|
import com.pixelized.headache.repository.event.Event
|
||||||
import com.pixelized.headache.repository.event.EventRepository
|
import com.pixelized.headache.repository.event.EventRepository
|
||||||
import com.pixelized.headache.ui.page.summary.month.item.MonthSummaryCell
|
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryCell
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.headache.ui.page.summary.month.item
|
package com.pixelized.headache.ui.page.summary.monthly.item
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.icu.text.SimpleDateFormat
|
import android.icu.text.SimpleDateFormat
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.headache.ui.page.summary.month.item
|
package com.pixelized.headache.ui.page.summary.monthly.item
|
||||||
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.headache.ui.page.summary.month.item
|
package com.pixelized.headache.ui.page.summary.monthly.item
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.icu.text.SimpleDateFormat
|
import android.icu.text.SimpleDateFormat
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.headache.ui.page.summary.month.item
|
package com.pixelized.headache.ui.page.summary.monthly.item
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.headache.ui.page.summary.month.item
|
package com.pixelized.headache.ui.page.summary.monthly.item
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.icu.text.SimpleDateFormat
|
import android.icu.text.SimpleDateFormat
|
||||||
|
|
@ -1,483 +0,0 @@
|
||||||
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.keepScreenOn
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
|
||||||
import androidx.compose.ui.platform.LocalWindowInfo
|
|
||||||
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.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.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 com.pixelized.headache.ui.theme.color.HeadacheColorPalette
|
|
||||||
import com.pixelized.headache.utils.extention.calculate
|
|
||||||
import java.util.Calendar
|
|
||||||
|
|
||||||
@Stable
|
|
||||||
data class YearUio(
|
|
||||||
val year: Int,
|
|
||||||
val months: List<YearSummaryMonthUio>,
|
|
||||||
)
|
|
||||||
|
|
||||||
@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()
|
|
||||||
.keepScreenOn()
|
|
||||||
.fillMaxSize(),
|
|
||||||
uio = uio,
|
|
||||||
onBack = {
|
|
||||||
navigation.popBackstack()
|
|
||||||
},
|
|
||||||
onMonth = {
|
|
||||||
navigation.navigateToEventPage(date = it.date)
|
|
||||||
},
|
|
||||||
onAddEvent = {
|
|
||||||
editViewModel.show()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
EventEditBottomSheet(
|
|
||||||
viewModel = editViewModel,
|
|
||||||
onDismissRequest = { editViewModel.dismiss() },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun rememberDaySize(
|
|
||||||
column: Int = 3,
|
|
||||||
space: Dp,
|
|
||||||
paddingValues: PaddingValues,
|
|
||||||
): Dp {
|
|
||||||
val density = LocalDensity.current
|
|
||||||
val windowInfo = LocalWindowInfo.current
|
|
||||||
val screenWidth = remember(density, windowInfo) {
|
|
||||||
with(density) {
|
|
||||||
windowInfo.containerSize.width.toDp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val (start, _, end, _) = paddingValues.calculate()
|
|
||||||
return (screenWidth - space * (column - 1) - start - end) / (7 * column)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun YearSummaryContent(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
paddingValues: PaddingValues = PaddingValues(
|
|
||||||
start = 16.dp,
|
|
||||||
top = 16.dp,
|
|
||||||
end = 16.dp,
|
|
||||||
bottom = 16.dp + 16.dp + 56.dp,
|
|
||||||
),
|
|
||||||
space: Dp = 8.dp,
|
|
||||||
daySize: Dp = rememberDaySize(
|
|
||||||
column = 3,
|
|
||||||
paddingValues = paddingValues,
|
|
||||||
space = space,
|
|
||||||
),
|
|
||||||
uio: State<List<YearUio>>,
|
|
||||||
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 = { it ->
|
|
||||||
LazyVerticalGrid(
|
|
||||||
modifier = Modifier.padding(paddingValues = it),
|
|
||||||
columns = GridCells.Adaptive(minSize = daySize * 7),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(space = space),
|
|
||||||
contentPadding = paddingValues,
|
|
||||||
) {
|
|
||||||
uio.value.forEachIndexed { index, (year, months) ->
|
|
||||||
item(
|
|
||||||
span = { GridItemSpan(maxLineSpan) },
|
|
||||||
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,
|
|
||||||
daySize = daySize,
|
|
||||||
onMonth = onMonth,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Preview()
|
|
||||||
private fun YearSummaryPreview(
|
|
||||||
@PreviewParameter(YearPreviewProvider::class) preview: List<YearUio>,
|
|
||||||
) {
|
|
||||||
HeadacheTheme {
|
|
||||||
Surface {
|
|
||||||
YearSummaryContent(
|
|
||||||
uio = remember { mutableStateOf(preview) },
|
|
||||||
onBack = { },
|
|
||||||
onMonth = { },
|
|
||||||
onAddEvent = { },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class YearPreviewProvider() : PreviewParameterProvider<List<YearUio>> {
|
|
||||||
|
|
||||||
private var day = 1
|
|
||||||
|
|
||||||
private fun day(): () -> YearSummaryDayUio = {
|
|
||||||
YearSummaryDayUio(
|
|
||||||
number = day++,
|
|
||||||
headache = false,
|
|
||||||
pills = emptyList(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun headache(
|
|
||||||
spifen: Boolean = false,
|
|
||||||
eletriptan: Boolean = false,
|
|
||||||
): () -> YearSummaryDayUio = {
|
|
||||||
YearSummaryDayUio(
|
|
||||||
number = day++,
|
|
||||||
headache = true,
|
|
||||||
pills = listOfNotNull(
|
|
||||||
if (spifen) HeadacheColorPalette.Pill.Spifen400 else null,
|
|
||||||
if (eletriptan) HeadacheColorPalette.Pill.Eletriptan40 else null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// inline to avoid un sequential call to "day()" between default and overridden function parameters.
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private fun week(
|
|
||||||
mon: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
tue: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
wen: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
thu: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
fry: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
sat: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
sun: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
): List<YearSummaryDayUio> = listOfNotNull(
|
|
||||||
mon?.invoke(),
|
|
||||||
tue?.invoke(),
|
|
||||||
wen?.invoke(),
|
|
||||||
thu?.invoke(),
|
|
||||||
fry?.invoke(),
|
|
||||||
sat?.invoke(),
|
|
||||||
sun?.invoke(),
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun month(
|
|
||||||
month: Int,
|
|
||||||
year: Int,
|
|
||||||
vararg weeks: List<YearSummaryDayUio>,
|
|
||||||
): YearSummaryMonthUio {
|
|
||||||
day = 1
|
|
||||||
return YearSummaryMonthUio(
|
|
||||||
date = Calendar.getInstance().apply {
|
|
||||||
set(Calendar.YEAR, year)
|
|
||||||
set(Calendar.MONTH, month)
|
|
||||||
}.time,
|
|
||||||
weeks = weeks.toList(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun year(
|
|
||||||
year: Int,
|
|
||||||
vararg months: YearSummaryMonthUio,
|
|
||||||
) = YearUio(
|
|
||||||
year = year,
|
|
||||||
months = months.toList(),
|
|
||||||
)
|
|
||||||
|
|
||||||
override val values: Sequence<List<YearUio>> = sequenceOf(
|
|
||||||
listOf(
|
|
||||||
year(
|
|
||||||
year = 2025,
|
|
||||||
month(
|
|
||||||
month = Calendar.JANUARY,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, tue = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.FEBRUARY,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.MARCH,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(tue = null, wen = null, thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.APRIL,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.MAY,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, tue = null, wen = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.JUNE,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null, fry = null, sat = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(tue = null, wen = null, thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.JULY,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, sun = headache(spifen = true)),
|
|
||||||
week(wen = headache(spifen = true)),
|
|
||||||
week(sat = headache()),
|
|
||||||
week(
|
|
||||||
wen = headache(spifen = true),
|
|
||||||
thu = headache(),
|
|
||||||
sat = headache(spifen = true),
|
|
||||||
),
|
|
||||||
week(wen = headache(), fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.AUGUST,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null),
|
|
||||||
week(wen = headache(), fry = headache()),
|
|
||||||
week(fry = headache(spifen = true)),
|
|
||||||
week(tue = headache(spifen = true), fry = headache()),
|
|
||||||
week(
|
|
||||||
tue = headache(spifen = true, eletriptan = true),
|
|
||||||
wen = headache(),
|
|
||||||
sat = headache(spifen = true),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.SEPTEMBER,
|
|
||||||
year = 2025,
|
|
||||||
week(
|
|
||||||
tue = headache(spifen = true, eletriptan = true),
|
|
||||||
wen = headache(spifen = true),
|
|
||||||
fry = headache(),
|
|
||||||
sat = headache(),
|
|
||||||
sun = headache(),
|
|
||||||
),
|
|
||||||
week(
|
|
||||||
sun = headache(spifen = true),
|
|
||||||
),
|
|
||||||
week(
|
|
||||||
wen = headache(spifen = true),
|
|
||||||
tue = headache(),
|
|
||||||
),
|
|
||||||
week(),
|
|
||||||
week(wen = null, thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.OCTOBER,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, tue = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.NOVEMBER,
|
|
||||||
year = 2025,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.DECEMBER,
|
|
||||||
year = 2025,
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
year(
|
|
||||||
year = 2024,
|
|
||||||
month(
|
|
||||||
month = Calendar.JANUARY,
|
|
||||||
year = 2024,
|
|
||||||
week(mon = null, tue = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.FEBRUARY,
|
|
||||||
year = 2024,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.MARCH,
|
|
||||||
year = 2024,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(tue = null, wen = null, thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.APRIL,
|
|
||||||
year = 2024,
|
|
||||||
week(mon = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.MAY,
|
|
||||||
year = 2024,
|
|
||||||
week(mon = null, tue = null, wen = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = Calendar.JUNE,
|
|
||||||
year = 2024,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null, fry = null, sat = null),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(),
|
|
||||||
week(tue = null, wen = null, thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
package com.pixelized.headache.ui.page.summary.year.item
|
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Surface
|
|
||||||
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<Color>,
|
|
||||||
)
|
|
||||||
|
|
||||||
@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
|
|
||||||
.matchParentSize()
|
|
||||||
.padding(vertical = pill + 1.dp)
|
|
||||||
.background(color = HeadacheColorPalette.Pill.Unknown)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
|
||||||
modifier = Modifier.align(alignment = Alignment.Center),
|
|
||||||
style = MaterialTheme.typography.labelSmall,
|
|
||||||
color = when {
|
|
||||||
!isSystemInDarkTheme() && day.headache -> Color.White
|
|
||||||
else -> MaterialTheme.colorScheme.onSurface
|
|
||||||
},
|
|
||||||
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 {
|
|
||||||
Surface {
|
|
||||||
YearSummaryDay(
|
|
||||||
modifier = Modifier.size(size = 16.dp),
|
|
||||||
day = preview,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DayPreviewProvider() : PreviewParameterProvider<YearSummaryDayUio> {
|
|
||||||
override val values: Sequence<YearSummaryDayUio>
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,243 +0,0 @@
|
||||||
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.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
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.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
|
|
||||||
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<List<YearSummaryDayUio>>,
|
|
||||||
)
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun YearSummaryMonth(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
formatter: DateFormat = YearSummaryMonthDefault.formatter,
|
|
||||||
daySize: Dp,
|
|
||||||
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(),
|
|
||||||
)
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.size(width = daySize * 7, height = daySize * 6),
|
|
||||||
) {
|
|
||||||
uio.weeks.forEachIndexed { row, week ->
|
|
||||||
week.forEachIndexed { column, day ->
|
|
||||||
YearSummaryDay(
|
|
||||||
Modifier.offset(
|
|
||||||
x = when (row) {
|
|
||||||
0 -> daySize * (column + 7 - week.size)
|
|
||||||
else -> daySize * column
|
|
||||||
},
|
|
||||||
y = daySize * row,
|
|
||||||
),
|
|
||||||
size = daySize,
|
|
||||||
day = day,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Preview
|
|
||||||
private fun YearSummaryMonthPreview(
|
|
||||||
@PreviewParameter(MonthPreviewProvider::class) preview: YearSummaryMonthUio,
|
|
||||||
) {
|
|
||||||
HeadacheTheme {
|
|
||||||
Surface {
|
|
||||||
YearSummaryMonth(
|
|
||||||
modifier = Modifier.padding(all = 8.dp),
|
|
||||||
daySize = YearSummaryDayDefault.size,
|
|
||||||
uio = preview,
|
|
||||||
onMonth = { },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MonthPreviewProvider() : PreviewParameterProvider<YearSummaryMonthUio> {
|
|
||||||
|
|
||||||
private var day: Int = 1
|
|
||||||
|
|
||||||
private fun day(): () -> YearSummaryDayUio = {
|
|
||||||
YearSummaryDayUio(
|
|
||||||
number = day++,
|
|
||||||
headache = false,
|
|
||||||
pills = emptyList(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun headache(
|
|
||||||
spifen: Boolean = false,
|
|
||||||
eletriptan: Boolean = false,
|
|
||||||
): () -> YearSummaryDayUio = {
|
|
||||||
YearSummaryDayUio(
|
|
||||||
number = day++,
|
|
||||||
headache = true,
|
|
||||||
pills = listOfNotNull(
|
|
||||||
if (spifen) HeadacheColorPalette.Pill.Spifen400 else null,
|
|
||||||
if (eletriptan) HeadacheColorPalette.Pill.Eletriptan40 else null,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// inline to avoid un sequential call to "day()" between default and overridden function parameters.
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private fun week(
|
|
||||||
mon: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
tue: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
wen: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
thu: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
fry: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
sat: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
sun: (() -> YearSummaryDayUio)? = day(),
|
|
||||||
): List<YearSummaryDayUio> = listOfNotNull(
|
|
||||||
mon?.invoke(),
|
|
||||||
tue?.invoke(),
|
|
||||||
wen?.invoke(),
|
|
||||||
thu?.invoke(),
|
|
||||||
fry?.invoke(),
|
|
||||||
sat?.invoke(),
|
|
||||||
sun?.invoke(),
|
|
||||||
)
|
|
||||||
|
|
||||||
private fun month(
|
|
||||||
month: Int,
|
|
||||||
vararg weeks: List<YearSummaryDayUio>,
|
|
||||||
): YearSummaryMonthUio {
|
|
||||||
day = 1
|
|
||||||
return YearSummaryMonthUio(
|
|
||||||
date = java.util.Calendar.getInstance().apply {
|
|
||||||
set(java.util.Calendar.YEAR, 2025)
|
|
||||||
set(java.util.Calendar.MONTH, month)
|
|
||||||
}.time,
|
|
||||||
weeks = weeks.toList(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val values: Sequence<YearSummaryMonthUio>
|
|
||||||
get() = sequenceOf(
|
|
||||||
month(
|
|
||||||
month = Calendar.MARCH,
|
|
||||||
week(
|
|
||||||
mon = null,
|
|
||||||
tue = null,
|
|
||||||
wen = null,
|
|
||||||
thu = null,
|
|
||||||
fry = null,
|
|
||||||
sat = headache(spifen = true)
|
|
||||||
),
|
|
||||||
week(sat = headache(spifen = true)),
|
|
||||||
week(
|
|
||||||
wen = headache(spifen = true, eletriptan = true),
|
|
||||||
thu = headache(spifen = true, eletriptan = true),
|
|
||||||
fry = headache(spifen = true, eletriptan = true),
|
|
||||||
sat = headache(),
|
|
||||||
sun = headache(spifen = true, eletriptan = true)
|
|
||||||
),
|
|
||||||
week(
|
|
||||||
mon = headache(),
|
|
||||||
tue = headache(),
|
|
||||||
sat = headache(spifen = true, eletriptan = true),
|
|
||||||
),
|
|
||||||
week(thu = headache(), fry = headache(), sat = headache()),
|
|
||||||
week(
|
|
||||||
mon = headache(spifen = true, eletriptan = true),
|
|
||||||
tue = null, wen = null, thu = null, fry = null, sat = null, sun = null
|
|
||||||
),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = java.util.Calendar.JULY,
|
|
||||||
week(mon = null, sun = headache(spifen = true)),
|
|
||||||
week(wen = headache(spifen = true)),
|
|
||||||
week(sat = headache()),
|
|
||||||
week(
|
|
||||||
wen = headache(spifen = true),
|
|
||||||
thu = headache(),
|
|
||||||
sat = headache(spifen = true),
|
|
||||||
),
|
|
||||||
week(wen = headache(), fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = java.util.Calendar.AUGUST,
|
|
||||||
week(mon = null, tue = null, wen = null, thu = null),
|
|
||||||
week(wen = headache(), fry = headache()),
|
|
||||||
week(fry = headache(spifen = true)),
|
|
||||||
week(tue = headache(spifen = true), fry = headache()),
|
|
||||||
week(
|
|
||||||
tue = headache(spifen = true, eletriptan = true),
|
|
||||||
wen = headache(),
|
|
||||||
sat = headache(spifen = true),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
month(
|
|
||||||
month = java.util.Calendar.SEPTEMBER,
|
|
||||||
week(
|
|
||||||
tue = headache(spifen = true, eletriptan = true),
|
|
||||||
wen = headache(spifen = true),
|
|
||||||
fry = headache(),
|
|
||||||
sat = headache(),
|
|
||||||
sun = headache(),
|
|
||||||
),
|
|
||||||
week(
|
|
||||||
sun = headache(spifen = true),
|
|
||||||
),
|
|
||||||
week(
|
|
||||||
wen = headache(spifen = true),
|
|
||||||
tue = headache(),
|
|
||||||
),
|
|
||||||
week(),
|
|
||||||
week(wen = null, thu = null, fry = null, sat = null, sun = null),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
package com.pixelized.headache.ui.page.summary.year
|
package com.pixelized.headache.ui.page.summary.yearly
|
||||||
|
|
||||||
import android.icu.util.Calendar
|
import android.icu.util.Calendar
|
||||||
import com.pixelized.headache.repository.event.Event
|
import com.pixelized.headache.repository.event.Event
|
||||||
import com.pixelized.headache.ui.page.summary.year.item.YearSummaryDayUio
|
import com.pixelized.headache.ui.page.summary.yearly.YearSummaryMonthUio.DayUio
|
||||||
import com.pixelized.headache.ui.page.summary.year.item.YearSummaryMonthUio
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
class YearSummaryFactory @Inject constructor() {
|
class YearSummaryFactory @Inject constructor() {
|
||||||
|
|
||||||
fun convertToUio(
|
fun convertToUio(
|
||||||
|
|
@ -70,7 +68,7 @@ class YearSummaryFactory @Inject constructor() {
|
||||||
val initial = MutableList(
|
val initial = MutableList(
|
||||||
size = monthLastDayCalendar.get(Calendar.WEEK_OF_MONTH),
|
size = monthLastDayCalendar.get(Calendar.WEEK_OF_MONTH),
|
||||||
) {
|
) {
|
||||||
mutableListOf<YearSummaryDayUio>()
|
mutableListOf<DayUio>()
|
||||||
}
|
}
|
||||||
val weeks = (1..monthLastDayCalendar.get(Calendar.DAY_OF_MONTH))
|
val weeks = (1..monthLastDayCalendar.get(Calendar.DAY_OF_MONTH))
|
||||||
.fold(initial = initial) { accumulator, dayNumber ->
|
.fold(initial = initial) { accumulator, dayNumber ->
|
||||||
|
|
@ -83,7 +81,7 @@ class YearSummaryFactory @Inject constructor() {
|
||||||
set(Calendar.MILLISECONDS_IN_DAY, 1)
|
set(Calendar.MILLISECONDS_IN_DAY, 1)
|
||||||
}
|
}
|
||||||
.get(Calendar.WEEK_OF_MONTH) - 1
|
.get(Calendar.WEEK_OF_MONTH) - 1
|
||||||
val day = YearSummaryDayUio(
|
val day = DayUio(
|
||||||
number = dayNumber,
|
number = dayNumber,
|
||||||
headache = event != null,
|
headache = event != null,
|
||||||
pills = event?.pills?.map { it.color } ?: emptyList(),
|
pills = event?.pills?.map { it.color } ?: emptyList(),
|
||||||
|
|
@ -0,0 +1,346 @@
|
||||||
|
package com.pixelized.headache.ui.page.summary.yearly
|
||||||
|
|
||||||
|
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.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
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.draw.drawWithCache
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.text.TextMeasurer
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.drawText
|
||||||
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
|
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
|
||||||
|
import com.pixelized.headache.utils.extention.capitalize
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data object YearSummaryMonthDefault {
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val daySize: Dp = 16.dp
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val pillSize: Dp = 2.dp
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val pillSpace: Dp = 1.dp
|
||||||
|
|
||||||
|
@SuppressLint("ConstantLocale")
|
||||||
|
@Stable
|
||||||
|
val formatter = SimpleDateFormat("MMMM", Locale.getDefault())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class YearSummaryMonthUio(
|
||||||
|
val date: Date,
|
||||||
|
val weeks: List<List<DayUio>>,
|
||||||
|
) {
|
||||||
|
@Stable
|
||||||
|
data class DayUio(
|
||||||
|
val number: Int,
|
||||||
|
val headache: Boolean,
|
||||||
|
val pills: List<Color>,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun YearSummaryMonth(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
formatter: DateFormat = YearSummaryMonthDefault.formatter,
|
||||||
|
defaultTextStyle: TextStyle,
|
||||||
|
headacheTextStyle: TextStyle,
|
||||||
|
daySize: Dp = YearSummaryMonthDefault.daySize,
|
||||||
|
pillSize: Dp = YearSummaryMonthDefault.pillSize,
|
||||||
|
pillSpace: Dp = YearSummaryMonthDefault.pillSpace,
|
||||||
|
uio: YearSummaryMonthUio,
|
||||||
|
onMonth: (YearSummaryMonthUio) -> Unit,
|
||||||
|
) {
|
||||||
|
val textMeasurer: TextMeasurer = rememberTextMeasurer()
|
||||||
|
val pill: Float = with(LocalDensity.current) { pillSize.toPx() }
|
||||||
|
val space: Float = with(LocalDensity.current) { pillSpace.toPx() }
|
||||||
|
|
||||||
|
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(),
|
||||||
|
)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(width = daySize * 7, height = daySize * 6)
|
||||||
|
.drawWithCache {
|
||||||
|
val cellSize = Size(
|
||||||
|
width = size.width / 7f,
|
||||||
|
height = size.height / 6f,
|
||||||
|
)
|
||||||
|
val pillSize = Size(
|
||||||
|
width = pill,
|
||||||
|
height = pill,
|
||||||
|
)
|
||||||
|
|
||||||
|
onDrawBehind {
|
||||||
|
var localCellOffset: Offset = Offset.Zero
|
||||||
|
var localCellSize: Size = Size.Zero
|
||||||
|
|
||||||
|
uio.weeks.forEachIndexed { row, week ->
|
||||||
|
week.forEachIndexed { column, day ->
|
||||||
|
// Compute current cell offset and size
|
||||||
|
localCellOffset = Offset(
|
||||||
|
x = when (row) {
|
||||||
|
0 -> cellSize.width * (column + 7 - week.size)
|
||||||
|
else -> cellSize.width * column
|
||||||
|
},
|
||||||
|
y = cellSize.height * row,
|
||||||
|
)
|
||||||
|
localCellSize = Size(
|
||||||
|
width = cellSize.width,
|
||||||
|
height = cellSize.height,
|
||||||
|
)
|
||||||
|
|
||||||
|
// draw background
|
||||||
|
if (day.headache) {
|
||||||
|
drawRect(
|
||||||
|
topLeft = Offset(
|
||||||
|
x = localCellOffset.x - 1f,
|
||||||
|
y = localCellOffset.y + pillSize.height + space,
|
||||||
|
),
|
||||||
|
size = Size(
|
||||||
|
width = localCellSize.width - 0f,
|
||||||
|
height = localCellSize.height - (pillSize.height + space) * 2,
|
||||||
|
),
|
||||||
|
color = HeadacheColorPalette.Pill.Unknown,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw day number
|
||||||
|
val label = "${day.number}"
|
||||||
|
val measure = textMeasurer.measure(label, defaultTextStyle)
|
||||||
|
|
||||||
|
drawText(
|
||||||
|
textMeasurer = textMeasurer,
|
||||||
|
text = label,
|
||||||
|
style = when {
|
||||||
|
day.headache -> headacheTextStyle
|
||||||
|
else -> defaultTextStyle
|
||||||
|
},
|
||||||
|
topLeft = Offset(
|
||||||
|
x = localCellOffset.x + (localCellSize.width - measure.size.width) / 2,
|
||||||
|
y = localCellOffset.y + (localCellSize.height - measure.size.height) / 2,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// draw pills
|
||||||
|
day.pills.forEachIndexed { index, pill ->
|
||||||
|
drawRect(
|
||||||
|
topLeft = Offset(
|
||||||
|
x = localCellOffset.x + (localCellSize.width - (pillSize.width + space) * day.pills.size) / 2f + index * (pillSize.width + space),
|
||||||
|
y = localCellOffset.y + localCellSize.height - pillSize.height,
|
||||||
|
),
|
||||||
|
size = pillSize,
|
||||||
|
color = pill
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview
|
||||||
|
private fun YearSummaryMonthPreview(
|
||||||
|
@PreviewParameter(MonthPreviewProvider::class) preview: YearSummaryMonthUio,
|
||||||
|
) {
|
||||||
|
HeadacheTheme {
|
||||||
|
Surface {
|
||||||
|
YearSummaryMonth(
|
||||||
|
modifier = Modifier.padding(all = 8.dp),
|
||||||
|
defaultTextStyle = MaterialTheme.typography.labelSmall,
|
||||||
|
headacheTextStyle = MaterialTheme.typography.labelSmall.copy(color = Color.White),
|
||||||
|
uio = preview,
|
||||||
|
onMonth = { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object MonthPreviewProviderHelper {
|
||||||
|
private var day: Int = 1
|
||||||
|
|
||||||
|
fun day(): () -> YearSummaryMonthUio.DayUio = {
|
||||||
|
YearSummaryMonthUio.DayUio(
|
||||||
|
number = day++,
|
||||||
|
headache = false,
|
||||||
|
pills = emptyList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun headache(
|
||||||
|
spifen: Boolean = false,
|
||||||
|
eletriptan: Boolean = false,
|
||||||
|
): () -> YearSummaryMonthUio.DayUio = {
|
||||||
|
YearSummaryMonthUio.DayUio(
|
||||||
|
number = day++,
|
||||||
|
headache = true,
|
||||||
|
pills = listOfNotNull(
|
||||||
|
if (spifen) HeadacheColorPalette.Pill.Spifen400 else null,
|
||||||
|
if (eletriptan) HeadacheColorPalette.Pill.Eletriptan40 else null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// inline to avoid un sequential call to "day()" between default and overridden function parameters.
|
||||||
|
@Suppress("NOTHING_TO_INLINE")
|
||||||
|
fun week(
|
||||||
|
mon: (() -> YearSummaryMonthUio.DayUio)? = day(),
|
||||||
|
tue: (() -> YearSummaryMonthUio.DayUio)? = day(),
|
||||||
|
wen: (() -> YearSummaryMonthUio.DayUio)? = day(),
|
||||||
|
thu: (() -> YearSummaryMonthUio.DayUio)? = day(),
|
||||||
|
fry: (() -> YearSummaryMonthUio.DayUio)? = day(),
|
||||||
|
sat: (() -> YearSummaryMonthUio.DayUio)? = day(),
|
||||||
|
sun: (() -> YearSummaryMonthUio.DayUio)? = day(),
|
||||||
|
): List<YearSummaryMonthUio.DayUio> = listOfNotNull(
|
||||||
|
mon?.invoke(),
|
||||||
|
tue?.invoke(),
|
||||||
|
wen?.invoke(),
|
||||||
|
thu?.invoke(),
|
||||||
|
fry?.invoke(),
|
||||||
|
sat?.invoke(),
|
||||||
|
sun?.invoke(),
|
||||||
|
)
|
||||||
|
|
||||||
|
fun month(
|
||||||
|
month: Int,
|
||||||
|
year: Int,
|
||||||
|
vararg weeks: List<YearSummaryMonthUio.DayUio>,
|
||||||
|
): YearSummaryMonthUio {
|
||||||
|
day = 1
|
||||||
|
return YearSummaryMonthUio(
|
||||||
|
date = java.util.Calendar.getInstance().apply {
|
||||||
|
set(java.util.Calendar.YEAR, year)
|
||||||
|
set(java.util.Calendar.MONTH, month)
|
||||||
|
}.time,
|
||||||
|
weeks = weeks.toList(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MonthPreviewProvider() : PreviewParameterProvider<YearSummaryMonthUio> {
|
||||||
|
override val values: Sequence<YearSummaryMonthUio>
|
||||||
|
get() = with(MonthPreviewProviderHelper) {
|
||||||
|
sequenceOf(
|
||||||
|
month(
|
||||||
|
month = Calendar.MARCH,
|
||||||
|
year = 2025,
|
||||||
|
week(
|
||||||
|
mon = null,
|
||||||
|
tue = null,
|
||||||
|
wen = null,
|
||||||
|
thu = null,
|
||||||
|
fry = null,
|
||||||
|
sat = headache(spifen = true)
|
||||||
|
),
|
||||||
|
week(sat = headache(spifen = true)),
|
||||||
|
week(
|
||||||
|
wen = headache(spifen = true, eletriptan = true),
|
||||||
|
thu = headache(spifen = true, eletriptan = true),
|
||||||
|
fry = headache(spifen = true, eletriptan = true),
|
||||||
|
sat = headache(),
|
||||||
|
sun = headache(spifen = true, eletriptan = true)
|
||||||
|
),
|
||||||
|
week(
|
||||||
|
mon = headache(),
|
||||||
|
tue = headache(),
|
||||||
|
sat = headache(spifen = true, eletriptan = true),
|
||||||
|
),
|
||||||
|
week(thu = headache(), fry = headache(), sat = headache()),
|
||||||
|
week(
|
||||||
|
mon = headache(spifen = true, eletriptan = true),
|
||||||
|
tue = null, wen = null, thu = null, fry = null, sat = null, sun = null
|
||||||
|
),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = java.util.Calendar.JULY,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, sun = headache(spifen = true)),
|
||||||
|
week(wen = headache(spifen = true)),
|
||||||
|
week(sat = headache()),
|
||||||
|
week(
|
||||||
|
wen = headache(spifen = true),
|
||||||
|
thu = headache(),
|
||||||
|
sat = headache(spifen = true),
|
||||||
|
),
|
||||||
|
week(wen = headache(), fry = null, sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = java.util.Calendar.AUGUST,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, tue = null, wen = null, thu = null),
|
||||||
|
week(wen = headache(), fry = headache()),
|
||||||
|
week(fry = headache(spifen = true)),
|
||||||
|
week(tue = headache(spifen = true), fry = headache()),
|
||||||
|
week(
|
||||||
|
tue = headache(spifen = true, eletriptan = true),
|
||||||
|
wen = headache(),
|
||||||
|
sat = headache(spifen = true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = java.util.Calendar.SEPTEMBER,
|
||||||
|
year = 2025,
|
||||||
|
week(
|
||||||
|
tue = headache(spifen = true, eletriptan = true),
|
||||||
|
wen = headache(spifen = true),
|
||||||
|
fry = headache(),
|
||||||
|
sat = headache(),
|
||||||
|
sun = headache(),
|
||||||
|
),
|
||||||
|
week(
|
||||||
|
sun = headache(spifen = true),
|
||||||
|
),
|
||||||
|
week(
|
||||||
|
wen = headache(spifen = true),
|
||||||
|
tue = headache(),
|
||||||
|
),
|
||||||
|
week(),
|
||||||
|
week(wen = null, thu = null, fry = null, sat = null, sun = null),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,477 @@
|
||||||
|
package com.pixelized.headache.ui.page.summary.yearly
|
||||||
|
|
||||||
|
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.keepScreenOn
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.platform.LocalWindowInfo
|
||||||
|
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.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.theme.HeadacheTheme
|
||||||
|
import com.pixelized.headache.ui.theme.headache
|
||||||
|
import com.pixelized.headache.utils.extention.calculate
|
||||||
|
import java.util.Calendar
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class YearUio(
|
||||||
|
val year: Int,
|
||||||
|
val months: List<YearSummaryMonthUio>,
|
||||||
|
)
|
||||||
|
|
||||||
|
@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()
|
||||||
|
.keepScreenOn()
|
||||||
|
.fillMaxSize(),
|
||||||
|
uio = uio,
|
||||||
|
onBack = {
|
||||||
|
navigation.popBackstack()
|
||||||
|
},
|
||||||
|
onMonth = {
|
||||||
|
navigation.navigateToEventPage(date = it.date)
|
||||||
|
},
|
||||||
|
onAddEvent = {
|
||||||
|
editViewModel.show()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
EventEditBottomSheet(
|
||||||
|
viewModel = editViewModel,
|
||||||
|
onDismissRequest = { editViewModel.dismiss() },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun rememberDaySize(
|
||||||
|
column: Int,
|
||||||
|
space: Dp,
|
||||||
|
paddingValues: PaddingValues,
|
||||||
|
): Dp {
|
||||||
|
val density = LocalDensity.current
|
||||||
|
val windowInfo = LocalWindowInfo.current
|
||||||
|
val screenWidth = remember(density, windowInfo) {
|
||||||
|
with(density) { windowInfo.containerSize.width.toDp() }
|
||||||
|
}
|
||||||
|
val (start, _, end, _) = paddingValues.calculate()
|
||||||
|
return remember {
|
||||||
|
(screenWidth - space * (column - 1) - start - end) / (7 * column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun YearSummaryContent(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
paddingValues: PaddingValues = PaddingValues(
|
||||||
|
start = 16.dp,
|
||||||
|
top = 16.dp,
|
||||||
|
end = 16.dp,
|
||||||
|
bottom = 16.dp + 16.dp + 56.dp,
|
||||||
|
),
|
||||||
|
space: Dp = 8.dp,
|
||||||
|
daySize: Dp = rememberDaySize(
|
||||||
|
column = 3,
|
||||||
|
paddingValues = paddingValues,
|
||||||
|
space = space,
|
||||||
|
),
|
||||||
|
uio: State<List<YearUio>>,
|
||||||
|
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 = { it ->
|
||||||
|
val typography = MaterialTheme.typography
|
||||||
|
val colorScheme = MaterialTheme.headache.colorScheme
|
||||||
|
val defaultTextStyle = remember(typography, colorScheme) {
|
||||||
|
typography.labelSmall.copy(color = colorScheme.base.onSurface)
|
||||||
|
}
|
||||||
|
val headacheTextStyle = remember(typography, colorScheme) {
|
||||||
|
defaultTextStyle.copy(color = colorScheme.calendar.onHeadache)
|
||||||
|
}
|
||||||
|
LazyVerticalGrid(
|
||||||
|
modifier = Modifier.padding(paddingValues = it),
|
||||||
|
columns = GridCells.Adaptive(minSize = daySize * 7),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(space = space),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = space),
|
||||||
|
contentPadding = paddingValues,
|
||||||
|
) {
|
||||||
|
uio.value.forEachIndexed { index, (year, months) ->
|
||||||
|
item(
|
||||||
|
span = { GridItemSpan(maxLineSpan) },
|
||||||
|
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,
|
||||||
|
defaultTextStyle = defaultTextStyle,
|
||||||
|
headacheTextStyle = headacheTextStyle,
|
||||||
|
daySize = daySize,
|
||||||
|
onMonth = onMonth,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Preview()
|
||||||
|
private fun YearSummaryPreview(
|
||||||
|
@PreviewParameter(YearPreviewProvider::class) preview: List<YearUio>,
|
||||||
|
) {
|
||||||
|
HeadacheTheme {
|
||||||
|
Surface {
|
||||||
|
YearSummaryContent(
|
||||||
|
uio = remember { mutableStateOf(preview) },
|
||||||
|
onBack = { },
|
||||||
|
onMonth = { },
|
||||||
|
onAddEvent = { },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class YearPreviewProvider() : PreviewParameterProvider<List<YearUio>> {
|
||||||
|
|
||||||
|
private fun year(
|
||||||
|
year: Int,
|
||||||
|
vararg months: YearSummaryMonthUio,
|
||||||
|
) = YearUio(
|
||||||
|
year = year,
|
||||||
|
months = months.toList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
override val values: Sequence<List<YearUio>>
|
||||||
|
get() = with(MonthPreviewProviderHelper) {
|
||||||
|
sequenceOf(
|
||||||
|
listOf(
|
||||||
|
year(
|
||||||
|
year = 2025,
|
||||||
|
month(
|
||||||
|
month = Calendar.JANUARY,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, tue = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.FEBRUARY,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.MARCH,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(
|
||||||
|
tue = null,
|
||||||
|
wen = null,
|
||||||
|
thu = null,
|
||||||
|
fry = null,
|
||||||
|
sat = null,
|
||||||
|
sun = null
|
||||||
|
),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.APRIL,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(thu = null, fry = null, sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.MAY,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, tue = null, wen = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.JUNE,
|
||||||
|
year = 2025,
|
||||||
|
week(
|
||||||
|
mon = null,
|
||||||
|
tue = null,
|
||||||
|
wen = null,
|
||||||
|
thu = null,
|
||||||
|
fry = null,
|
||||||
|
sat = null
|
||||||
|
),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(
|
||||||
|
tue = null,
|
||||||
|
wen = null,
|
||||||
|
thu = null,
|
||||||
|
fry = null,
|
||||||
|
sat = null,
|
||||||
|
sun = null
|
||||||
|
),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.JULY,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, sun = headache(spifen = true)),
|
||||||
|
week(wen = headache(spifen = true)),
|
||||||
|
week(sat = headache()),
|
||||||
|
week(
|
||||||
|
wen = headache(spifen = true),
|
||||||
|
thu = headache(),
|
||||||
|
sat = headache(spifen = true),
|
||||||
|
),
|
||||||
|
week(wen = headache(), fry = null, sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.AUGUST,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, tue = null, wen = null, thu = null),
|
||||||
|
week(wen = headache(), fry = headache()),
|
||||||
|
week(fry = headache(spifen = true)),
|
||||||
|
week(tue = headache(spifen = true), fry = headache()),
|
||||||
|
week(
|
||||||
|
tue = headache(spifen = true, eletriptan = true),
|
||||||
|
wen = headache(),
|
||||||
|
sat = headache(spifen = true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.SEPTEMBER,
|
||||||
|
year = 2025,
|
||||||
|
week(
|
||||||
|
tue = headache(spifen = true, eletriptan = true),
|
||||||
|
wen = headache(spifen = true),
|
||||||
|
fry = headache(),
|
||||||
|
sat = headache(),
|
||||||
|
sun = headache(),
|
||||||
|
),
|
||||||
|
week(
|
||||||
|
sun = headache(spifen = true),
|
||||||
|
),
|
||||||
|
week(
|
||||||
|
wen = headache(spifen = true),
|
||||||
|
tue = headache(),
|
||||||
|
),
|
||||||
|
week(),
|
||||||
|
week(wen = null, thu = null, fry = null, sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.OCTOBER,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, tue = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.NOVEMBER,
|
||||||
|
year = 2025,
|
||||||
|
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.DECEMBER,
|
||||||
|
year = 2025,
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(thu = null, fry = null, sat = null, sun = null),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
year(
|
||||||
|
year = 2024,
|
||||||
|
month(
|
||||||
|
month = Calendar.JANUARY,
|
||||||
|
year = 2024,
|
||||||
|
week(mon = null, tue = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.FEBRUARY,
|
||||||
|
year = 2024,
|
||||||
|
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.MARCH,
|
||||||
|
year = 2024,
|
||||||
|
week(mon = null, tue = null, wen = null, thu = null, fry = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(
|
||||||
|
tue = null,
|
||||||
|
wen = null,
|
||||||
|
thu = null,
|
||||||
|
fry = null,
|
||||||
|
sat = null,
|
||||||
|
sun = null
|
||||||
|
),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.APRIL,
|
||||||
|
year = 2024,
|
||||||
|
week(mon = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(thu = null, fry = null, sat = null, sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.MAY,
|
||||||
|
year = 2024,
|
||||||
|
week(mon = null, tue = null, wen = null),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(sun = null),
|
||||||
|
),
|
||||||
|
month(
|
||||||
|
month = Calendar.JUNE,
|
||||||
|
year = 2024,
|
||||||
|
week(
|
||||||
|
mon = null,
|
||||||
|
tue = null,
|
||||||
|
wen = null,
|
||||||
|
thu = null,
|
||||||
|
fry = null,
|
||||||
|
sat = null
|
||||||
|
),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(),
|
||||||
|
week(
|
||||||
|
tue = null,
|
||||||
|
wen = null,
|
||||||
|
thu = null,
|
||||||
|
fry = null,
|
||||||
|
sat = null,
|
||||||
|
sun = null
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.pixelized.headache.ui.page.summary.year
|
package com.pixelized.headache.ui.page.summary.yearly
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
|
@ -3,6 +3,7 @@ package com.pixelized.headache.ui.theme
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.ReadOnlyComposable
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
|
@ -10,15 +11,15 @@ import com.pixelized.headache.ui.theme.color.HeadacheColors
|
||||||
import com.pixelized.headache.ui.theme.color.headacheDarkColorScheme
|
import com.pixelized.headache.ui.theme.color.headacheDarkColorScheme
|
||||||
import com.pixelized.headache.ui.theme.color.headacheLightColorScheme
|
import com.pixelized.headache.ui.theme.color.headacheLightColorScheme
|
||||||
|
|
||||||
val LocalLwaTheme = compositionLocalOf<HeadacheTheme> {
|
val LocalHeadacheTheme = compositionLocalOf<HeadacheTheme> {
|
||||||
error("Local Snack Controller is not yet ready")
|
error("Local Theme Controller is not yet ready")
|
||||||
}
|
}
|
||||||
|
|
||||||
val MaterialTheme.headache: HeadacheTheme
|
val MaterialTheme.headache: HeadacheTheme
|
||||||
@Stable
|
@Stable
|
||||||
@Composable
|
@Composable
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
get() = LocalLwaTheme.current
|
get() = LocalHeadacheTheme.current
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class HeadacheTheme(
|
data class HeadacheTheme(
|
||||||
|
|
@ -30,14 +31,19 @@ fun HeadacheTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val colorScheme = when {
|
val theme = HeadacheTheme(
|
||||||
darkTheme -> headacheDarkColorScheme()
|
colorScheme = when {
|
||||||
else -> headacheLightColorScheme()
|
darkTheme -> headacheDarkColorScheme()
|
||||||
}
|
else -> headacheLightColorScheme()
|
||||||
|
}
|
||||||
MaterialTheme(
|
|
||||||
colorScheme = colorScheme.base,
|
|
||||||
typography = Typography,
|
|
||||||
content = content
|
|
||||||
)
|
)
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalHeadacheTheme provides theme,
|
||||||
|
) {
|
||||||
|
MaterialTheme(
|
||||||
|
colorScheme = theme.colorScheme.base,
|
||||||
|
typography = Typography,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -16,25 +16,67 @@ import kotlin.math.ln
|
||||||
@Stable
|
@Stable
|
||||||
data class HeadacheColors(
|
data class HeadacheColors(
|
||||||
val base: ColorScheme,
|
val base: ColorScheme,
|
||||||
)
|
val calendar: Calendar,
|
||||||
|
val pill: Pill,
|
||||||
|
) {
|
||||||
|
@Stable
|
||||||
|
data class Calendar(
|
||||||
|
val headache: Color,
|
||||||
|
val onHeadache: Color,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class Pill(
|
||||||
|
val unknown: Color,
|
||||||
|
val ibuprofene400: Color,
|
||||||
|
val paracetamol1000: Color,
|
||||||
|
val spifen400: Color,
|
||||||
|
val eletriptan40: Color,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Stable
|
@Stable
|
||||||
fun headacheDarkColorScheme(
|
fun headacheDarkColorScheme(
|
||||||
base: ColorScheme = darkColorScheme(),
|
base: ColorScheme = darkColorScheme(),
|
||||||
|
calendar: HeadacheColors.Calendar = HeadacheColors.Calendar(
|
||||||
|
headache = HeadacheColorPalette.Additional.Red,
|
||||||
|
onHeadache = Color.White,
|
||||||
|
),
|
||||||
|
pill: HeadacheColors.Pill = HeadacheColors.Pill(
|
||||||
|
unknown = HeadacheColorPalette.Pill.Unknown,
|
||||||
|
ibuprofene400 = HeadacheColorPalette.Pill.Ibuprofene400,
|
||||||
|
paracetamol1000 = HeadacheColorPalette.Pill.Paracetamol1000,
|
||||||
|
spifen400 = HeadacheColorPalette.Pill.Spifen400,
|
||||||
|
eletriptan40 = HeadacheColorPalette.Pill.Eletriptan40,
|
||||||
|
),
|
||||||
) = HeadacheColors(
|
) = HeadacheColors(
|
||||||
base = base,
|
base = base,
|
||||||
|
calendar = calendar,
|
||||||
|
pill = pill,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@Stable
|
@Stable
|
||||||
fun headacheLightColorScheme(
|
fun headacheLightColorScheme(
|
||||||
base: ColorScheme = lightColorScheme(),
|
base: ColorScheme = lightColorScheme(),
|
||||||
|
calendar: HeadacheColors.Calendar = HeadacheColors.Calendar(
|
||||||
|
headache = HeadacheColorPalette.Additional.Red,
|
||||||
|
onHeadache = Color.White,
|
||||||
|
),
|
||||||
|
pill: HeadacheColors.Pill = HeadacheColors.Pill(
|
||||||
|
unknown = HeadacheColorPalette.Pill.Unknown,
|
||||||
|
ibuprofene400 = HeadacheColorPalette.Pill.Ibuprofene400,
|
||||||
|
paracetamol1000 = HeadacheColorPalette.Pill.Paracetamol1000,
|
||||||
|
spifen400 = HeadacheColorPalette.Pill.Spifen400,
|
||||||
|
eletriptan40 = HeadacheColorPalette.Pill.Eletriptan40,
|
||||||
|
),
|
||||||
) = HeadacheColors(
|
) = HeadacheColors(
|
||||||
base = base,
|
base = base,
|
||||||
|
calendar = calendar,
|
||||||
|
pill = pill,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
@Composable
|
@Composable
|
||||||
fun calculateElevatedColor(
|
fun calculateElevatedColor(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue