diff --git a/app/release/baselineProfiles/0/app-release.dm b/app/release/baselineProfiles/0/app-release.dm index f3c8975..6b396b6 100644 Binary files a/app/release/baselineProfiles/0/app-release.dm and b/app/release/baselineProfiles/0/app-release.dm differ diff --git a/app/release/baselineProfiles/1/app-release.dm b/app/release/baselineProfiles/1/app-release.dm index fb2a7fd..2b85bed 100644 Binary files a/app/release/baselineProfiles/1/app-release.dm and b/app/release/baselineProfiles/1/app-release.dm differ diff --git a/app/src/main/java/com/pixelized/headache/MainActivity.kt b/app/src/main/java/com/pixelized/headache/MainActivity.kt index 2df193a..3e1b6b4 100644 --- a/app/src/main/java/com/pixelized/headache/MainActivity.kt +++ b/app/src/main/java/com/pixelized/headache/MainActivity.kt @@ -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, ) } } diff --git a/app/src/main/java/com/pixelized/headache/module/AppModule.kt b/app/src/main/java/com/pixelized/headache/module/AppModule.kt index ccb74ad..583424f 100644 --- a/app/src/main/java/com/pixelized/headache/module/AppModule.kt +++ b/app/src/main/java/com/pixelized/headache/module/AppModule.kt @@ -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) + } } diff --git a/app/src/main/java/com/pixelized/headache/repository/event/EventRepository.kt b/app/src/main/java/com/pixelized/headache/repository/event/EventRepository.kt index c7c74e2..062f632 100644 --- a/app/src/main/java/com/pixelized/headache/repository/event/EventRepository.kt +++ b/app/src/main/java/com/pixelized/headache/repository/event/EventRepository.kt @@ -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()) + // Collectable data. Provide events in a sorted list by dates. private val eventListFlow: StateFlow> = 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>>> = eventListFlow + .map { events -> + events.fold(initial = hashMapOf>>()) { 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> = eventFlow - fun eventsListFlow(): StateFlow> = eventListFlow + fun eventsMapFlow(): StateFlow>>> = eventMapFlow + fun event(id: Long): Event? = eventFlow.value.get(key = id) fun createEvent( diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/CalendarChooserDestination.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/CalendarChooserDestination.kt index de619b0..4248285 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/CalendarChooserDestination.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/CalendarChooserDestination.kt @@ -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 { @@ -13,6 +14,6 @@ fun EntryProviderBuilder<*>.calendarChooserDestinationEntry() { } } -fun Navigator.navigateToCalendarChooserPage() { +fun MainNavigator.navigateToCalendarChooserPage() { goTo(CalendarChooserDestination) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/EventDestination.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/EventDestination.kt index 4eff6e4..ad901f5 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/EventDestination.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/EventDestination.kt @@ -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 { key -> @@ -30,7 +31,7 @@ fun EntryProviderBuilder<*>.eventDestinationEntry() { } } -fun Navigator.navigateToEventPage( +fun MainNavigator.navigateToEventPage( misspelledFilter: Boolean = false, invalidFilter: Boolean = false, date: Date? = null, diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/HomeDestination.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/HomeDestination.kt index a53fde6..d506f26 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/HomeDestination.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/HomeDestination.kt @@ -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 { - HomePage() + HomePage( + navigator = navigator, + ) } } -fun Navigator.navigateToHomePage() { +fun MainNavigator.navigateToHomePage() { goTo(HomePageDestination) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/MonthSummaryDestination.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/MonthSummaryDestination.kt index 2f4541a..6166876 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/MonthSummaryDestination.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/MonthSummaryDestination.kt @@ -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 { @@ -13,6 +14,6 @@ fun EntryProviderBuilder<*>.monthSummaryDestinationEntry() { } } -fun Navigator.navigateToMonthSummary() { +fun HomeNavigator.navigateToMonthSummary() { goTo(MonthSummaryDestination) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/YearSummaryDestination.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/YearSummaryDestination.kt index 6fd30fb..1218a57 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/destination/YearSummaryDestination.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/destination/YearSummaryDestination.kt @@ -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 { @@ -14,6 +15,6 @@ fun EntryProviderBuilder<*>.yearSummaryDestinationEntry() { } } -fun Navigator.navigateToYearSummary() { +fun HomeNavigator.navigateToYearSummary() { goTo(YearSummaryDestination) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/home/HomeNavDisplay.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/home/HomeNavDisplay.kt new file mode 100644 index 0000000..ec6204b --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/home/HomeNavDisplay.kt @@ -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 { + 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() + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/Navigator.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/home/HomeNavigator.kt similarity index 67% rename from app/src/main/java/com/pixelized/headache/ui/navigation/Navigator.kt rename to app/src/main/java/com/pixelized/headache/ui/navigation/home/HomeNavigator.kt index 262d9e6..ff85fba 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/Navigator.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/home/HomeNavigator.kt @@ -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 = mutableStateListOf(startDestination) fun popBackstack() = backStack.removeLastOrNull() - fun goTo(destination: Any) { + fun goTo(destination: HomeDestination) { backStack.add(destination) } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/MainNavDisplay.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/main/MainNavDisplay.kt similarity index 75% rename from app/src/main/java/com/pixelized/headache/ui/navigation/MainNavDisplay.kt rename to app/src/main/java/com/pixelized/headache/ui/navigation/main/MainNavDisplay.kt index 0b88856..a1b1e3f 100644 --- a/app/src/main/java/com/pixelized/headache/ui/navigation/MainNavDisplay.kt +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/main/MainNavDisplay.kt @@ -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 { +val LocalMainNavigator = staticCompositionLocalOf { 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() diff --git a/app/src/main/java/com/pixelized/headache/ui/navigation/main/MainNavigator.kt b/app/src/main/java/com/pixelized/headache/ui/navigation/main/MainNavigator.kt new file mode 100644 index 0000000..34b83f1 --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/navigation/main/MainNavigator.kt @@ -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 = mutableStateListOf(startDestination) + + fun popBackstack() = backStack.removeLastOrNull() + + fun goTo(destination: MainDestination) { + backStack.add(destination) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/MainPage.kt b/app/src/main/java/com/pixelized/headache/ui/page/MainPage.kt index 56ea573..b6cddef 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/MainPage.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/MainPage.kt @@ -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 { error("Local SnackHost no yet ready") @@ -25,7 +26,8 @@ val LocalErrorSnack = staticCompositionLocalOf { @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, ) } ) diff --git a/app/src/main/java/com/pixelized/headache/ui/page/calendar/CalendarChooserPage.kt b/app/src/main/java/com/pixelized/headache/ui/page/calendar/CalendarChooserPage.kt index 5540219..9d158f0 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/calendar/CalendarChooserPage.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/calendar/CalendarChooserPage.kt @@ -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() diff --git a/app/src/main/java/com/pixelized/headache/ui/page/event/list/EventPage.kt b/app/src/main/java/com/pixelized/headache/ui/page/event/list/EventPage.kt index c7406f7..30d4f90 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/event/list/EventPage.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/event/list/EventPage.kt @@ -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() diff --git a/app/src/main/java/com/pixelized/headache/ui/page/event/list/EventViewModel.kt b/app/src/main/java/com/pixelized/headache/ui/page/event/list/EventViewModel.kt index 9e62d33..d4db750 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/event/list/EventViewModel.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/event/list/EventViewModel.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/headache/ui/page/home/HomePage.kt b/app/src/main/java/com/pixelized/headache/ui/page/home/HomePage.kt index c4908d0..dbf6aa7 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/home/HomePage.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/home/HomePage.kt @@ -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 = 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, + 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, + ) } ) } diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryFactory.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryFactory.kt similarity index 90% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryFactory.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryFactory.kt index eb624aa..2fd478a 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryFactory.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryFactory.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryPage.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryPage.kt similarity index 72% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryPage.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryPage.kt index 40ab528..c2b4f9b 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryPage.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryPage.kt @@ -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, events: State>>, - 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 = { }, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryViewModel.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryViewModel.kt similarity index 91% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryViewModel.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryViewModel.kt index 86f038a..06c3d71 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/MonthSummaryViewModel.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/MonthSummaryViewModel.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryBox.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryBox.kt similarity index 99% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryBox.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryBox.kt index dc4d060..99f51ba 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryBox.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryBox.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryCell.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryCell.kt similarity index 67% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryCell.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryCell.kt index 863d793..9d06e78 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryCell.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryCell.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryItem.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryItem.kt similarity index 98% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryItem.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryItem.kt index 9adf1f0..4101862 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryItem.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryItem.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryPillItem.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryPillItem.kt similarity index 97% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryPillItem.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryPillItem.kt index f0c799e..1d55727 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryPillItem.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryPillItem.kt @@ -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 diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryTitle.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryTitle.kt similarity index 53% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryTitle.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryTitle.kt index 65cc3ec..f898bd3 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/month/item/MonthSummaryTitle.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/monthly/item/MonthSummaryTitle.kt @@ -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) + ) } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryPage.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryPage.kt deleted file mode 100644 index 026bcfe..0000000 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryPage.kt +++ /dev/null @@ -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, -) - -@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>, - 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, -) { - HeadacheTheme { - Surface { - YearSummaryContent( - uio = remember { mutableStateOf(preview) }, - onBack = { }, - onMonth = { }, - onAddEvent = { }, - ) - } - } -} - -private class YearPreviewProvider() : PreviewParameterProvider> { - - 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 = 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, - ): 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> = 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), - ), - ) - ), - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryDay.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryDay.kt deleted file mode 100644 index c3040b0..0000000 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryDay.kt +++ /dev/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, -) - -@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 { - override val values: Sequence - get() = sequenceOf( - YearSummaryDayUio( - number = 1, - headache = false, - pills = emptyList(), - ), - YearSummaryDayUio( - number = 2, - headache = true, - pills = emptyList(), - ), - YearSummaryDayUio( - number = 3, - headache = true, - pills = listOf( - HeadacheColorPalette.Pill.Eletriptan40, - ), - ), - YearSummaryDayUio( - number = 3, - headache = true, - pills = listOf( - HeadacheColorPalette.Pill.Eletriptan40, - HeadacheColorPalette.Pill.Ibuprofene400, - HeadacheColorPalette.Pill.Spifen400, - HeadacheColorPalette.Pill.Paracetamol1000, - ), - ), - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryMonth.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryMonth.kt deleted file mode 100644 index 1946042..0000000 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/item/YearSummaryMonth.kt +++ /dev/null @@ -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>, -) - -@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 { - - 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 = listOfNotNull( - mon?.invoke(), - tue?.invoke(), - wen?.invoke(), - thu?.invoke(), - fry?.invoke(), - sat?.invoke(), - sun?.invoke(), - ) - - private fun month( - month: Int, - vararg weeks: List, - ): 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 - 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), - ), - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryFactory.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryFactory.kt similarity index 68% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryFactory.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryFactory.kt index 6b11433..8b49002 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryFactory.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryFactory.kt @@ -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, + events: Map>>, ): List { - val monthFirstDayCalendar = Calendar.getInstance().apply { firstDayOfWeek = Calendar.MONDAY setMinimalDaysInFirstWeek(1) @@ -27,28 +24,6 @@ class YearSummaryFactory @Inject constructor() { } return events - .fold(initial = hashMapOf>>()) { 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() + mutableListOf() } 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(), diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryMonth.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryMonth.kt new file mode 100644 index 0000000..7961ea3 --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryMonth.kt @@ -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>, +) { + @Stable + data class DayUio( + val number: Int, + val headache: Boolean, + val pills: List, + ) +} + +@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 = 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 { + 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 { + override val values: Sequence + 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), + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryPage.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryPage.kt new file mode 100644 index 0000000..790dd46 --- /dev/null +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryPage.kt @@ -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, +) + +@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>, + 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, +) { + HeadacheTheme { + Surface { + YearSummaryContent( + uio = remember { mutableStateOf(preview) }, + onMonth = { }, + ) + } + } +} + +private class YearPreviewProvider() : PreviewParameterProvider> { + + private fun year( + year: Int, + vararg months: YearSummaryMonthUio, + ) = YearUio( + year = year, + months = months.toList(), + ) + + override val values: Sequence> + 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 + ), + ), + ) + ), + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryViewModel.kt b/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryViewModel.kt similarity index 90% rename from app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryViewModel.kt rename to app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryViewModel.kt index 56ab4a5..19bdfd4 100644 --- a/app/src/main/java/com/pixelized/headache/ui/page/summary/year/YearSummaryViewModel.kt +++ b/app/src/main/java/com/pixelized/headache/ui/page/summary/yearly/YearSummaryViewModel.kt @@ -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> = eventRepository - .eventsListFlow() + .eventsMapFlow() .map(summaryFactory::convertToUio) .stateIn( scope = viewModelScope, diff --git a/app/src/main/java/com/pixelized/headache/ui/theme/Theme.kt b/app/src/main/java/com/pixelized/headache/ui/theme/Theme.kt index e22dca7..b0955df 100644 --- a/app/src/main/java/com/pixelized/headache/ui/theme/Theme.kt +++ b/app/src/main/java/com/pixelized/headache/ui/theme/Theme.kt @@ -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 { - error("Local Snack Controller is not yet ready") +val LocalHeadacheTheme = compositionLocalOf { + 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 + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/headache/ui/theme/color/HeadacheColors.kt b/app/src/main/java/com/pixelized/headache/ui/theme/color/HeadacheColors.kt index 0e9efdd..8ce3d84 100644 --- a/app/src/main/java/com/pixelized/headache/ui/theme/color/HeadacheColors.kt +++ b/app/src/main/java/com/pixelized/headache/ui/theme/color/HeadacheColors.kt @@ -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( diff --git a/app/src/main/res/drawable/ic_analytics_24dp.xml b/app/src/main/res/drawable/ic_analytics_24dp.xml new file mode 100644 index 0000000..fd21c24 --- /dev/null +++ b/app/src/main/res/drawable/ic_analytics_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_analytics_filled_24dp.xml b/app/src/main/res/drawable/ic_analytics_filled_24dp.xml new file mode 100644 index 0000000..2c36e68 --- /dev/null +++ b/app/src/main/res/drawable/ic_analytics_filled_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_calendar_month_24dp.xml b/app/src/main/res/drawable/ic_calendar_month_24dp.xml new file mode 100644 index 0000000..eec5fdc --- /dev/null +++ b/app/src/main/res/drawable/ic_calendar_month_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_calendar_month_filled_24dp.xml b/app/src/main/res/drawable/ic_calendar_month_filled_24dp.xml new file mode 100644 index 0000000..b305544 --- /dev/null +++ b/app/src/main/res/drawable/ic_calendar_month_filled_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_list_alt_24dp.xml b/app/src/main/res/drawable/ic_list_alt_24dp.xml new file mode 100644 index 0000000..20cb491 --- /dev/null +++ b/app/src/main/res/drawable/ic_list_alt_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_list_alt_filled_24dp.xml b/app/src/main/res/drawable/ic_list_alt_filled_24dp.xml new file mode 100644 index 0000000..c93cae2 --- /dev/null +++ b/app/src/main/res/drawable/ic_list_alt_filled_24dp.xml @@ -0,0 +1,10 @@ + + +