Optimize the month cell display in the year summary.
This commit is contained in:
parent
7d2508a4de
commit
6bb58e06c0
42 changed files with 1266 additions and 1119 deletions
Binary file not shown.
Binary file not shown.
|
|
@ -10,7 +10,8 @@ import androidx.activity.enableEdgeToEdge
|
|||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.pixelized.headache.ui.navigation.Navigator
|
||||
import com.pixelized.headache.ui.navigation.home.HomeNavigator
|
||||
import com.pixelized.headache.ui.navigation.main.MainNavigator
|
||||
import com.pixelized.headache.ui.page.MainPage
|
||||
import com.pixelized.headache.ui.theme.HeadacheTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
|
@ -20,7 +21,10 @@ import javax.inject.Inject
|
|||
class MainActivity : ComponentActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var navigator: Navigator
|
||||
lateinit var mainNavigator: MainNavigator
|
||||
|
||||
@Inject
|
||||
lateinit var homeNavigator: HomeNavigator
|
||||
|
||||
private val requestPermissionLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission()
|
||||
|
|
@ -41,7 +45,8 @@ class MainActivity : ComponentActivity() {
|
|||
HeadacheTheme {
|
||||
Surface {
|
||||
MainPage(
|
||||
navigator = navigator,
|
||||
mainNavigator = mainNavigator,
|
||||
homeNavigator = homeNavigator,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.pixelized.headache.module
|
||||
|
||||
import com.pixelized.headache.ui.navigation.Navigator
|
||||
import com.pixelized.headache.ui.navigation.destination.HomePageDestination
|
||||
import com.pixelized.headache.ui.navigation.destination.YearSummaryDestination
|
||||
import com.pixelized.headache.ui.navigation.home.HomeNavigator
|
||||
import com.pixelized.headache.ui.navigation.main.MainNavigator
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -14,5 +16,13 @@ object AppModule {
|
|||
|
||||
@Provides
|
||||
@ActivityRetainedScoped
|
||||
fun provideNavigator(): Navigator = Navigator(startDestination = HomePageDestination)
|
||||
fun provideMainNavigator(): MainNavigator {
|
||||
return MainNavigator(startDestination = HomePageDestination)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ActivityRetainedScoped
|
||||
fun provideHomeNavigator(): HomeNavigator {
|
||||
return HomeNavigator(startDestination = YearSummaryDestination)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,11 @@ class EventRepository @Inject constructor(
|
|||
) {
|
||||
private val timeZone = TimeZone.getTimeZone("UTC")
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
||||
// Main flow of event. Store event in a map by Id
|
||||
private val eventFlow = MutableStateFlow(mapOf<Long, Event>())
|
||||
|
||||
// Collectable data. Provide events in a sorted list by dates.
|
||||
private val eventListFlow: StateFlow<Collection<Event>> = eventFlow
|
||||
.map { events -> events.values.sortedBy { it.date } }
|
||||
.stateIn(
|
||||
|
|
@ -43,6 +46,38 @@ class EventRepository @Inject constructor(
|
|||
initialValue = emptyList(),
|
||||
)
|
||||
|
||||
// Collectable data. Provide event data in a nested map struct of year, month, day.
|
||||
private val eventMapFlow: StateFlow<Map<Int, Map<Int, Map<Int, Event>>>> = eventListFlow
|
||||
.map { events ->
|
||||
events.fold(initial = hashMapOf<Int, HashMap<Int, HashMap<Int, Event>>>()) { acc, event ->
|
||||
acc.also {
|
||||
val years = it.getOrPut(key = event.date.year) {
|
||||
hashMapOf(
|
||||
Calendar.JANUARY to hashMapOf(),
|
||||
Calendar.FEBRUARY to hashMapOf(),
|
||||
Calendar.MARCH to hashMapOf(),
|
||||
Calendar.APRIL to hashMapOf(),
|
||||
Calendar.MAY to hashMapOf(),
|
||||
Calendar.JUNE to hashMapOf(),
|
||||
Calendar.JULY to hashMapOf(),
|
||||
Calendar.AUGUST to hashMapOf(),
|
||||
Calendar.SEPTEMBER to hashMapOf(),
|
||||
Calendar.OCTOBER to hashMapOf(),
|
||||
Calendar.NOVEMBER to hashMapOf(),
|
||||
Calendar.DECEMBER to hashMapOf(),
|
||||
)
|
||||
}
|
||||
val months = years.getOrPut(key = event.date.month) { hashMapOf() }
|
||||
months[event.date.day] = event
|
||||
}
|
||||
}
|
||||
}
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Lazily,
|
||||
initialValue = emptyMap(),
|
||||
)
|
||||
|
||||
init {
|
||||
calendarIdRepository.calendarId
|
||||
.onEach { id ->
|
||||
|
|
@ -55,10 +90,10 @@ class EventRepository @Inject constructor(
|
|||
.launchIn(scope = scope)
|
||||
}
|
||||
|
||||
fun eventsFlow(): StateFlow<Map<Long, Event>> = eventFlow
|
||||
|
||||
fun eventsListFlow(): StateFlow<Collection<Event>> = eventListFlow
|
||||
|
||||
fun eventsMapFlow(): StateFlow<Map<Int, Map<Int, Map<Int, Event>>>> = eventMapFlow
|
||||
|
||||
fun event(id: Long): Event? = eventFlow.value.get(key = id)
|
||||
|
||||
fun createEvent(
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@ 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.navigation.main.MainDestination
|
||||
import com.pixelized.headache.ui.navigation.main.MainNavigator
|
||||
import com.pixelized.headache.ui.page.calendar.CalendarChooserPage
|
||||
|
||||
data object CalendarChooserDestination
|
||||
data object CalendarChooserDestination : MainDestination
|
||||
|
||||
fun EntryProviderBuilder<*>.calendarChooserDestinationEntry() {
|
||||
entry<CalendarChooserDestination> {
|
||||
|
|
@ -13,6 +14,6 @@ fun EntryProviderBuilder<*>.calendarChooserDestinationEntry() {
|
|||
}
|
||||
}
|
||||
|
||||
fun Navigator.navigateToCalendarChooserPage() {
|
||||
fun MainNavigator.navigateToCalendarChooserPage() {
|
||||
goTo(CalendarChooserDestination)
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||
import androidx.navigation3.runtime.EntryProviderBuilder
|
||||
import androidx.navigation3.runtime.entry
|
||||
import com.pixelized.headache.repository.event.Event
|
||||
import com.pixelized.headache.ui.navigation.Navigator
|
||||
import com.pixelized.headache.ui.navigation.main.MainDestination
|
||||
import com.pixelized.headache.ui.navigation.main.MainNavigator
|
||||
import com.pixelized.headache.ui.page.event.list.EventPage
|
||||
import com.pixelized.headache.ui.page.event.list.EventViewModel
|
||||
import com.pixelized.headache.utils.extention.event
|
||||
|
|
@ -15,7 +16,7 @@ data class EventDestination(
|
|||
val date: Event.Date?,
|
||||
val misspelledFilter: Boolean,
|
||||
val invalidFilter: Boolean,
|
||||
)
|
||||
) : MainDestination
|
||||
|
||||
fun EntryProviderBuilder<*>.eventDestinationEntry() {
|
||||
entry<EventDestination> { key ->
|
||||
|
|
@ -30,7 +31,7 @@ fun EntryProviderBuilder<*>.eventDestinationEntry() {
|
|||
}
|
||||
}
|
||||
|
||||
fun Navigator.navigateToEventPage(
|
||||
fun MainNavigator.navigateToEventPage(
|
||||
misspelledFilter: Boolean = false,
|
||||
invalidFilter: Boolean = false,
|
||||
date: Date? = null,
|
||||
|
|
|
|||
|
|
@ -2,17 +2,23 @@ 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.navigation.home.HomeNavigator
|
||||
import com.pixelized.headache.ui.navigation.main.MainDestination
|
||||
import com.pixelized.headache.ui.navigation.main.MainNavigator
|
||||
import com.pixelized.headache.ui.page.home.HomePage
|
||||
|
||||
data object HomePageDestination
|
||||
data object HomePageDestination : MainDestination
|
||||
|
||||
fun EntryProviderBuilder<*>.homeDestinationEntry() {
|
||||
fun EntryProviderBuilder<*>.homeDestinationEntry(
|
||||
navigator: HomeNavigator,
|
||||
) {
|
||||
entry<HomePageDestination> {
|
||||
HomePage()
|
||||
HomePage(
|
||||
navigator = navigator,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Navigator.navigateToHomePage() {
|
||||
fun MainNavigator.navigateToHomePage() {
|
||||
goTo(HomePageDestination)
|
||||
}
|
||||
|
|
@ -2,10 +2,11 @@ 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.month.MonthSummaryPage
|
||||
import com.pixelized.headache.ui.navigation.home.HomeDestination
|
||||
import com.pixelized.headache.ui.navigation.home.HomeNavigator
|
||||
import com.pixelized.headache.ui.page.summary.monthly.MonthSummaryPage
|
||||
|
||||
data object MonthSummaryDestination
|
||||
data object MonthSummaryDestination : HomeDestination
|
||||
|
||||
fun EntryProviderBuilder<*>.monthSummaryDestinationEntry() {
|
||||
entry<MonthSummaryDestination> {
|
||||
|
|
@ -13,6 +14,6 @@ fun EntryProviderBuilder<*>.monthSummaryDestinationEntry() {
|
|||
}
|
||||
}
|
||||
|
||||
fun Navigator.navigateToMonthSummary() {
|
||||
fun HomeNavigator.navigateToMonthSummary() {
|
||||
goTo(MonthSummaryDestination)
|
||||
}
|
||||
|
|
@ -2,11 +2,12 @@ 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
|
||||
import com.pixelized.headache.ui.navigation.home.HomeDestination
|
||||
import com.pixelized.headache.ui.navigation.home.HomeNavigator
|
||||
import com.pixelized.headache.ui.page.summary.yearly.YearSummaryPage
|
||||
|
||||
|
||||
data object YearSummaryDestination
|
||||
data object YearSummaryDestination : HomeDestination
|
||||
|
||||
fun EntryProviderBuilder<*>.yearSummaryDestinationEntry() {
|
||||
entry<YearSummaryDestination> {
|
||||
|
|
@ -14,6 +15,6 @@ fun EntryProviderBuilder<*>.yearSummaryDestinationEntry() {
|
|||
}
|
||||
}
|
||||
|
||||
fun Navigator.navigateToYearSummary() {
|
||||
fun HomeNavigator.navigateToYearSummary() {
|
||||
goTo(YearSummaryDestination)
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.pixelized.headache.ui.navigation.home
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
import androidx.navigation3.runtime.rememberSavedStateNavEntryDecorator
|
||||
import androidx.navigation3.ui.NavDisplay
|
||||
import androidx.navigation3.ui.rememberSceneSetupNavEntryDecorator
|
||||
import com.pixelized.headache.ui.navigation.destination.calendarChooserDestinationEntry
|
||||
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 LocalHomeNavigator = staticCompositionLocalOf<HomeNavigator> {
|
||||
error("Local Navigation no yet ready")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HomeNavDisplay(
|
||||
modifier: Modifier = Modifier,
|
||||
navigator: HomeNavigator,
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalHomeNavigator provides navigator,
|
||||
) {
|
||||
NavDisplay(
|
||||
modifier = modifier,
|
||||
backStack = navigator.backStack,
|
||||
entryDecorators = listOf(
|
||||
rememberSceneSetupNavEntryDecorator(),
|
||||
rememberSavedStateNavEntryDecorator(),
|
||||
rememberViewModelStoreNavEntryDecorator(),
|
||||
),
|
||||
onBack = {
|
||||
navigator.popBackstack()
|
||||
},
|
||||
entryProvider = entryProvider {
|
||||
monthSummaryDestinationEntry()
|
||||
yearSummaryDestinationEntry()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,20 @@
|
|||
package com.pixelized.headache.ui.navigation
|
||||
package com.pixelized.headache.ui.navigation.home
|
||||
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import dagger.hilt.android.scopes.ActivityRetainedScoped
|
||||
|
||||
interface HomeDestination
|
||||
|
||||
@ActivityRetainedScoped
|
||||
class Navigator(
|
||||
startDestination: Any,
|
||||
class HomeNavigator(
|
||||
startDestination: HomeDestination,
|
||||
) {
|
||||
val backStack: SnapshotStateList<Any> = mutableStateListOf(startDestination)
|
||||
|
||||
fun popBackstack() = backStack.removeLastOrNull()
|
||||
|
||||
fun goTo(destination: Any) {
|
||||
fun goTo(destination: HomeDestination) {
|
||||
backStack.add(destination)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.pixelized.headache.ui.navigation
|
||||
package com.pixelized.headache.ui.navigation.main
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
|
|
@ -14,32 +14,34 @@ 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
|
||||
import com.pixelized.headache.ui.navigation.home.HomeNavigator
|
||||
|
||||
val LocalNavigator = staticCompositionLocalOf<Navigator> {
|
||||
val LocalMainNavigator = staticCompositionLocalOf<MainNavigator> {
|
||||
error("Local Navigation no yet ready")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainNavDisplay(
|
||||
modifier: Modifier = Modifier,
|
||||
navigator: Navigator,
|
||||
mainNavigator: MainNavigator,
|
||||
homeNavigator: HomeNavigator,
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalNavigator provides navigator,
|
||||
LocalMainNavigator provides mainNavigator,
|
||||
) {
|
||||
NavDisplay(
|
||||
modifier = modifier,
|
||||
backStack = navigator.backStack,
|
||||
backStack = mainNavigator.backStack,
|
||||
entryDecorators = listOf(
|
||||
rememberSceneSetupNavEntryDecorator(),
|
||||
rememberSavedStateNavEntryDecorator(),
|
||||
rememberViewModelStoreNavEntryDecorator()
|
||||
rememberViewModelStoreNavEntryDecorator(),
|
||||
),
|
||||
onBack = {
|
||||
navigator.popBackstack()
|
||||
mainNavigator.popBackstack()
|
||||
},
|
||||
entryProvider = entryProvider {
|
||||
homeDestinationEntry()
|
||||
homeDestinationEntry(navigator = homeNavigator)
|
||||
calendarChooserDestinationEntry()
|
||||
eventDestinationEntry()
|
||||
monthSummaryDestinationEntry()
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.pixelized.headache.ui.navigation.main
|
||||
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import dagger.hilt.android.scopes.ActivityRetainedScoped
|
||||
|
||||
interface MainDestination
|
||||
|
||||
@ActivityRetainedScoped
|
||||
class MainNavigator(
|
||||
startDestination: MainDestination,
|
||||
) {
|
||||
val backStack: SnapshotStateList<Any> = mutableStateListOf(startDestination)
|
||||
|
||||
fun popBackstack() = backStack.removeLastOrNull()
|
||||
|
||||
fun goTo(destination: MainDestination) {
|
||||
backStack.add(destination)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,9 @@ import androidx.compose.runtime.CompositionLocalProvider
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.pixelized.headache.ui.navigation.MainNavDisplay
|
||||
import com.pixelized.headache.ui.navigation.Navigator
|
||||
import com.pixelized.headache.ui.navigation.home.HomeNavigator
|
||||
import com.pixelized.headache.ui.navigation.main.MainNavDisplay
|
||||
import com.pixelized.headache.ui.navigation.main.MainNavigator
|
||||
|
||||
val LocalSnack = staticCompositionLocalOf<SnackbarHostState> {
|
||||
error("Local SnackHost no yet ready")
|
||||
|
|
@ -25,7 +26,8 @@ val LocalErrorSnack = staticCompositionLocalOf<SnackbarHostState> {
|
|||
|
||||
@Composable
|
||||
fun MainPage(
|
||||
navigator: Navigator,
|
||||
mainNavigator: MainNavigator,
|
||||
homeNavigator: HomeNavigator,
|
||||
snack: SnackbarHostState = remember { SnackbarHostState() },
|
||||
errorSnack: SnackbarHostState = remember { SnackbarHostState() },
|
||||
) {
|
||||
|
|
@ -54,7 +56,8 @@ fun MainPage(
|
|||
content = { padding ->
|
||||
MainNavDisplay(
|
||||
modifier = Modifier.padding(paddingValues = padding),
|
||||
navigator = navigator,
|
||||
mainNavigator = mainNavigator,
|
||||
homeNavigator = homeNavigator,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ 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.main.LocalMainNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ import kotlinx.coroutines.launch
|
|||
fun CalendarChooserPage(
|
||||
viewModel: CalendarViewModel = hiltViewModel(),
|
||||
) {
|
||||
val navigation = LocalNavigator.current
|
||||
val navigation = LocalMainNavigator.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val calendars = viewModel.calendars.collectAsStateWithLifecycle()
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ 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.main.LocalMainNavigator
|
||||
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
|
||||
|
|
@ -63,7 +63,7 @@ fun EventPage(
|
|||
viewModel: EventViewModel = hiltViewModel(),
|
||||
editViewModel: EventEditBottomSheetViewModel = hiltViewModel(),
|
||||
) {
|
||||
val navigation = LocalNavigator.current
|
||||
val navigation = LocalMainNavigator.current
|
||||
val events = viewModel.events.collectAsStateWithLifecycle()
|
||||
val misspelledFilter = viewModel.misspelledFilter.collectAsStateWithLifecycle()
|
||||
val invalidFilter = viewModel.invalidFilter.collectAsStateWithLifecycle()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package com.pixelized.headache.ui.page.event.list
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.headache.repository.calendar.GoogleCalendarIdRepository
|
||||
import com.pixelized.headache.repository.event.Event
|
||||
import com.pixelized.headache.repository.event.EventRepository
|
||||
import com.pixelized.headache.ui.navigation.destination.EventDestination
|
||||
|
|
|
|||
|
|
@ -1,54 +1,119 @@
|
|||
package com.pixelized.headache.ui.page.home
|
||||
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarDefaults
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.IntState
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.headache.R
|
||||
import com.pixelized.headache.ui.navigation.LocalNavigator
|
||||
import com.pixelized.headache.ui.navigation.destination.navigateToCalendarChooserPage
|
||||
import com.pixelized.headache.ui.navigation.destination.navigateToEventPage
|
||||
import com.pixelized.headache.ui.navigation.destination.navigateToMonthSummary
|
||||
import com.pixelized.headache.ui.navigation.destination.navigateToYearSummary
|
||||
import com.pixelized.headache.ui.navigation.home.HomeNavDisplay
|
||||
import com.pixelized.headache.ui.navigation.home.HomeNavigator
|
||||
import com.pixelized.headache.ui.page.event.edit.EventEditBottomSheet
|
||||
import com.pixelized.headache.ui.page.event.edit.EventEditBottomSheetViewModel
|
||||
|
||||
|
||||
@Stable
|
||||
data class BottomBarItemUio(
|
||||
val selectedIcon: Int,
|
||||
val unselectedIcon: Int,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun HomePage() {
|
||||
val navigation = LocalNavigator.current
|
||||
fun HomePage(
|
||||
navigator: HomeNavigator,
|
||||
editViewModel: EventEditBottomSheetViewModel = hiltViewModel(),
|
||||
) {
|
||||
val selectedItem = remember { mutableIntStateOf(0) }
|
||||
val items = rememberBottomBarItems(
|
||||
onYearlyFollowUp = {
|
||||
selectedItem.intValue = 0
|
||||
navigator.navigateToYearSummary()
|
||||
},
|
||||
onMonthlyStatFollowUp = {
|
||||
selectedItem.intValue = 1
|
||||
navigator.navigateToMonthSummary()
|
||||
},
|
||||
onMonthlyListFollowUp = {
|
||||
selectedItem.intValue = 2
|
||||
},
|
||||
)
|
||||
|
||||
HomePageContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
onCalendarChooser = {
|
||||
navigation.navigateToCalendarChooserPage()
|
||||
navigator = navigator,
|
||||
items = items,
|
||||
selectedItem = selectedItem,
|
||||
onFabClick = {
|
||||
editViewModel.show()
|
||||
},
|
||||
onEvent = {
|
||||
navigation.navigateToEventPage()
|
||||
},
|
||||
onMonthSummary = {
|
||||
navigation.navigateToMonthSummary()
|
||||
},
|
||||
onYearSummary = {
|
||||
navigation.navigateToYearSummary()
|
||||
}
|
||||
)
|
||||
|
||||
EventEditBottomSheet(
|
||||
viewModel = editViewModel,
|
||||
onDismissRequest = { editViewModel.dismiss() },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberBottomBarItems(
|
||||
onYearlyFollowUp: () -> Unit,
|
||||
onMonthlyStatFollowUp: () -> Unit,
|
||||
onMonthlyListFollowUp: () -> Unit,
|
||||
): List<BottomBarItemUio> = remember {
|
||||
listOf(
|
||||
BottomBarItemUio(
|
||||
selectedIcon = R.drawable.ic_calendar_month_filled_24dp,
|
||||
unselectedIcon = R.drawable.ic_calendar_month_24dp,
|
||||
onClick = onYearlyFollowUp,
|
||||
),
|
||||
BottomBarItemUio(
|
||||
selectedIcon = R.drawable.ic_analytics_filled_24dp,
|
||||
unselectedIcon = R.drawable.ic_analytics_24dp,
|
||||
onClick = onMonthlyStatFollowUp,
|
||||
),
|
||||
BottomBarItemUio(
|
||||
selectedIcon = R.drawable.ic_list_alt_filled_24dp,
|
||||
unselectedIcon = R.drawable.ic_list_alt_24dp,
|
||||
onClick = onMonthlyListFollowUp,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -56,49 +121,57 @@ fun HomePage() {
|
|||
@Composable
|
||||
private fun HomePageContent(
|
||||
modifier: Modifier = Modifier,
|
||||
onCalendarChooser: () -> Unit,
|
||||
onEvent: () -> Unit,
|
||||
onMonthSummary: () -> Unit,
|
||||
onYearSummary: () -> Unit,
|
||||
navigator: HomeNavigator,
|
||||
items: List<BottomBarItemUio>,
|
||||
selectedItem: IntState,
|
||||
onFabClick: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(text = stringResource(R.string.app_name))
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(paddingValues = paddingValues),
|
||||
) {
|
||||
item {
|
||||
NavigationItem(
|
||||
label = stringResource(R.string.calendar_chooser_title),
|
||||
onClick = onCalendarChooser,
|
||||
)
|
||||
}
|
||||
item {
|
||||
NavigationItem(
|
||||
label = stringResource(R.string.event_title),
|
||||
onClick = onEvent,
|
||||
)
|
||||
}
|
||||
item {
|
||||
NavigationItem(
|
||||
label = stringResource(R.string.month_summary_title),
|
||||
onClick = onMonthSummary,
|
||||
)
|
||||
}
|
||||
item {
|
||||
NavigationItem(
|
||||
label = stringResource(R.string.year_summary_title),
|
||||
onClick = onYearSummary,
|
||||
contentWindowInsets = remember { WindowInsets(0, 0, 0, 0) },
|
||||
bottomBar = {
|
||||
NavigationBar {
|
||||
items.forEachIndexed { index, item ->
|
||||
NavigationBarItem(
|
||||
icon = {
|
||||
AnimatedContent(
|
||||
targetState = selectedItem.value,
|
||||
transitionSpec = { fadeIn() togetherWith fadeOut() },
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(
|
||||
when (it) {
|
||||
index -> item.selectedIcon
|
||||
else -> item.unselectedIcon
|
||||
}
|
||||
),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
selected = selectedItem.value == index,
|
||||
onClick = item.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
shape = CircleShape,
|
||||
onClick = onFabClick,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = { paddingValues ->
|
||||
HomeNavDisplay(
|
||||
modifier = Modifier.padding(paddingValues = paddingValues),
|
||||
navigator = navigator,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 androidx.compose.ui.graphics.Color
|
||||
import com.pixelized.headache.repository.event.Event
|
||||
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.ui.page.summary.monthly.item.MonthSummaryBoxUio
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryCell
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryItemUio
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryPillItemUio
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryTitleUio
|
||||
import com.pixelized.headache.utils.extention.event
|
||||
import javax.inject.Inject
|
||||
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.fadeIn
|
||||
|
|
@ -6,26 +6,23 @@ import androidx.compose.animation.fadeOut
|
|||
import androidx.compose.animation.togetherWith
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBarsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.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.automirrored.filled.Article
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.BarChart
|
||||
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.NavigationBarDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.State
|
||||
|
|
@ -41,18 +38,16 @@ 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.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.navigation.main.LocalMainNavigator
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryBox
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryBoxUio
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryCell
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryItem
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryItemUio
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryPillItemUio
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryTitle
|
||||
import com.pixelized.headache.ui.page.summary.monthly.item.MonthSummaryTitleUio
|
||||
import com.pixelized.headache.ui.theme.HeadacheTheme
|
||||
import com.pixelized.headache.ui.theme.color.HeadacheColorPalette
|
||||
import java.util.Date
|
||||
|
|
@ -60,7 +55,9 @@ import java.util.Date
|
|||
@Stable
|
||||
data object MonthSummaryPageDefault {
|
||||
@Stable
|
||||
val listPadding: PaddingValues = PaddingValues(bottom = 56.dp + 16.dp * 2)
|
||||
val listPadding: PaddingValues = PaddingValues(
|
||||
bottom = 16.dp + 16.dp + 56.dp,
|
||||
)
|
||||
|
||||
@Stable
|
||||
val spacing: Dp = 0.dp
|
||||
|
|
@ -70,36 +67,23 @@ data object MonthSummaryPageDefault {
|
|||
@Composable
|
||||
fun MonthSummaryPage(
|
||||
viewModel: MonthSummaryViewModel = hiltViewModel(),
|
||||
editViewModel: EventEditBottomSheetViewModel = hiltViewModel(),
|
||||
) {
|
||||
val navigation = LocalNavigator.current
|
||||
val navigation = LocalMainNavigator.current
|
||||
val boxMode = viewModel.boxMode.collectAsStateWithLifecycle()
|
||||
val events = viewModel.events.collectAsStateWithLifecycle()
|
||||
|
||||
MonthSummaryContent(
|
||||
modifier = Modifier
|
||||
.keepScreenOn()
|
||||
.systemBarsPadding()
|
||||
.fillMaxSize(),
|
||||
events = events,
|
||||
boxMode = boxMode,
|
||||
onBack = {
|
||||
navigation.popBackstack()
|
||||
},
|
||||
onDisplay = {
|
||||
viewModel.toggleDisplay()
|
||||
},
|
||||
onItem = {
|
||||
navigation.navigateToEventPage(date = it.date)
|
||||
},
|
||||
onAddEvent = {
|
||||
editViewModel.show()
|
||||
},
|
||||
)
|
||||
|
||||
EventEditBottomSheet(
|
||||
viewModel = editViewModel,
|
||||
onDismissRequest = { editViewModel.dismiss() },
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -111,25 +95,17 @@ private fun MonthSummaryContent(
|
|||
listPadding: PaddingValues = MonthSummaryPageDefault.listPadding,
|
||||
boxMode: State<Boolean>,
|
||||
events: State<Map<MonthSummaryCell, List<MonthSummaryCell>>>,
|
||||
onBack: () -> Unit,
|
||||
onDisplay: () -> Unit,
|
||||
onItem: (MonthSummaryCell) -> Unit,
|
||||
onAddEvent: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
contentWindowInsets = remember { WindowInsets(0, 0, 0, 0) },
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = onBack,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = NavigationBarDefaults.containerColor,
|
||||
),
|
||||
title = {
|
||||
Text(text = stringResource(R.string.month_summary_title))
|
||||
},
|
||||
|
|
@ -153,39 +129,28 @@ private fun MonthSummaryContent(
|
|||
}
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
shape = CircleShape,
|
||||
onClick = onAddEvent,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
},
|
||||
content = { paddingValues ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues = paddingValues)
|
||||
.fillMaxSize(),
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues = paddingValues),
|
||||
contentPadding = listPadding,
|
||||
verticalArrangement = Arrangement.spacedBy(space = spacing),
|
||||
reverseLayout = false,
|
||||
) {
|
||||
events.value.forEach { entry ->
|
||||
stickyHeader {
|
||||
item {
|
||||
MonthSummaryCell(
|
||||
modifier = Modifier.padding(top = 16.dp),
|
||||
item = entry.key,
|
||||
onItem = onItem,
|
||||
)
|
||||
}
|
||||
items(
|
||||
items = entry.value,
|
||||
key = { it.date },
|
||||
contentType = {
|
||||
when (it) {
|
||||
key = { item -> item.date },
|
||||
contentType = { item ->
|
||||
when (item) {
|
||||
is MonthSummaryBoxUio -> "MonthSummaryBoxUio"
|
||||
is MonthSummaryItemUio -> "MonthSummaryItemUio"
|
||||
is MonthSummaryTitleUio -> "MonthSummaryTitleUio"
|
||||
|
|
@ -205,11 +170,14 @@ private fun MonthSummaryContent(
|
|||
|
||||
@Composable
|
||||
private fun MonthSummaryCell(
|
||||
modifier: Modifier = Modifier,
|
||||
item: MonthSummaryCell,
|
||||
onItem: (MonthSummaryCell) -> Unit,
|
||||
) {
|
||||
AnimatedContent(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.then(other = modifier),
|
||||
targetState = item,
|
||||
transitionSpec = { fadeIn() togetherWith fadeOut() },
|
||||
) { item ->
|
||||
|
|
@ -272,10 +240,8 @@ private fun MonthSummaryPreview() {
|
|||
)
|
||||
)
|
||||
},
|
||||
onBack = { },
|
||||
onDisplay = { },
|
||||
onItem = { },
|
||||
onAddEvent = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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.viewModelScope
|
||||
import com.pixelized.headache.repository.event.Event
|
||||
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 kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
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.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 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.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.layout.Arrangement
|
||||
|
|
@ -1,18 +1,14 @@
|
|||
package com.pixelized.headache.ui.page.summary.month.item
|
||||
package com.pixelized.headache.ui.page.summary.monthly.item
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
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.graphics.Brush
|
||||
import androidx.compose.ui.unit.dp
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
|
@ -26,30 +22,13 @@ data class MonthSummaryTitleUio(
|
|||
object MonthSummaryTitleDefault {
|
||||
|
||||
@Stable
|
||||
val padding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
|
||||
val padding: PaddingValues = PaddingValues(horizontal = 16.dp)
|
||||
|
||||
@SuppressLint("ConstantLocale")
|
||||
@Stable
|
||||
val formatter = SimpleDateFormat("yyyy", Locale.getDefault())
|
||||
}
|
||||
|
||||
val backgroundBrush: Brush
|
||||
@Composable
|
||||
@Stable
|
||||
get() {
|
||||
val colorScheme = MaterialTheme.colorScheme
|
||||
return remember(colorScheme) {
|
||||
Brush.verticalGradient(
|
||||
listOf(
|
||||
colorScheme.surface,
|
||||
colorScheme.surface,
|
||||
colorScheme.surface,
|
||||
colorScheme.surface.copy(alpha = 0f),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MonthSummaryTitle(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -57,15 +36,11 @@ fun MonthSummaryTitle(
|
|||
formatter: SimpleDateFormat = MonthSummaryTitleDefault.formatter,
|
||||
item: MonthSummaryTitleUio,
|
||||
) {
|
||||
Box(
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.background(brush = backgroundBrush)
|
||||
.padding(paddingValues = padding)
|
||||
.then(other = modifier),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(paddingValues = padding),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
text = formatter.format(item.date)
|
||||
)
|
||||
}
|
||||
style = MaterialTheme.typography.displaySmall,
|
||||
text = formatter.format(item.date)
|
||||
)
|
||||
}
|
||||
|
|
@ -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,18 +1,15 @@
|
|||
package com.pixelized.headache.ui.page.summary.year
|
||||
package com.pixelized.headache.ui.page.summary.yearly
|
||||
|
||||
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 com.pixelized.headache.ui.page.summary.yearly.YearSummaryMonthUio.DayUio
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class YearSummaryFactory @Inject constructor() {
|
||||
|
||||
fun convertToUio(
|
||||
events: Collection<Event>,
|
||||
events: Map<Int, Map<Int, Map<Int, Event>>>,
|
||||
): List<YearUio> {
|
||||
|
||||
val monthFirstDayCalendar = Calendar.getInstance().apply {
|
||||
firstDayOfWeek = Calendar.MONDAY
|
||||
setMinimalDaysInFirstWeek(1)
|
||||
|
|
@ -27,28 +24,6 @@ class YearSummaryFactory @Inject constructor() {
|
|||
}
|
||||
|
||||
return events
|
||||
.fold(initial = hashMapOf<Int, HashMap<Int, HashMap<Int, Event>>>()) { acc, event ->
|
||||
acc.also {
|
||||
val years = it.getOrPut(key = event.date.year) {
|
||||
hashMapOf(
|
||||
Calendar.JANUARY to hashMapOf(),
|
||||
Calendar.FEBRUARY to hashMapOf(),
|
||||
Calendar.MARCH to hashMapOf(),
|
||||
Calendar.APRIL to hashMapOf(),
|
||||
Calendar.MAY to hashMapOf(),
|
||||
Calendar.JUNE to hashMapOf(),
|
||||
Calendar.JULY to hashMapOf(),
|
||||
Calendar.AUGUST to hashMapOf(),
|
||||
Calendar.SEPTEMBER to hashMapOf(),
|
||||
Calendar.OCTOBER to hashMapOf(),
|
||||
Calendar.NOVEMBER to hashMapOf(),
|
||||
Calendar.DECEMBER to hashMapOf(),
|
||||
)
|
||||
}
|
||||
val months = years.getOrPut(key = event.date.month) { hashMapOf() }
|
||||
months[event.date.day] = event
|
||||
}
|
||||
}
|
||||
.map { yearEntry ->
|
||||
YearUio(
|
||||
year = yearEntry.key,
|
||||
|
|
@ -70,7 +45,7 @@ class YearSummaryFactory @Inject constructor() {
|
|||
val initial = MutableList(
|
||||
size = monthLastDayCalendar.get(Calendar.WEEK_OF_MONTH),
|
||||
) {
|
||||
mutableListOf<YearSummaryDayUio>()
|
||||
mutableListOf<DayUio>()
|
||||
}
|
||||
val weeks = (1..monthLastDayCalendar.get(Calendar.DAY_OF_MONTH))
|
||||
.fold(initial = initial) { accumulator, dayNumber ->
|
||||
|
|
@ -83,7 +58,7 @@ class YearSummaryFactory @Inject constructor() {
|
|||
set(Calendar.MILLISECONDS_IN_DAY, 1)
|
||||
}
|
||||
.get(Calendar.WEEK_OF_MONTH) - 1
|
||||
val day = YearSummaryDayUio(
|
||||
val day = DayUio(
|
||||
number = dayNumber,
|
||||
headache = event != null,
|
||||
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,437 @@
|
|||
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.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBarDefaults
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
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.destination.navigateToEventPage
|
||||
import com.pixelized.headache.ui.navigation.main.LocalMainNavigator
|
||||
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 object YearSummaryPageDefault {
|
||||
@Stable
|
||||
val paddingValues = PaddingValues(
|
||||
start = 16.dp,
|
||||
end = 16.dp,
|
||||
bottom = 16.dp + 16.dp + 56.dp,
|
||||
)
|
||||
|
||||
@Stable
|
||||
val space = 8.dp
|
||||
|
||||
@Stable
|
||||
val column = 3
|
||||
}
|
||||
|
||||
@Stable
|
||||
data class YearUio(
|
||||
val year: Int,
|
||||
val months: List<YearSummaryMonthUio>,
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun YearSummaryPage(
|
||||
viewModel: YearSummaryViewModel = hiltViewModel(),
|
||||
) {
|
||||
val navigation = LocalMainNavigator.current
|
||||
val uio = viewModel.events.collectAsStateWithLifecycle()
|
||||
|
||||
YearSummaryContent(
|
||||
modifier = Modifier
|
||||
.keepScreenOn()
|
||||
.fillMaxSize(),
|
||||
uio = uio,
|
||||
onMonth = {
|
||||
navigation.navigateToEventPage(date = it.date)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberDaySize(
|
||||
column: Int,
|
||||
paddingValues: PaddingValues = YearSummaryPageDefault.paddingValues,
|
||||
space: Dp = YearSummaryPageDefault.space,
|
||||
): 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 = YearSummaryPageDefault.paddingValues,
|
||||
space: Dp = YearSummaryPageDefault.space,
|
||||
daySize: Dp = rememberDaySize(column = YearSummaryPageDefault.column),
|
||||
uio: State<List<YearUio>>,
|
||||
onMonth: (YearSummaryMonthUio) -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
contentWindowInsets = remember { WindowInsets(0, 0, 0, 0) },
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = NavigationBarDefaults.containerColor,
|
||||
),
|
||||
title = {
|
||||
Text(text = stringResource(R.string.year_summary_title))
|
||||
},
|
||||
)
|
||||
},
|
||||
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 = 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) },
|
||||
onMonth = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.viewModelScope
|
||||
|
|
@ -18,7 +18,7 @@ class YearSummaryViewModel @Inject constructor(
|
|||
) : ViewModel() {
|
||||
|
||||
val events: StateFlow<List<YearUio>> = eventRepository
|
||||
.eventsListFlow()
|
||||
.eventsMapFlow()
|
||||
.map(summaryFactory::convertToUio)
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
|
|
@ -3,6 +3,7 @@ package com.pixelized.headache.ui.theme
|
|||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.runtime.Stable
|
||||
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.headacheLightColorScheme
|
||||
|
||||
val LocalLwaTheme = compositionLocalOf<HeadacheTheme> {
|
||||
error("Local Snack Controller is not yet ready")
|
||||
val LocalHeadacheTheme = compositionLocalOf<HeadacheTheme> {
|
||||
error("Local Theme Controller is not yet ready")
|
||||
}
|
||||
|
||||
val MaterialTheme.headache: HeadacheTheme
|
||||
@Stable
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
get() = LocalLwaTheme.current
|
||||
get() = LocalHeadacheTheme.current
|
||||
|
||||
@Stable
|
||||
data class HeadacheTheme(
|
||||
|
|
@ -30,14 +31,19 @@ fun HeadacheTheme(
|
|||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val colorScheme = when {
|
||||
darkTheme -> headacheDarkColorScheme()
|
||||
else -> headacheLightColorScheme()
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme.base,
|
||||
typography = Typography,
|
||||
content = content
|
||||
val theme = HeadacheTheme(
|
||||
colorScheme = when {
|
||||
darkTheme -> headacheDarkColorScheme()
|
||||
else -> headacheLightColorScheme()
|
||||
}
|
||||
)
|
||||
CompositionLocalProvider(
|
||||
LocalHeadacheTheme provides theme,
|
||||
) {
|
||||
MaterialTheme(
|
||||
colorScheme = theme.colorScheme.base,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,25 +16,67 @@ import kotlin.math.ln
|
|||
@Stable
|
||||
data class HeadacheColors(
|
||||
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
|
||||
@Stable
|
||||
fun headacheDarkColorScheme(
|
||||
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(
|
||||
base = base,
|
||||
calendar = calendar,
|
||||
pill = pill,
|
||||
)
|
||||
|
||||
@Composable
|
||||
@Stable
|
||||
fun headacheLightColorScheme(
|
||||
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(
|
||||
base = base,
|
||||
calendar = calendar,
|
||||
pill = pill,
|
||||
)
|
||||
|
||||
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
fun calculateElevatedColor(
|
||||
|
|
|
|||
9
app/src/main/res/drawable/ic_analytics_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_analytics_24dp.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M280,680L360,680L360,480L280,480L280,680ZM600,680L680,680L680,280L600,280L600,680ZM440,680L520,680L520,560L440,560L440,680ZM440,480L520,480L520,400L440,400L440,480ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM200,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200Z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_analytics_filled_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_analytics_filled_24dp.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M280,680L360,680L360,480L280,480L280,680ZM600,680L680,680L680,280L600,280L600,680ZM440,680L520,680L520,560L440,560L440,680ZM440,480L520,480L520,400L440,400L440,480ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840Z" />
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_calendar_month_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_calendar_month_24dp.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200,880Q167,880 143.5,856.5Q120,833 120,800L120,240Q120,207 143.5,183.5Q167,160 200,160L240,160L240,80L320,80L320,160L640,160L640,80L720,80L720,160L760,160Q793,160 816.5,183.5Q840,207 840,240L840,800Q840,833 816.5,856.5Q793,880 760,880L200,880ZM200,800L760,800Q760,800 760,800Q760,800 760,800L760,400L200,400L200,800Q200,800 200,800Q200,800 200,800ZM200,320L760,320L760,240Q760,240 760,240Q760,240 760,240L200,240Q200,240 200,240Q200,240 200,240L200,320ZM200,320L200,240Q200,240 200,240Q200,240 200,240L200,240Q200,240 200,240Q200,240 200,240L200,320ZM480,560Q463,560 451.5,548.5Q440,537 440,520Q440,503 451.5,491.5Q463,480 480,480Q497,480 508.5,491.5Q520,503 520,520Q520,537 508.5,548.5Q497,560 480,560ZM320,560Q303,560 291.5,548.5Q280,537 280,520Q280,503 291.5,491.5Q303,480 320,480Q337,480 348.5,491.5Q360,503 360,520Q360,537 348.5,548.5Q337,560 320,560ZM640,560Q623,560 611.5,548.5Q600,537 600,520Q600,503 611.5,491.5Q623,480 640,480Q657,480 668.5,491.5Q680,503 680,520Q680,537 668.5,548.5Q657,560 640,560ZM480,720Q463,720 451.5,708.5Q440,697 440,680Q440,663 451.5,651.5Q463,640 480,640Q497,640 508.5,651.5Q520,663 520,680Q520,697 508.5,708.5Q497,720 480,720ZM320,720Q303,720 291.5,708.5Q280,697 280,680Q280,663 291.5,651.5Q303,640 320,640Q337,640 348.5,651.5Q360,663 360,680Q360,697 348.5,708.5Q337,720 320,720ZM640,720Q623,720 611.5,708.5Q600,697 600,680Q600,663 611.5,651.5Q623,640 640,640Q657,640 668.5,651.5Q680,663 680,680Q680,697 668.5,708.5Q657,720 640,720Z" />
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,560Q463,560 451.5,548.5Q440,537 440,520Q440,503 451.5,491.5Q463,480 480,480Q497,480 508.5,491.5Q520,503 520,520Q520,537 508.5,548.5Q497,560 480,560ZM320,560Q303,560 291.5,548.5Q280,537 280,520Q280,503 291.5,491.5Q303,480 320,480Q337,480 348.5,491.5Q360,503 360,520Q360,537 348.5,548.5Q337,560 320,560ZM640,560Q623,560 611.5,548.5Q600,537 600,520Q600,503 611.5,491.5Q623,480 640,480Q657,480 668.5,491.5Q680,503 680,520Q680,537 668.5,548.5Q657,560 640,560ZM480,720Q463,720 451.5,708.5Q440,697 440,680Q440,663 451.5,651.5Q463,640 480,640Q497,640 508.5,651.5Q520,663 520,680Q520,697 508.5,708.5Q497,720 480,720ZM320,720Q303,720 291.5,708.5Q280,697 280,680Q280,663 291.5,651.5Q303,640 320,640Q337,640 348.5,651.5Q360,663 360,680Q360,697 348.5,708.5Q337,720 320,720ZM640,720Q623,720 611.5,708.5Q600,697 600,680Q600,663 611.5,651.5Q623,640 640,640Q657,640 668.5,651.5Q680,663 680,680Q680,697 668.5,708.5Q657,720 640,720ZM200,880Q167,880 143.5,856.5Q120,833 120,800L120,240Q120,207 143.5,183.5Q167,160 200,160L240,160L240,80L320,80L320,160L640,160L640,80L720,80L720,160L760,160Q793,160 816.5,183.5Q840,207 840,240L840,800Q840,833 816.5,856.5Q793,880 760,880L200,880ZM200,800L760,800Q760,800 760,800Q760,800 760,800L760,400L200,400L200,800Q200,800 200,800Q200,800 200,800Z" />
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_list_alt_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_list_alt_24dp.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M320,680Q337,680 348.5,668.5Q360,657 360,640Q360,623 348.5,611.5Q337,600 320,600Q303,600 291.5,611.5Q280,623 280,640Q280,657 291.5,668.5Q303,680 320,680ZM320,520Q337,520 348.5,508.5Q360,497 360,480Q360,463 348.5,451.5Q337,440 320,440Q303,440 291.5,451.5Q280,463 280,480Q280,497 291.5,508.5Q303,520 320,520ZM320,360Q337,360 348.5,348.5Q360,337 360,320Q360,303 348.5,291.5Q337,280 320,280Q303,280 291.5,291.5Q280,303 280,320Q280,337 291.5,348.5Q303,360 320,360ZM440,680L680,680L680,600L440,600L440,680ZM440,520L680,520L680,440L440,440L440,520ZM440,360L680,360L680,280L440,280L440,360ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM200,760L760,760Q760,760 760,760Q760,760 760,760L760,200Q760,200 760,200Q760,200 760,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760ZM200,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200Z" />
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_list_alt_filled_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_list_alt_filled_24dp.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M320,680Q337,680 348.5,668.5Q360,657 360,640Q360,623 348.5,611.5Q337,600 320,600Q303,600 291.5,611.5Q280,623 280,640Q280,657 291.5,668.5Q303,680 320,680ZM320,520Q337,520 348.5,508.5Q360,497 360,480Q360,463 348.5,451.5Q337,440 320,440Q303,440 291.5,451.5Q280,463 280,480Q280,497 291.5,508.5Q303,520 320,520ZM320,360Q337,360 348.5,348.5Q360,337 360,320Q360,303 348.5,291.5Q337,280 320,280Q303,280 291.5,291.5Q280,303 280,320Q280,337 291.5,348.5Q303,360 320,360ZM440,680L680,680L680,600L440,600L440,680ZM440,520L680,520L680,440L440,440L440,520ZM440,360L680,360L680,280L440,280L440,360ZM200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L760,120Q793,120 816.5,143.5Q840,167 840,200L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840Z" />
|
||||
</vector>
|
||||
Loading…
Add table
Add a link
Reference in a new issue