feat(stats): implement heatmap in last year screen
This commit is contained in:
@@ -58,15 +58,17 @@ fun StatsScreenRoot(
|
|||||||
|
|
||||||
val todayStat by viewModel.todayStat.collectAsStateWithLifecycle(null)
|
val todayStat by viewModel.todayStat.collectAsStateWithLifecycle(null)
|
||||||
|
|
||||||
val lastWeekSummaryChartData by viewModel.lastWeekSummaryChartData.collectAsStateWithLifecycle()
|
val lastWeekMainChartData by viewModel.lastWeekMainChartData.collectAsStateWithLifecycle()
|
||||||
val lastWeekSummaryValues by viewModel.lastWeekStats.collectAsStateWithLifecycle()
|
val lastWeekFocusHistoryValues by viewModel.lastWeekFocusHistoryValues.collectAsStateWithLifecycle()
|
||||||
val lastWeekAnalysisValues by viewModel.lastWeekAverageFocusTimes.collectAsStateWithLifecycle()
|
val lastWeekFocusBreakdownValues by viewModel.lastWeekFocusBreakdownValues.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val lastMonthSummaryChartData by viewModel.lastMonthSummaryChartData.collectAsStateWithLifecycle()
|
val lastMonthSummaryChartData by viewModel.lastMonthSummaryChartData.collectAsStateWithLifecycle()
|
||||||
val lastMonthAnalysisValues by viewModel.lastMonthAverageFocusTimes.collectAsStateWithLifecycle()
|
val lastMonthAnalysisValues by viewModel.lastMonthAverageFocusTimes.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val lastYearSummaryChartData by viewModel.lastYearSummaryChartData.collectAsStateWithLifecycle()
|
val lastYearMainChartData by viewModel.lastYearMainChartData.collectAsStateWithLifecycle()
|
||||||
val lastYearAnalysisValues by viewModel.lastYearAverageFocusTimes.collectAsStateWithLifecycle()
|
val lastYearFocusHeatmapData by viewModel.lastYearFocusHeatmapData.collectAsStateWithLifecycle()
|
||||||
|
val lastYearFocusBreakdownValues by viewModel.lastYearFocusBreakdownValues.collectAsStateWithLifecycle()
|
||||||
|
val lastYearMaxFocus by viewModel.lastYearMaxFocus.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val colorScheme = colorScheme
|
val colorScheme = colorScheme
|
||||||
|
|
||||||
@@ -95,13 +97,13 @@ fun StatsScreenRoot(
|
|||||||
entry<Screen.Stats.Main> {
|
entry<Screen.Stats.Main> {
|
||||||
StatsMainScreen(
|
StatsMainScreen(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
lastWeekSummaryChartData = lastWeekSummaryChartData,
|
lastWeekSummaryChartData = lastWeekMainChartData,
|
||||||
lastMonthSummaryChartData = lastMonthSummaryChartData,
|
lastMonthSummaryChartData = lastMonthSummaryChartData,
|
||||||
lastYearSummaryChartData = lastYearSummaryChartData,
|
lastYearSummaryChartData = lastYearMainChartData,
|
||||||
todayStat = todayStat,
|
todayStat = todayStat,
|
||||||
lastWeekAverageFocusTimes = lastWeekAnalysisValues.first,
|
lastWeekAverageFocusTimes = lastWeekFocusBreakdownValues.first,
|
||||||
lastMonthAverageFocusTimes = lastMonthAnalysisValues.first,
|
lastMonthAverageFocusTimes = lastMonthAnalysisValues.first,
|
||||||
lastYearAverageFocusTimes = lastYearAnalysisValues.first,
|
lastYearAverageFocusTimes = lastYearFocusBreakdownValues.first,
|
||||||
generateSampleData = viewModel::generateSampleData,
|
generateSampleData = viewModel::generateSampleData,
|
||||||
hoursFormat = hoursFormat,
|
hoursFormat = hoursFormat,
|
||||||
hoursMinutesFormat = hoursMinutesFormat,
|
hoursMinutesFormat = hoursMinutesFormat,
|
||||||
@@ -119,9 +121,9 @@ fun StatsScreenRoot(
|
|||||||
entry<Screen.Stats.LastWeek> {
|
entry<Screen.Stats.LastWeek> {
|
||||||
LastWeekScreen(
|
LastWeekScreen(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
focusBreakdownValues = lastWeekAnalysisValues,
|
focusBreakdownValues = lastWeekFocusBreakdownValues,
|
||||||
focusHistoryValues = lastWeekSummaryValues,
|
focusHistoryValues = lastWeekFocusHistoryValues,
|
||||||
mainChartData = lastWeekSummaryChartData,
|
mainChartData = lastWeekMainChartData,
|
||||||
onBack = backStack::removeLastOrNull,
|
onBack = backStack::removeLastOrNull,
|
||||||
hoursMinutesFormat = hoursMinutesFormat,
|
hoursMinutesFormat = hoursMinutesFormat,
|
||||||
hoursFormat = hoursFormat,
|
hoursFormat = hoursFormat,
|
||||||
@@ -148,8 +150,10 @@ fun StatsScreenRoot(
|
|||||||
entry<Screen.Stats.LastYear> {
|
entry<Screen.Stats.LastYear> {
|
||||||
LastYearScreen(
|
LastYearScreen(
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
lastYearAnalysisValues = lastYearAnalysisValues,
|
focusBreakdownValues = lastYearFocusBreakdownValues,
|
||||||
lastYearSummaryChartData = lastYearSummaryChartData,
|
focusHeatmapData = lastYearFocusHeatmapData,
|
||||||
|
heatmapMaxValue = lastYearMaxFocus,
|
||||||
|
mainChartData = lastYearMainChartData,
|
||||||
onBack = backStack::removeLastOrNull,
|
onBack = backStack::removeLastOrNull,
|
||||||
hoursMinutesFormat = hoursMinutesFormat,
|
hoursMinutesFormat = hoursMinutesFormat,
|
||||||
hoursFormat = hoursFormat,
|
hoursFormat = hoursFormat,
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.fastForEach
|
import androidx.compose.ui.util.fastForEach
|
||||||
import androidx.compose.ui.util.fastForEachIndexed
|
import androidx.compose.ui.util.fastForEachIndexed
|
||||||
|
import androidx.compose.ui.util.fastMaxBy
|
||||||
import org.nsh07.pomodoro.ui.theme.TomatoTheme
|
import org.nsh07.pomodoro.ui.theme.TomatoTheme
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.format.TextStyle
|
import java.time.format.TextStyle
|
||||||
@@ -208,6 +209,8 @@ val HEATMAP_CELL_GAP = 4.dp
|
|||||||
* insert gaps in the heatmap, and can be used to, for example, delimit months by inserting an
|
* insert gaps in the heatmap, and can be used to, for example, delimit months by inserting an
|
||||||
* empty week
|
* empty week
|
||||||
* @param modifier Modifier to be applied to the heatmap
|
* @param modifier Modifier to be applied to the heatmap
|
||||||
|
* @param maxValue Maximum total value of the items present in [data]. This value must correspond to
|
||||||
|
* the sum of the list present in one of the elements on [data] for accurate representation.
|
||||||
*
|
*
|
||||||
* Note that it is assumed that the dates are continuous (without gaps) and start with a Monday
|
* Note that it is assumed that the dates are continuous (without gaps) and start with a Monday
|
||||||
*/
|
*/
|
||||||
@@ -218,7 +221,7 @@ fun HeatmapWithWeekLabels(
|
|||||||
size: Dp = HEATMAP_CELL_SIZE,
|
size: Dp = HEATMAP_CELL_SIZE,
|
||||||
gap: Dp = HEATMAP_CELL_GAP,
|
gap: Dp = HEATMAP_CELL_GAP,
|
||||||
contentPadding: PaddingValues = PaddingValues(),
|
contentPadding: PaddingValues = PaddingValues(),
|
||||||
maxValue: Long = remember { data.maxBy { it?.sum() ?: 0 }?.sum() ?: 0 },
|
maxValue: Long = remember { data.fastMaxBy { it?.sum() ?: 0 }?.sum() ?: 0 },
|
||||||
) {
|
) {
|
||||||
val locale = Locale.getDefault()
|
val locale = Locale.getDefault()
|
||||||
|
|
||||||
@@ -242,7 +245,7 @@ fun HeatmapWithWeekLabels(
|
|||||||
|
|
||||||
LazyHorizontalGrid(
|
LazyHorizontalGrid(
|
||||||
rows = GridCells.Fixed(7),
|
rows = GridCells.Fixed(7),
|
||||||
modifier = modifier,
|
modifier = modifier.height(size * 7 + gap * 6),
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
verticalArrangement = Arrangement.spacedBy(gap),
|
verticalArrangement = Arrangement.spacedBy(gap),
|
||||||
horizontalArrangement = Arrangement.spacedBy(gap)
|
horizontalArrangement = Arrangement.spacedBy(gap)
|
||||||
@@ -265,7 +268,6 @@ fun HeatmapWithWeekLabels(
|
|||||||
Spacer(
|
Spacer(
|
||||||
Modifier
|
Modifier
|
||||||
.size(size)
|
.size(size)
|
||||||
.background(colorScheme.surfaceVariant, shapes.small)
|
|
||||||
.background(
|
.background(
|
||||||
colorScheme.primary.copy(
|
colorScheme.primary.copy(
|
||||||
remember(it) {
|
remember(it) {
|
||||||
|
|||||||
@@ -68,11 +68,12 @@ import org.nsh07.pomodoro.R
|
|||||||
import org.nsh07.pomodoro.ui.mergePaddingValues
|
import org.nsh07.pomodoro.ui.mergePaddingValues
|
||||||
import org.nsh07.pomodoro.ui.statsScreen.components.FocusBreakRatioVisualization
|
import org.nsh07.pomodoro.ui.statsScreen.components.FocusBreakRatioVisualization
|
||||||
import org.nsh07.pomodoro.ui.statsScreen.components.FocusBreakdownChart
|
import org.nsh07.pomodoro.ui.statsScreen.components.FocusBreakdownChart
|
||||||
|
import org.nsh07.pomodoro.ui.statsScreen.components.HeatmapWithWeekLabels
|
||||||
import org.nsh07.pomodoro.ui.statsScreen.components.HorizontalStackedBar
|
import org.nsh07.pomodoro.ui.statsScreen.components.HorizontalStackedBar
|
||||||
import org.nsh07.pomodoro.ui.statsScreen.components.TimeLineChart
|
import org.nsh07.pomodoro.ui.statsScreen.components.TimeLineChart
|
||||||
import org.nsh07.pomodoro.ui.statsScreen.components.sharedBoundsReveal
|
import org.nsh07.pomodoro.ui.statsScreen.components.sharedBoundsReveal
|
||||||
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
|
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
|
||||||
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape
|
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.bottomListItemShape
|
||||||
import org.nsh07.pomodoro.utils.millisecondsToHoursMinutes
|
import org.nsh07.pomodoro.utils.millisecondsToHoursMinutes
|
||||||
import org.nsh07.pomodoro.utils.millisecondsToMinutes
|
import org.nsh07.pomodoro.utils.millisecondsToMinutes
|
||||||
|
|
||||||
@@ -80,8 +81,10 @@ import org.nsh07.pomodoro.utils.millisecondsToMinutes
|
|||||||
@Composable
|
@Composable
|
||||||
fun SharedTransitionScope.LastYearScreen(
|
fun SharedTransitionScope.LastYearScreen(
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
lastYearAnalysisValues: Pair<List<Long>, Long>,
|
focusBreakdownValues: Pair<List<Long>, Long>,
|
||||||
lastYearSummaryChartData: Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>,
|
focusHeatmapData: List<List<Long>?>,
|
||||||
|
heatmapMaxValue: Long,
|
||||||
|
mainChartData: Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
hoursMinutesFormat: String,
|
hoursMinutesFormat: String,
|
||||||
@@ -95,18 +98,18 @@ fun SharedTransitionScope.LastYearScreen(
|
|||||||
val lastYearSummaryAnalysisModelProducer = remember { CartesianChartModelProducer() }
|
val lastYearSummaryAnalysisModelProducer = remember { CartesianChartModelProducer() }
|
||||||
var breakdownChartExpanded by remember { mutableStateOf(false) }
|
var breakdownChartExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(lastYearAnalysisValues.first) {
|
LaunchedEffect(focusBreakdownValues.first) {
|
||||||
lastYearSummaryAnalysisModelProducer.runTransaction {
|
lastYearSummaryAnalysisModelProducer.runTransaction {
|
||||||
columnSeries {
|
columnSeries {
|
||||||
series(lastYearAnalysisValues.first)
|
series(focusBreakdownValues.first)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val rankList = remember(lastYearAnalysisValues) {
|
val rankList = remember(focusBreakdownValues) {
|
||||||
val sortedIndices =
|
val sortedIndices =
|
||||||
lastYearAnalysisValues.first.indices.sortedByDescending { lastYearAnalysisValues.first[it] }
|
focusBreakdownValues.first.indices.sortedByDescending { focusBreakdownValues.first[it] }
|
||||||
val ranks = MutableList(lastYearAnalysisValues.first.size) { 0 }
|
val ranks = MutableList(focusBreakdownValues.first.size) { 0 }
|
||||||
|
|
||||||
sortedIndices.forEachIndexed { rank, originalIndex ->
|
sortedIndices.forEachIndexed { rank, originalIndex ->
|
||||||
ranks[originalIndex] = rank
|
ranks[originalIndex] = rank
|
||||||
@@ -115,8 +118,8 @@ fun SharedTransitionScope.LastYearScreen(
|
|||||||
ranks
|
ranks
|
||||||
}
|
}
|
||||||
|
|
||||||
val focusDuration = remember(lastYearAnalysisValues) {
|
val focusDuration = remember(focusBreakdownValues) {
|
||||||
lastYearAnalysisValues.first.sum()
|
focusBreakdownValues.first.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -158,7 +161,7 @@ fun SharedTransitionScope.LastYearScreen(
|
|||||||
"last year card"
|
"last year card"
|
||||||
),
|
),
|
||||||
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
|
animatedVisibilityScope = LocalNavAnimatedContentScope.current,
|
||||||
clipShape = middleListItemShape
|
clipShape = bottomListItemShape
|
||||||
)
|
)
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
val insets = mergePaddingValues(innerPadding, contentPadding)
|
val insets = mergePaddingValues(innerPadding, contentPadding)
|
||||||
@@ -203,14 +206,14 @@ fun SharedTransitionScope.LastYearScreen(
|
|||||||
}
|
}
|
||||||
item {
|
item {
|
||||||
TimeLineChart(
|
TimeLineChart(
|
||||||
modelProducer = lastYearSummaryChartData.first,
|
modelProducer = mainChartData.first,
|
||||||
hoursFormat = hoursFormat,
|
hoursFormat = hoursFormat,
|
||||||
hoursMinutesFormat = hoursMinutesFormat,
|
hoursMinutesFormat = hoursMinutesFormat,
|
||||||
minutesFormat = minutesFormat,
|
minutesFormat = minutesFormat,
|
||||||
axisTypeface = axisTypeface,
|
axisTypeface = axisTypeface,
|
||||||
markerTypeface = markerTypeface,
|
markerTypeface = markerTypeface,
|
||||||
xValueFormatter = CartesianValueFormatter { context, x, _ ->
|
xValueFormatter = CartesianValueFormatter { context, x, _ ->
|
||||||
context.model.extraStore[lastYearSummaryChartData.second][x.toInt()]
|
context.model.extraStore[mainChartData.second][x.toInt()]
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.sharedElement(
|
.sharedElement(
|
||||||
@@ -235,10 +238,10 @@ fun SharedTransitionScope.LastYearScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
item { HorizontalStackedBar(lastYearAnalysisValues.first, rankList = rankList) }
|
item { HorizontalStackedBar(focusBreakdownValues.first, rankList = rankList) }
|
||||||
item {
|
item {
|
||||||
Row {
|
Row {
|
||||||
lastYearAnalysisValues.first.fastForEach {
|
focusBreakdownValues.first.fastForEach {
|
||||||
Text(
|
Text(
|
||||||
if (it <= 60 * 60 * 1000)
|
if (it <= 60 * 60 * 1000)
|
||||||
millisecondsToMinutes(it, minutesFormat)
|
millisecondsToMinutes(it, minutesFormat)
|
||||||
@@ -288,7 +291,7 @@ fun SharedTransitionScope.LastYearScreen(
|
|||||||
item {
|
item {
|
||||||
FocusBreakRatioVisualization(
|
FocusBreakRatioVisualization(
|
||||||
focusDuration = focusDuration,
|
focusDuration = focusDuration,
|
||||||
breakDuration = lastYearAnalysisValues.second
|
breakDuration = focusBreakdownValues.second
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,6 +308,13 @@ fun SharedTransitionScope.LastYearScreen(
|
|||||||
color = colorScheme.onSurfaceVariant
|
color = colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
HeatmapWithWeekLabels(
|
||||||
|
data = focusHeatmapData,
|
||||||
|
maxValue = heatmapMaxValue,
|
||||||
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
package org.nsh07.pomodoro.ui.statsScreen.viewModel
|
package org.nsh07.pomodoro.ui.statsScreen.viewModel
|
||||||
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
import androidx.compose.ui.util.fastMaxBy
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
|
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
|
||||||
@@ -29,17 +30,21 @@ import com.patrykandpatrick.vico.core.cartesian.data.columnSeries
|
|||||||
import com.patrykandpatrick.vico.core.cartesian.data.lineSeries
|
import com.patrykandpatrick.vico.core.cartesian.data.lineSeries
|
||||||
import com.patrykandpatrick.vico.core.common.data.ExtraStore
|
import com.patrykandpatrick.vico.core.common.data.ExtraStore
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.nsh07.pomodoro.BuildConfig
|
import org.nsh07.pomodoro.BuildConfig
|
||||||
import org.nsh07.pomodoro.TomatoApplication
|
import org.nsh07.pomodoro.TomatoApplication
|
||||||
import org.nsh07.pomodoro.data.Stat
|
import org.nsh07.pomodoro.data.Stat
|
||||||
import org.nsh07.pomodoro.data.StatRepository
|
import org.nsh07.pomodoro.data.StatRepository
|
||||||
import org.nsh07.pomodoro.ui.Screen
|
import org.nsh07.pomodoro.ui.Screen
|
||||||
|
import java.time.DayOfWeek
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.time.format.TextStyle
|
import java.time.format.TextStyle
|
||||||
@@ -68,8 +73,12 @@ class StatsViewModel(
|
|||||||
private val yearDayFormatter = DateTimeFormatter.ofPattern("d MMM")
|
private val yearDayFormatter = DateTimeFormatter.ofPattern("d MMM")
|
||||||
|
|
||||||
private val lastWeekStatsFlow = statRepository.getLastNDaysStats(7)
|
private val lastWeekStatsFlow = statRepository.getLastNDaysStats(7)
|
||||||
|
private val lastYearStatsFlow = statRepository.getLastNDaysStats(365)
|
||||||
|
|
||||||
val lastWeekSummaryChartData: StateFlow<Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>> =
|
private val _lastYearMaxFocus = MutableStateFlow(Long.MAX_VALUE)
|
||||||
|
val lastYearMaxFocus = _lastYearMaxFocus.asStateFlow()
|
||||||
|
|
||||||
|
val lastWeekMainChartData: StateFlow<Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>> =
|
||||||
lastWeekStatsFlow
|
lastWeekStatsFlow
|
||||||
.map { list ->
|
.map { list ->
|
||||||
// reversing is required because we need ascending order while the DB returns descending order
|
// reversing is required because we need ascending order while the DB returns descending order
|
||||||
@@ -94,7 +103,7 @@ class StatsViewModel(
|
|||||||
initialValue = lastWeekSummary
|
initialValue = lastWeekSummary
|
||||||
)
|
)
|
||||||
|
|
||||||
val lastWeekStats: StateFlow<List<Pair<String, List<Long>>>> =
|
val lastWeekFocusHistoryValues: StateFlow<List<Pair<String, List<Long>>>> =
|
||||||
lastWeekStatsFlow
|
lastWeekStatsFlow
|
||||||
.map { value ->
|
.map { value ->
|
||||||
value.reversed().map {
|
value.reversed().map {
|
||||||
@@ -119,7 +128,7 @@ class StatsViewModel(
|
|||||||
initialValue = emptyList()
|
initialValue = emptyList()
|
||||||
)
|
)
|
||||||
|
|
||||||
val lastWeekAverageFocusTimes: StateFlow<Pair<List<Long>, Long>> =
|
val lastWeekFocusBreakdownValues: StateFlow<Pair<List<Long>, Long>> =
|
||||||
statRepository.getLastNDaysAverageFocusTimes(7)
|
statRepository.getLastNDaysAverageFocusTimes(7)
|
||||||
.map {
|
.map {
|
||||||
Pair(
|
Pair(
|
||||||
@@ -178,8 +187,8 @@ class StatsViewModel(
|
|||||||
initialValue = Pair(listOf(0L, 0L, 0L, 0L), 0L)
|
initialValue = Pair(listOf(0L, 0L, 0L, 0L), 0L)
|
||||||
)
|
)
|
||||||
|
|
||||||
val lastYearSummaryChartData: StateFlow<Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>> =
|
val lastYearMainChartData: StateFlow<Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>> =
|
||||||
statRepository.getLastNDaysStats(365)
|
lastYearStatsFlow
|
||||||
.map { list ->
|
.map { list ->
|
||||||
val reversed = list.reversed()
|
val reversed = list.reversed()
|
||||||
val keys = reversed.map { it.date.format(yearDayFormatter) }
|
val keys = reversed.map { it.date.format(yearDayFormatter) }
|
||||||
@@ -197,7 +206,37 @@ class StatsViewModel(
|
|||||||
initialValue = lastYearSummary
|
initialValue = lastYearSummary
|
||||||
)
|
)
|
||||||
|
|
||||||
val lastYearAverageFocusTimes: StateFlow<Pair<List<Long>, Long>> =
|
val lastYearFocusHeatmapData: StateFlow<List<List<Long>?>> =
|
||||||
|
lastYearStatsFlow
|
||||||
|
.map { list ->
|
||||||
|
val list = list.reversed()
|
||||||
|
_lastYearMaxFocus.update {
|
||||||
|
list.fastMaxBy {
|
||||||
|
it.totalFocusTime()
|
||||||
|
}?.totalFocusTime() ?: Long.MAX_VALUE
|
||||||
|
}
|
||||||
|
buildList {
|
||||||
|
repeat(list.first().date.dayOfWeek.value - DayOfWeek.MONDAY.value) {
|
||||||
|
add(null) // Make sure that the data starts with a Monday
|
||||||
|
}
|
||||||
|
list.indices.forEach {
|
||||||
|
if (it > 0 && list[it].date.month != list[it - 1].date.month) {
|
||||||
|
repeat(7) { add(null) } // Add a week gap if a new month starts
|
||||||
|
}
|
||||||
|
with(list[it]) {
|
||||||
|
add(listOf(focusTimeQ1, focusTimeQ2, focusTimeQ3, focusTimeQ4))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flowOn(Dispatchers.IO)
|
||||||
|
.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(5000),
|
||||||
|
initialValue = emptyList()
|
||||||
|
)
|
||||||
|
|
||||||
|
val lastYearFocusBreakdownValues: StateFlow<Pair<List<Long>, Long>> =
|
||||||
statRepository.getLastNDaysAverageFocusTimes(365)
|
statRepository.getLastNDaysAverageFocusTimes(365)
|
||||||
.map {
|
.map {
|
||||||
Pair(
|
Pair(
|
||||||
@@ -221,7 +260,7 @@ class StatsViewModel(
|
|||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val today = LocalDate.now().plusDays(1)
|
val today = LocalDate.now().plusDays(1)
|
||||||
var it = today.minusDays(40)
|
var it = today.minusDays(365)
|
||||||
|
|
||||||
while (it.isBefore(today)) {
|
while (it.isBefore(today)) {
|
||||||
statRepository.insertStat(
|
statRepository.insertStat(
|
||||||
|
|||||||
Reference in New Issue
Block a user