Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,6 @@ dependencies {
implementation(libs.decompose.core)
implementation(libs.decompose.compose)

implementation(libs.vico.multiplatform)
implementation(libs.vico.multiplatform.m3)

// Wearable
implementation(libs.playServices.wearable)
// for connectedNodes.await()
Expand Down
3 changes: 3 additions & 0 deletions cmp-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ kotlin {
implementation(libs.koin.compose)

implementation(libs.coil.compose)

implementation(libs.vico.multiplatform)
implementation(libs.vico.multiplatform.m3)
}
val notWasm by getting {
dependencies {
Expand Down
15 changes: 15 additions & 0 deletions cmp-common/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,19 @@
<string name="map_short">Specialty</string>
<string name="add_drink">Add drink</string>

<string name="tab_weekly">Weekly</string>
<string name="tab_all_time">All Time</string>
<string name="chart_title_weekly">Coffee Consumption This Week</string>
<string name="chart_title_over_time">Coffee Consumption Over Time</string>
<string name="chart_title_distribution">Coffee Type Distribution</string>
<string name="no_data_available">No data available</string>

<string name="day_mon">Mon</string>
<string name="day_tue">Tue</string>
<string name="day_wed">Wed</string>
<string name="day_thu">Thu</string>
<string name="day_fri">Fri</string>
<string name="day_sat">Sat</string>
<string name="day_sun">Sun</string>

</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ interface RootComponent {
val component: CoffeeEditComponent,
) : Child

class Stats(
val component: StatsComponent,
) : Child

class Settings(
val component: SettingsComponent,
) : Child
Expand All @@ -41,7 +45,12 @@ class DefaultRootComponent(
childPages(
source = navigation,
serializer = Config.serializer(),
initialPages = { Pages(items = listOf(Config.CoffeeEdit, Config.Settings), selectedIndex = 0) },
initialPages = {
Pages(
items = listOf(Config.CoffeeEdit, Config.Stats, Config.Settings),
selectedIndex = 0
)
},
childFactory = ::child,
)

Expand All @@ -63,6 +72,13 @@ class DefaultRootComponent(
)
)

Config.Stats -> RootComponent.Child.Stats(
DefaultStatsComponent(
context = context,
daysCoffeesStore = daysCoffeesStore,
)
)

Config.Settings -> RootComponent.Child.Settings(
DefaultSettingsComponent(
context = context,
Expand All @@ -76,6 +92,9 @@ class DefaultRootComponent(
@Serializable
data object CoffeeEdit : Config

@Serializable
data object Stats : Config

@Serializable
data object Settings : Config
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ val settings = NavBarItem(
Icons.Default.LocationOn
)

internal fun getNavBarItems() = persistentListOf(calendar, settings)
internal fun getNavBarItems() = persistentListOf(calendar, stats, settings)

@Composable
@Suppress("ModifierMissing")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
package ru.beryukhov.coffeegram.pages

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SecondaryTabRow
import androidx.compose.material3.Tab
Expand All @@ -22,6 +28,20 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import coffeegram.cmp_common.generated.resources.Res
import coffeegram.cmp_common.generated.resources.chart_title_distribution
import coffeegram.cmp_common.generated.resources.chart_title_over_time
import coffeegram.cmp_common.generated.resources.chart_title_weekly
import coffeegram.cmp_common.generated.resources.day_fri
import coffeegram.cmp_common.generated.resources.day_mon
import coffeegram.cmp_common.generated.resources.day_sat
import coffeegram.cmp_common.generated.resources.day_sun
import coffeegram.cmp_common.generated.resources.day_thu
import coffeegram.cmp_common.generated.resources.day_tue
import coffeegram.cmp_common.generated.resources.day_wed
import coffeegram.cmp_common.generated.resources.no_data_available
import coffeegram.cmp_common.generated.resources.tab_all_time
import coffeegram.cmp_common.generated.resources.tab_weekly
import com.patrykandpatrick.vico.multiplatform.cartesian.CartesianChartHost
import com.patrykandpatrick.vico.multiplatform.cartesian.axis.HorizontalAxis
import com.patrykandpatrick.vico.multiplatform.cartesian.axis.VerticalAxis
Expand All @@ -36,12 +56,12 @@ import com.patrykandpatrick.vico.multiplatform.m3.common.rememberM3VicoTheme
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.datetime.DatePeriod
import kotlinx.datetime.DayOfWeek
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.minus
import kotlinx.datetime.plus
import kotlinx.datetime.toLocalDateTime
import org.jetbrains.compose.resources.stringResource
import ru.beryukhov.coffeegram.data.CoffeeType
import ru.beryukhov.coffeegram.data.CoffeeTypes
import ru.beryukhov.coffeegram.data.DayCoffee
Expand All @@ -54,7 +74,7 @@ import kotlin.time.ExperimentalTime
@Composable
fun CoffeeCharts(coffeeState: DaysCoffeesState, modifier: Modifier = Modifier) {
var selectedTabIndex by remember { mutableIntStateOf(0) }
val tabs = listOf("Weekly", "All Time")
val tabs = listOf(stringResource(Res.string.tab_weekly), stringResource(Res.string.tab_all_time))

Column(modifier = modifier.fillMaxWidth()) {
SecondaryTabRow(selectedTabIndex = selectedTabIndex) {
Expand Down Expand Up @@ -99,21 +119,31 @@ fun WeeklyCoffeeChart(coffeeState: DaysCoffeesState) {
}
}
Text(
text = "Coffee Consumption This Week",
text = stringResource(Res.string.chart_title_weekly),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)

Spacer(modifier = Modifier.height(16.dp))

val dayNames = listOf(
stringResource(Res.string.day_mon),
stringResource(Res.string.day_tue),
stringResource(Res.string.day_wed),
stringResource(Res.string.day_thu),
stringResource(Res.string.day_fri),
stringResource(Res.string.day_sat),
stringResource(Res.string.day_sun),
)

CartesianChartHost(
chart = rememberCartesianChart(
rememberColumnCartesianLayer(),
startAxis = VerticalAxis.rememberStart(),
bottomAxis = HorizontalAxis.rememberBottom(
valueFormatter = { _, value, _ ->
weekData.getOrNull(value.toInt())?.dayName ?: ""
dayNames.getOrNull(weekData.getOrNull(value.toInt())?.dayIndex ?: -1) ?: ""
}
),
),
Expand All @@ -133,15 +163,7 @@ internal fun weeklyChartData(

WeeklyChartData(
date = date,
dayName = when (date.dayOfWeek) {
DayOfWeek.MONDAY -> "Mon"
DayOfWeek.TUESDAY -> "Tue"
DayOfWeek.WEDNESDAY -> "Wed"
DayOfWeek.THURSDAY -> "Thu"
DayOfWeek.FRIDAY -> "Fri"
DayOfWeek.SATURDAY -> "Sat"
DayOfWeek.SUNDAY -> "Sun"
},
dayIndex = date.dayOfWeek.ordinal,
totalCoffees = totalForDay,
)
}
Expand All @@ -160,7 +182,7 @@ fun AllTimeCoffeeChart(coffeeState: DaysCoffeesState) {
.fillMaxWidth()
.padding(16.dp)
) {
Text("No data available")
Text(stringResource(Res.string.no_data_available))
}
return
}
Expand All @@ -184,36 +206,85 @@ fun AllTimeCoffeeChart(coffeeState: DaysCoffeesState) {
dailyAggregation(coffeeState)
}

Column(
BoxWithConstraints(
modifier = Modifier
.fillMaxWidth()
.fillMaxSize()
.padding(16.dp)
) {
Text(
text = "Coffee Consumption Over Time",
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
val isLandscape = maxWidth > maxHeight

if (isLandscape) {
// Horizontal layout for landscape
Row(
modifier = Modifier.fillMaxSize()
) {
// Left chart - Coffee Consumption Over Time
Column(
modifier = Modifier
.weight(1f)
.verticalScroll(rememberScrollState())
) {
Text(
text = stringResource(Res.string.chart_title_over_time),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(16.dp))
LineChart(aggregatedData.toImmutableList())
}

Spacer(modifier = Modifier.width(16.dp))

// Right chart - Coffee Type Distribution
Column(
modifier = Modifier
.weight(1f)
.verticalScroll(rememberScrollState())
) {
Text(
text = stringResource(Res.string.chart_title_distribution),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(16.dp))
ColumnChart(coffeeState)
}
}
} else {
// Vertical layout for portrait
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
) {
Text(
text = stringResource(Res.string.chart_title_over_time),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)

Spacer(modifier = Modifier.height(16.dp))
LineChart(aggregatedData.toImmutableList())
Spacer(modifier = Modifier.height(16.dp))
LineChart(aggregatedData.toImmutableList())

Spacer(modifier = Modifier.height(24.dp))
Spacer(modifier = Modifier.height(24.dp))

// Coffee type distribution
Text(
text = "Coffee Type Distribution",
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
Text(
text = stringResource(Res.string.chart_title_distribution),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)

Spacer(modifier = Modifier.height(16.dp))
Spacer(modifier = Modifier.height(16.dp))

ColumnChart(coffeeState)
ColumnChart(coffeeState)

Spacer(modifier = Modifier.height(16.dp))
Spacer(modifier = Modifier.height(16.dp))
}
}
}
}

Expand Down Expand Up @@ -317,7 +388,7 @@ internal fun monthlyAggregation(coffeeState: DaysCoffeesState): List<AggregatedD

data class WeeklyChartData(
val date: LocalDate,
val dayName: String,
val dayIndex: Int,
val totalCoffees: Int,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ private fun TopBar(rootComponent: RootComponent) {
) { index, page ->
when (val c = page) {
is RootComponent.Child.CoffeeEdit -> CoffeeEditAppBar(c.component)
is RootComponent.Child.Stats -> StatsAppBar()
is RootComponent.Child.Settings -> SettingsAppBar(c.component)
}
}
Expand All @@ -66,6 +67,7 @@ private fun CurrentScreen(
) { index, page ->
when (val c = page) {
is RootComponent.Child.CoffeeEdit -> CoffeeEditScreen(c.component)
is RootComponent.Child.Stats -> StatsScreen(c.component)
is RootComponent.Child.Settings -> SettingsScreen(c.component)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import ru.beryukhov.coffeegram.R
import coffeegram.cmp_common.generated.resources.Res
import coffeegram.cmp_common.generated.resources.stats
import org.jetbrains.compose.resources.stringResource
import ru.beryukhov.coffeegram.components.StatsComponent
import ru.beryukhov.coffeegram.pages.CoffeeCharts

Expand All @@ -31,7 +32,7 @@ fun StatsScreen(
@Composable
fun StatsAppBar(modifier: Modifier = Modifier) {
TopAppBar(
title = { Text(stringResource(R.string.stats)) },
title = { Text(stringResource(Res.string.stats)) },
modifier = modifier
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@ class CoffeeChartsDataTest {
)
assertEquals(
expected = listOf(
WeeklyChartData(date = LocalDate(2023, 1, 2), dayName = "Mon", totalCoffees = 3),
WeeklyChartData(date = LocalDate(2023, 1, 3), dayName = "Tue", totalCoffees = 4),
WeeklyChartData(date = LocalDate(2023, 1, 4), dayName = "Wed", totalCoffees = 0),
WeeklyChartData(date = LocalDate(2023, 1, 5), dayName = "Thu", totalCoffees = 0),
WeeklyChartData(date = LocalDate(2023, 1, 6), dayName = "Fri", totalCoffees = 0),
WeeklyChartData(date = LocalDate(2023, 1, 7), dayName = "Sat", totalCoffees = 0),
WeeklyChartData(date = LocalDate(2023, 1, 8), dayName = "Sun", totalCoffees = 0),

),
WeeklyChartData(date = LocalDate(2023, 1, 2), dayIndex = 0, totalCoffees = 3),
WeeklyChartData(date = LocalDate(2023, 1, 3), dayIndex = 1, totalCoffees = 4),
WeeklyChartData(date = LocalDate(2023, 1, 4), dayIndex = 2, totalCoffees = 0),
WeeklyChartData(date = LocalDate(2023, 1, 5), dayIndex = 3, totalCoffees = 0),
WeeklyChartData(date = LocalDate(2023, 1, 6), dayIndex = 4, totalCoffees = 0),
WeeklyChartData(date = LocalDate(2023, 1, 7), dayIndex = 5, totalCoffees = 0),
WeeklyChartData(date = LocalDate(2023, 1, 8), dayIndex = 6, totalCoffees = 0),
),
actual = actualData
)

Expand Down
Loading