feat(stats): implement showing last year stats in a line chart
This commit is contained in:
@@ -78,34 +78,18 @@ fun StatsScreenRoot(
|
||||
val lastMonthSummaryChartData by viewModel.lastMonthSummaryChartData.collectAsStateWithLifecycle()
|
||||
val lastMonthAnalysisValues by viewModel.lastMonthAverageFocusTimes.collectAsStateWithLifecycle()
|
||||
|
||||
val lastWeekSummaryAnalysisModelProducer = remember { CartesianChartModelProducer() }
|
||||
val lastMonthSummaryAnalysisModelProducer = remember { CartesianChartModelProducer() }
|
||||
|
||||
LaunchedEffect(lastWeekAnalysisValues) {
|
||||
lastWeekSummaryAnalysisModelProducer.runTransaction {
|
||||
columnSeries {
|
||||
series(lastWeekAnalysisValues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(lastMonthAnalysisValues) {
|
||||
lastMonthSummaryAnalysisModelProducer.runTransaction {
|
||||
columnSeries {
|
||||
series(lastMonthAnalysisValues)
|
||||
}
|
||||
}
|
||||
}
|
||||
val lastYearSummaryChartData by viewModel.lastYearSummaryChartData.collectAsStateWithLifecycle()
|
||||
val lastYearAnalysisValues by viewModel.lastYearAverageFocusTimes.collectAsStateWithLifecycle()
|
||||
|
||||
StatsScreen(
|
||||
contentPadding = contentPadding,
|
||||
lastWeekSummaryChartData = lastWeekSummaryChartData,
|
||||
lastWeekSummaryAnalysisModelProducer = lastWeekSummaryAnalysisModelProducer,
|
||||
lastMonthSummaryChartData = lastMonthSummaryChartData,
|
||||
lastMonthSummaryAnalysisModelProducer = lastMonthSummaryAnalysisModelProducer,
|
||||
lastYearSummaryChartData = lastYearSummaryChartData,
|
||||
todayStat = todayStat,
|
||||
lastWeekAverageFocusTimes = lastWeekAnalysisValues,
|
||||
lastMonthAverageFocusTimes = lastMonthAnalysisValues,
|
||||
lastYearAverageFocusTimes = lastYearAnalysisValues,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
@@ -115,12 +99,12 @@ fun StatsScreenRoot(
|
||||
fun StatsScreen(
|
||||
contentPadding: PaddingValues,
|
||||
lastWeekSummaryChartData: Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>,
|
||||
lastWeekSummaryAnalysisModelProducer: CartesianChartModelProducer,
|
||||
lastMonthSummaryChartData: Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>,
|
||||
lastMonthSummaryAnalysisModelProducer: CartesianChartModelProducer,
|
||||
lastYearSummaryChartData: Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>,
|
||||
todayStat: Stat?,
|
||||
lastWeekAverageFocusTimes: List<Int>,
|
||||
lastMonthAverageFocusTimes: List<Int>,
|
||||
lastYearAverageFocusTimes: List<Int>,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
@@ -128,6 +112,25 @@ fun StatsScreen(
|
||||
var lastWeekStatExpanded by rememberSaveable { mutableStateOf(false) }
|
||||
var lastMonthStatExpanded by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val lastWeekSummaryAnalysisModelProducer = remember { CartesianChartModelProducer() }
|
||||
val lastMonthSummaryAnalysisModelProducer = remember { CartesianChartModelProducer() }
|
||||
|
||||
LaunchedEffect(lastWeekAverageFocusTimes) {
|
||||
lastWeekSummaryAnalysisModelProducer.runTransaction {
|
||||
columnSeries {
|
||||
series(lastWeekAverageFocusTimes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(lastMonthAverageFocusTimes) {
|
||||
lastMonthSummaryAnalysisModelProducer.runTransaction {
|
||||
columnSeries {
|
||||
series(lastMonthAverageFocusTimes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
@@ -371,8 +374,51 @@ fun StatsScreen(
|
||||
modifier = Modifier.padding(horizontal = 32.dp)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
item { Spacer(Modifier) }
|
||||
item {
|
||||
Text(
|
||||
stringResource(R.string.last_year),
|
||||
style = typography.headlineSmall,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
)
|
||||
}
|
||||
item {
|
||||
Row(
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
millisecondsToHoursMinutes(
|
||||
remember(lastYearAverageFocusTimes) {
|
||||
lastYearAverageFocusTimes.sum().toLong()
|
||||
}
|
||||
),
|
||||
style = typography.displaySmall,
|
||||
fontFamily = openRundeClock
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.focus_per_day_avg),
|
||||
style = typography.titleSmall,
|
||||
modifier = Modifier.padding(bottom = 6.3.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
item {
|
||||
TimeLineChart(
|
||||
lastYearSummaryChartData.first,
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
xValueFormatter = CartesianValueFormatter { context, x, _ ->
|
||||
context.model.extraStore[lastYearSummaryChartData.second][x.toInt()]
|
||||
}
|
||||
)
|
||||
}
|
||||
item { Spacer(Modifier.height(16.dp)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -398,11 +444,11 @@ fun StatsScreenPreview() {
|
||||
StatsScreen(
|
||||
PaddingValues(),
|
||||
Pair(modelProducer, keys),
|
||||
modelProducer,
|
||||
Pair(modelProducer, keys),
|
||||
modelProducer,
|
||||
Pair(modelProducer, keys),
|
||||
null,
|
||||
listOf(0, 0, 0, 0),
|
||||
listOf(0, 0, 0, 0),
|
||||
listOf(0, 0, 0, 0)
|
||||
)
|
||||
}
|
||||
@@ -49,7 +49,7 @@ import org.nsh07.pomodoro.utils.millisecondsToMinutes
|
||||
|
||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||
@Composable
|
||||
internal fun TimeLineChart(
|
||||
fun TimeLineChart(
|
||||
modelProducer: CartesianChartModelProducer,
|
||||
modifier: Modifier = Modifier,
|
||||
thickness: Float = 2f,
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer
|
||||
import com.patrykandpatrick.vico.core.cartesian.data.columnSeries
|
||||
import com.patrykandpatrick.vico.core.cartesian.data.lineSeries
|
||||
import com.patrykandpatrick.vico.core.common.data.ExtraStore
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -23,6 +24,7 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import org.nsh07.pomodoro.TomatoApplication
|
||||
import org.nsh07.pomodoro.data.StatRepository
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.TextStyle
|
||||
import java.util.Locale
|
||||
|
||||
@@ -34,9 +36,12 @@ class StatsViewModel(
|
||||
|
||||
private val lastWeekSummary =
|
||||
Pair(CartesianChartModelProducer(), ExtraStore.Key<List<String>>())
|
||||
|
||||
private val lastMonthSummary =
|
||||
Pair(CartesianChartModelProducer(), ExtraStore.Key<List<String>>())
|
||||
private val lastYearSummary =
|
||||
Pair(CartesianChartModelProducer(), ExtraStore.Key<List<String>>())
|
||||
|
||||
private val yearDayFormatter = DateTimeFormatter.ofPattern("d MMM")
|
||||
|
||||
val lastWeekSummaryChartData: StateFlow<Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>> =
|
||||
statRepository.getLastNDaysStatsSummary(7)
|
||||
@@ -112,6 +117,40 @@ class StatsViewModel(
|
||||
initialValue = listOf(0, 0, 0, 0)
|
||||
)
|
||||
|
||||
val lastYearSummaryChartData: StateFlow<Pair<CartesianChartModelProducer, ExtraStore.Key<List<String>>>> =
|
||||
statRepository.getLastNDaysStatsSummary(365)
|
||||
.map { list ->
|
||||
val reversed = list.reversed()
|
||||
val keys = reversed.map { it.date.format(yearDayFormatter) }
|
||||
val values = reversed.map { it.focusTime }
|
||||
lastYearSummary.first.runTransaction {
|
||||
lineSeries { series(values) }
|
||||
extras { it[lastYearSummary.second] = keys }
|
||||
}
|
||||
lastYearSummary
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = lastYearSummary
|
||||
)
|
||||
|
||||
val lastYearAverageFocusTimes: StateFlow<List<Int>> =
|
||||
statRepository.getLastNDaysAverageFocusTimes(365)
|
||||
.map {
|
||||
listOf(
|
||||
it?.focusTimeQ1?.toInt() ?: 0,
|
||||
it?.focusTimeQ2?.toInt() ?: 0,
|
||||
it?.focusTimeQ3?.toInt() ?: 0,
|
||||
it?.focusTimeQ4?.toInt() ?: 0
|
||||
)
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = listOf(0, 0, 0, 0)
|
||||
)
|
||||
|
||||
companion object {
|
||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
|
||||
@@ -56,4 +56,5 @@
|
||||
<string name="up_next">Up next</string>
|
||||
<string name="timer">Timer</string>
|
||||
<string name="timer_progress">Timer progress</string>
|
||||
<string name="last_year">Last year</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user