diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 920cf3e2..63076eaf 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -28,8 +28,6 @@ dependencies { implementation(libs.lottie.compose) - implementation(libs.philJay.mpAndroidChart) - testImplementation(libs.test.junit4) androidTestImplementation(libs.androidx.test.ext) } diff --git a/core/ui/src/main/java/com/jacob/wakatimeapp/core/ui/components/RoundedBarChart.kt b/core/ui/src/main/java/com/jacob/wakatimeapp/core/ui/components/RoundedBarChart.kt deleted file mode 100644 index c367dc74..00000000 --- a/core/ui/src/main/java/com/jacob/wakatimeapp/core/ui/components/RoundedBarChart.kt +++ /dev/null @@ -1,198 +0,0 @@ -@file:Suppress("MagicNumber", "ComplexMethod", "LongMethod", "LoopWithTooManyJumpStatements") - -package com.jacob.wakatimeapp.core.ui.components - -import android.content.Context -import android.graphics.Canvas -import android.graphics.LinearGradient -import android.graphics.RectF -import android.graphics.Shader.TileMode.MIRROR -import android.util.AttributeSet -import com.github.mikephil.charting.animation.ChartAnimator -import com.github.mikephil.charting.charts.BarChart -import com.github.mikephil.charting.highlight.Highlight -import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider -import com.github.mikephil.charting.interfaces.datasets.IBarDataSet -import com.github.mikephil.charting.renderer.BarChartRenderer -import com.github.mikephil.charting.utils.Utils -import com.github.mikephil.charting.utils.ViewPortHandler -import kotlin.math.ceil -import kotlin.math.min - -/** - * [Source](https://gist.github.com/xanscale/e971cc4f2f0712a8a3bcc35e85325c27) - * */ -class RoundedBarChart : BarChart { - constructor(context: Context) : super(context) - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - setRadius(5) - } - - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super( - context, - attrs, - defStyle, - ) { - setRadius(5) - } - - fun setRadius(radius: Int) { - renderer = RoundedBarChartRenderer(this, animator, viewPortHandler, radius) - } - - private class RoundedBarChartRenderer( - chart: BarDataProvider?, - animator: ChartAnimator?, - viewPortHandler: ViewPortHandler?, - private val mRadius: Int, - ) : BarChartRenderer(chart, animator, viewPortHandler) { - private val mBarShadowRectBuffer = RectF() - override fun drawHighlighted(c: Canvas, indices: Array) { - val barData = mChart.barData - for (high in indices) { - val set = barData.getDataSetByIndex(high.dataSetIndex) - if (set == null || !set.isHighlightEnabled) continue - val e = set.getEntryForXValue(high.x, high.y) - if (!isInBoundsX(e, set)) continue - val trans = mChart.getTransformer(set.axisDependency) - mHighlightPaint.color = set.highLightColor - mHighlightPaint.alpha = set.highLightAlpha - val isStack = high.stackIndex >= 0 && e.isStacked - val y1: Float - val y2: Float - if (isStack) { - if (mChart.isHighlightFullBarEnabled) { - y1 = e.positiveSum - y2 = -e.negativeSum - } else { - val range = e.ranges[high.stackIndex] - y1 = range.from - y2 = range.to - } - } else { - y1 = e.y - y2 = 0f - } - prepareBarHighlight(e.x, y1, y2, barData.barWidth / 2f, trans) - setHighlightDrawPos(high, mBarRect) - c.drawRoundRect(mBarRect, mRadius.toFloat(), mRadius.toFloat(), mHighlightPaint) - } - } - - override fun drawDataSet(c: Canvas, dataSet: IBarDataSet, index: Int) { - val trans = mChart.getTransformer(dataSet.axisDependency) - mBarBorderPaint.color = dataSet.barBorderColor - mBarBorderPaint.strokeWidth = - Utils.convertDpToPixel(dataSet.barBorderWidth) - val drawBorder = dataSet.barBorderWidth > 0f - val phaseX = mAnimator.phaseX - val phaseY = mAnimator.phaseY - - // draw the bar shadow before the values - if (mChart.isDrawBarShadowEnabled) { - mShadowPaint.color = dataSet.barShadowColor - val barData = mChart.barData - val barWidth = barData.barWidth - val barWidthHalf = barWidth / 2.0f - var x: Float - var i = 0 - val count = min( - ceil((dataSet.entryCount * phaseX).toDouble()) - .toInt(), - dataSet.entryCount, - ) - while (i < count) { - val e = dataSet.getEntryForIndex(i) - x = e.x - mBarShadowRectBuffer.left = x - barWidthHalf - mBarShadowRectBuffer.right = x + barWidthHalf - trans.rectValueToPixel(mBarShadowRectBuffer) - if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right)) { - i++ - continue - } - if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left)) break - mBarShadowRectBuffer.top = mViewPortHandler.contentTop() - mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom() - c.drawRoundRect( - mBarShadowRectBuffer, - mRadius.toFloat(), - mRadius.toFloat(), - mShadowPaint, - ) - i++ - } - } - - // initialize the buffer - val buffer = mBarBuffers[index] - buffer.setPhases(phaseX, phaseY) - buffer.setDataSet(index) - buffer.setInverted(mChart.isInverted(dataSet.axisDependency)) - buffer.setBarWidth(mChart.barData.barWidth) - buffer.feed(dataSet) - trans.pointValuesToPixel(buffer.buffer) - val isSingleColor = dataSet.colors.size == 1 - if (isSingleColor) { - mRenderPaint.color = dataSet.color - } - var j = 0 - while (j < buffer.size()) { - if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2])) { - j += 4 - continue - } - if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j])) break - if (!isSingleColor) { - // Set the color for the currently drawn value. If the index - // is out of bounds, reuse colors. - mRenderPaint.color = dataSet.getColor(j / 4) - } - if (dataSet.gradientColor != null) { - val gradientColor = dataSet.gradientColor - mRenderPaint.shader = LinearGradient( - buffer.buffer[j], - buffer.buffer[j + 3], - buffer.buffer[j], - buffer.buffer[j + 1], - gradientColor.startColor, - gradientColor.endColor, - MIRROR, - ) - } - if (dataSet.gradientColors != null) { - mRenderPaint.shader = LinearGradient( - buffer.buffer[j], - buffer.buffer[j + 3], - buffer.buffer[j], - buffer.buffer[j + 1], - dataSet.getGradientColor(j / 4).startColor, - dataSet.getGradientColor(j / 4).endColor, - MIRROR, - ) - } - c.drawRoundRect( - buffer.buffer[j], - buffer.buffer[j + 1], - buffer.buffer[j + 2], - buffer.buffer[j + 3], - mRadius.toFloat(), - mRadius.toFloat(), - mRenderPaint, - ) - if (drawBorder) { - c.drawRoundRect( - buffer.buffer[j], - buffer.buffer[j + 1], - buffer.buffer[j + 2], - buffer.buffer[j + 3], - mRadius.toFloat(), - mRadius.toFloat(), - mBarBorderPaint, - ) - } - j += 4 - } - } - } -} diff --git a/core/ui/src/main/java/com/jacob/wakatimeapp/core/ui/theme/Type.kt b/core/ui/src/main/java/com/jacob/wakatimeapp/core/ui/theme/Type.kt index 1bbba90b..66dad223 100644 --- a/core/ui/src/main/java/com/jacob/wakatimeapp/core/ui/theme/Type.kt +++ b/core/ui/src/main/java/com/jacob/wakatimeapp/core/ui/theme/Type.kt @@ -43,8 +43,8 @@ val Typography = defaultTypography.let { bodyMedium = it.bodyMedium.setDefaultFontFamily(), bodySmall = it.bodySmall.setDefaultFontFamily(), labelLarge = it.labelLarge.setDefaultFontFamily(), - labelMedium = it.labelMedium.setDefaultFontFamily(), - labelSmall = it.labelSmall.setDefaultFontFamily(), + labelMedium = it.labelMedium.setDefaultFontFamily().copy(fontSize = 10.sp), + labelSmall = it.labelSmall.setDefaultFontFamily().copy(fontSize = 8.sp), ) } diff --git a/details/build.gradle.kts b/details/build.gradle.kts index d54a966f..31414eb3 100644 --- a/details/build.gradle.kts +++ b/details/build.gradle.kts @@ -9,6 +9,4 @@ android { dependencies { implementation(libs.accompanist.pager) implementation(libs.accompanist.pager.indicators) - - implementation(libs.philJay.mpAndroidChart) } diff --git a/details/src/main/java/com/jacob/wakatimeapp/details/ui/components/TimeTab.kt b/details/src/main/java/com/jacob/wakatimeapp/details/ui/components/TimeTab.kt index 0d7cbc49..46a36185 100644 --- a/details/src/main/java/com/jacob/wakatimeapp/details/ui/components/TimeTab.kt +++ b/details/src/main/java/com/jacob/wakatimeapp/details/ui/components/TimeTab.kt @@ -18,6 +18,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.tooling.data.EmptyGroup.data import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView import com.github.mikephil.charting.animation.Easing @@ -29,7 +30,6 @@ import com.github.mikephil.charting.data.BarEntry import com.github.mikephil.charting.formatter.ValueFormatter import com.jacob.wakatimeapp.core.common.utils.getDisplayNameForDay import com.jacob.wakatimeapp.core.models.Time -import com.jacob.wakatimeapp.core.ui.components.RoundedBarChart import com.jacob.wakatimeapp.core.ui.theme.spacing import kotlinx.collections.immutable.ImmutableCollection import kotlinx.collections.immutable.ImmutableMap diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 97d93bd3..3f43e080 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -44,12 +44,12 @@ timber = "5.0.1" # https://github.com/JakeWharton/timber coil = "2.4.0" # https://github.com/coil-kt/coil appAuth = "0.11.1" # https://github.com/openid/AppAuth-Android lottieCompose = "6.1.0" # https://github.com/airbnb/lottie-android -detekt = "1.23.1" # https://detekt.dev +detekt = "1.23.3" # https://detekt.dev gradleVersionsPlugin = "0.48.0" # https://github.com/ben-manes/gradle-versions-plugin turbine = "0.13.0" # https://github.com/cashapp/turbine/ mockk = "1.13.7" # https://mockk.io mannodermausAndroidJunit5 = "1.9.3.0" # https://github.com/mannodermaus/android-junit5 -mpAndroidChart = "v3.1.0" +vico = "1.13.1" detektVersion = "0.0.26" detektFormatting = "1.22.0" @@ -121,7 +121,7 @@ arrow-kt-core = { module = "io.arrow-kt:arrow-core", version.ref = "arrow" } lottie-compose = { module = "com.airbnb.android:lottie-compose", version.ref = "lottieCompose" } timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" } appAuth = { module = "net.openid:appauth", version.ref = "appAuth" } -philJay-mpAndroidChart = { module = "com.github.PhilJay:MPAndroidChart", version.ref = "mpAndroidChart" } +vico-compose-m3 = { group = "com.patrykandpatrick.vico", name = "compose-m3", version.ref = "vico" } detekt-gradle-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } gradle-version-plugin = { module = "com.github.ben-manes:gradle-versions-plugin", version.ref = "gradleVersionsPlugin" } diff --git a/home/build.gradle.kts b/home/build.gradle.kts index e0647d18..36eb9cf1 100644 --- a/home/build.gradle.kts +++ b/home/build.gradle.kts @@ -9,7 +9,7 @@ android { dependencies { // Image Loading, Charts, Lottie Animations implementation(libs.coil.kt.compose) - implementation(libs.philJay.mpAndroidChart) + implementation(libs.vico.compose.m3) implementation(libs.androidx.dataStore.preferences) } diff --git a/home/src/main/java/com/jacob/wakatimeapp/home/ui/HomePage.kt b/home/src/main/java/com/jacob/wakatimeapp/home/ui/HomePage.kt index 7dd7c443..6c1c02d1 100644 --- a/home/src/main/java/com/jacob/wakatimeapp/home/ui/HomePage.kt +++ b/home/src/main/java/com/jacob/wakatimeapp/home/ui/HomePage.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.PreviewParameter @@ -33,6 +34,7 @@ import com.jacob.wakatimeapp.core.ui.theme.spacing import com.jacob.wakatimeapp.home.domain.models.HomePageUserDetails import com.jacob.wakatimeapp.home.domain.models.Last7DaysStats import com.jacob.wakatimeapp.home.domain.models.Streak +import com.jacob.wakatimeapp.home.ui.HomePageViewState.Loaded import com.jacob.wakatimeapp.home.ui.components.OtherDailyStatsSection import com.jacob.wakatimeapp.home.ui.components.RecentProjects import com.jacob.wakatimeapp.home.ui.components.UserDetailsSection @@ -61,6 +63,7 @@ private fun HomePageScreen( ) { val snackBarCoroutineScope = rememberCoroutineScope() val viewState by viewModel.homePageState.collectAsState() + val todaysDate = remember { viewModel.getTodaysDate() } LaunchedEffect(viewState) { Timber.d("viewState: $viewState") @@ -79,6 +82,7 @@ private fun HomePageScreen( viewState = viewState, toDetailsPage = navigator::toProjectDetailsPage, toSearchPage = navigator::toSearchPage, + todaysDate = todaysDate, modifier = modifier, ) } @@ -89,6 +93,7 @@ private fun HomePageContent( toDetailsPage: (String) -> Unit, toSearchPage: () -> Unit, modifier: Modifier = Modifier, + todaysDate: LocalDate, ) { Column(modifier = modifier.statusBarsPadding()) { when (viewState) { @@ -97,6 +102,7 @@ private fun HomePageContent( homePageViewState = viewState, toDetailsPage = toDetailsPage, toSearchPage = toSearchPage, + todaysDate = todaysDate, ) is HomePageViewState.Error -> HomePageError(viewState) @@ -106,9 +112,10 @@ private fun HomePageContent( @Composable private fun HomePageLoaded( - homePageViewState: HomePageViewState.Loaded, + homePageViewState: Loaded, toDetailsPage: (String) -> Unit, toSearchPage: () -> Unit = {}, + todaysDate: LocalDate, ) { val scrollState = rememberScrollState() val spacing = MaterialTheme.spacing @@ -140,7 +147,7 @@ private fun HomePageLoaded( ) Spacer(modifier = Modifier.height(spacing.extraSmall)) - WeeklyReport(homePageViewState.last7DaysStats.weeklyTimeSpent) + WeeklyReport(homePageViewState.last7DaysStats.weeklyTimeSpent, today = todaysDate) Spacer(modifier = Modifier.height(spacing.small)) OtherDailyStatsSection( @@ -175,7 +182,7 @@ private fun HomePageLoading() = WtaAnimation( private fun HomePagePreview( @PreviewParameter(HomePagePreviewProvider::class) viewState: HomePageViewState, ) = WakaTimeAppTheme { - HomePageContent(viewState = viewState, toDetailsPage = { }, toSearchPage = { }) + HomePageContent(viewState = viewState, toDetailsPage = { }, toSearchPage = { }, todaysDate = LocalDate(2023, 1, 1)) } private class HomePagePreviewProvider : CollectionPreviewParameterProvider( diff --git a/home/src/main/java/com/jacob/wakatimeapp/home/ui/HomePageViewModel.kt b/home/src/main/java/com/jacob/wakatimeapp/home/ui/HomePageViewModel.kt index a2aad664..08b25bb3 100644 --- a/home/src/main/java/com/jacob/wakatimeapp/home/ui/HomePageViewModel.kt +++ b/home/src/main/java/com/jacob/wakatimeapp/home/ui/HomePageViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import arrow.core.Either import arrow.core.raise.either +import com.jacob.wakatimeapp.core.common.utils.InstantProvider import com.jacob.wakatimeapp.core.common.utils.log import com.jacob.wakatimeapp.home.domain.usecases.CalculateCurrentStreakUC import com.jacob.wakatimeapp.home.domain.usecases.CalculateLongestStreakUC @@ -17,6 +18,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import kotlinx.datetime.LocalDate @HiltViewModel internal class HomePageViewModel @Inject constructor( @@ -25,6 +27,7 @@ internal class HomePageViewModel @Inject constructor( private val calculateCurrentStreakUC: CalculateCurrentStreakUC, private val calculateLongestStreakUC: CalculateLongestStreakUC, private val updateCachedHomePageUiData: UpdateCachedHomePageUiData, + private val instantProvider: InstantProvider, ioDispatcher: CoroutineContext = Dispatchers.IO, ) : ViewModel() { @@ -84,4 +87,6 @@ internal class HomePageViewModel @Inject constructor( longestStreak = longestStreak, ) } + + fun getTodaysDate(): LocalDate = instantProvider.date() } diff --git a/home/src/main/java/com/jacob/wakatimeapp/home/ui/components/WeeklyReport.kt b/home/src/main/java/com/jacob/wakatimeapp/home/ui/components/WeeklyReport.kt index 94980fda..b32ac484 100644 --- a/home/src/main/java/com/jacob/wakatimeapp/home/ui/components/WeeklyReport.kt +++ b/home/src/main/java/com/jacob/wakatimeapp/home/ui/components/WeeklyReport.kt @@ -1,10 +1,6 @@ -@file: Suppress("MagicNumber") - package com.jacob.wakatimeapp.home.ui.components import android.content.res.Configuration -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import android.widget.LinearLayout.LayoutParams import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -12,7 +8,6 @@ import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.ColorScheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -23,30 +18,38 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.viewinterop.AndroidView -import com.github.mikephil.charting.animation.Easing -import com.github.mikephil.charting.components.AxisBase -import com.github.mikephil.charting.components.XAxis.XAxisPosition.BOTTOM -import com.github.mikephil.charting.data.BarData -import com.github.mikephil.charting.data.BarDataSet -import com.github.mikephil.charting.data.BarEntry -import com.github.mikephil.charting.formatter.ValueFormatter import com.jacob.wakatimeapp.core.common.utils.getDisplayNameForDay import com.jacob.wakatimeapp.core.models.Time -import com.jacob.wakatimeapp.core.ui.components.RoundedBarChart import com.jacob.wakatimeapp.core.ui.theme.WakaTimeAppTheme import com.jacob.wakatimeapp.core.ui.theme.sectionSubtitle import com.jacob.wakatimeapp.core.ui.theme.sectionTitle import com.jacob.wakatimeapp.core.ui.theme.spacing +import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis +import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis +import com.patrykandpatrick.vico.compose.chart.Chart +import com.patrykandpatrick.vico.compose.chart.column.columnChart +import com.patrykandpatrick.vico.core.DEF_LABEL_COUNT +import com.patrykandpatrick.vico.core.axis.AxisItemPlacer +import com.patrykandpatrick.vico.core.chart.values.AxisValuesOverrider +import com.patrykandpatrick.vico.core.chart.values.ChartValues +import com.patrykandpatrick.vico.core.component.shape.LineComponent +import com.patrykandpatrick.vico.core.component.shape.Shapes +import com.patrykandpatrick.vico.core.component.text.textComponent +import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer +import com.patrykandpatrick.vico.core.entry.entryOf +import com.patrykandpatrick.vico.core.extension.ceil +import com.patrykandpatrick.vico.core.formatter.ValueFormatter import kotlinx.collections.immutable.ImmutableCollection import kotlinx.collections.immutable.ImmutableMap -import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.toImmutableMap +import kotlinx.datetime.DateTimeUnit import kotlinx.datetime.LocalDate +import kotlinx.datetime.minus @Composable -fun WeeklyReport( +internal fun WeeklyReport( weeklyTimeSpent: ImmutableMap, + today: LocalDate, modifier: Modifier = Modifier, ) = Column( modifier = modifier.fillMaxWidth(), @@ -65,152 +68,93 @@ fun WeeklyReport( style = typography.sectionSubtitle, ) } - WeeklyReportChart(weeklyTimeSpent) -} - -@Composable -private fun WeeklyReportChart(weeklyTimeSpent: ImmutableMap) { - val cardShape = RoundedCornerShape(percent = 10) - val colorScheme = MaterialTheme.colorScheme - - val labels = rememberLabels(weeklyTimeSpent.keys) - val barData = rememberBarData(weeklyTimeSpent.values, colorScheme) - - val spacing = MaterialTheme.spacing - - Surface( - modifier = Modifier - .padding(horizontal = spacing.small) - .aspectRatio(1.4f), - shape = cardShape, - shadowElevation = 10.dp, - tonalElevation = 2.dp, - ) { - val onSurface = MaterialTheme.colorScheme.onSurface.toArgb() - AndroidView( - modifier = Modifier.padding(spacing.small), - factory = { - RoundedBarChart(it).apply { - layoutParams = LayoutParams(MATCH_PARENT, MATCH_PARENT) - data = barData - - configureChartProperties() - configureAxis(labels, onSurface) - - invalidate() - } - }, - update = { - it.data = barData - it.xAxis.valueFormatter = XAxisDayFormatter(labels) - it.invalidate() - }, - ) - } + WeeklyReportChart(weeklyTimeSpent, today) } @Composable -private fun rememberLabels(days: ImmutableSet): ImmutableMap = remember { - days.mapIndexed { index, value -> - index to value.getDisplayNameForDay() - }.toMap() - .toImmutableMap() +private fun WeeklyReportChart(weeklyTimeSpent: ImmutableMap, today: LocalDate) = Surface( + modifier = Modifier + .padding(horizontal = MaterialTheme.spacing.small) + .aspectRatio(ratio = 1.4f), + shape = RoundedCornerShape(percent = 10), + shadowElevation = 10.dp, + tonalElevation = 2.dp, +) { + VicoBarChart( + modifier = Modifier.padding(MaterialTheme.spacing.small), + entries = rememberChartEntries(weeklyStats = weeklyTimeSpent.values), + labelFormatter = object : ValueFormatter { + override fun formatValue( + value: Float, + chartValues: ChartValues, + ) = Time.fromDecimal(value).formattedPrint() + }, + yAxisFormatter = { value, _ -> "${value.toInt()}H" }, + xAxisFormatter = { value, chartValues -> + today.minus( + chartValues.chartEntryModel.entries.size - value.toLong(), + DateTimeUnit.DAY, + ).getDisplayNameForDay() + }, + ) } @Composable -private fun rememberBarData(weeklyStats: ImmutableCollection