Skip to content

Commit

Permalink
feat(android): pipeline for statistics chart to UI, basic UI for empt…
Browse files Browse the repository at this point in the history
…y widgets
  • Loading branch information
autoreleasefool committed Mar 15, 2024
1 parent 00800f1 commit 4816546
Show file tree
Hide file tree
Showing 22 changed files with 363 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ fun StatisticChartContent.getModelEntries(): List<ChartEntry> = when (this) {
else -> throw IllegalStateException("Unsupported chart type: $this")
}

fun StatisticChartContent.hasModelEntries(): Boolean = when (this) {
is StatisticChartContent.CountableChart,
is StatisticChartContent.AveragingChart,
is StatisticChartContent.PercentageChart,
-> true
else -> false
}

fun CountableChartData.getModelEntries(): List<ChartEntry> = entries.map {
entryOf(it.key.value, it.value.toFloat())
}
Expand Down
3 changes: 3 additions & 0 deletions android/feature/bowlerdetails/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ android {

dependencies {
implementation(project(":core:statistics"))
implementation(project(":core:statistics:charts"))
implementation(project(":feature:bowlerdetails:ui"))
implementation(project(":feature:leagueslist:ui"))
implementation(project(":feature:gearlist:ui"))
implementation(project(":feature:statisticswidget:ui"))

implementation(libs.vico.compose)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import ca.josephroque.bowlingcompanion.core.data.repository.UserDataRepository
import ca.josephroque.bowlingcompanion.core.model.LeagueListItem
import ca.josephroque.bowlingcompanion.core.model.LeagueRecurrence
import ca.josephroque.bowlingcompanion.core.navigation.Route
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticChartContent
import ca.josephroque.bowlingcompanion.core.statistics.charts.utils.getModelEntries
import ca.josephroque.bowlingcompanion.core.statistics.charts.utils.hasModelEntries
import ca.josephroque.bowlingcompanion.feature.bowlerdetails.ui.BowlerDetailsTopBarUiState
import ca.josephroque.bowlingcompanion.feature.bowlerdetails.ui.BowlerDetailsUiAction
import ca.josephroque.bowlingcompanion.feature.bowlerdetails.ui.BowlerDetailsUiState
Expand All @@ -23,6 +24,7 @@ import ca.josephroque.bowlingcompanion.feature.leagueslist.ui.LeaguesListUiActio
import ca.josephroque.bowlingcompanion.feature.leagueslist.ui.LeaguesListUiState
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.layout.StatisticsWidgetLayoutUiAction
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.layout.StatisticsWidgetLayoutUiState
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.UUID
import javax.inject.Inject
Expand Down Expand Up @@ -79,29 +81,34 @@ class BowlerDetailsViewModel @Inject constructor(
}
}

private val widgetCharts: MutableStateFlow<Map<UUID, StatisticChartContent>> = MutableStateFlow(
emptyMap(),
)
private val widgetCharts: MutableStateFlow<Map<UUID, StatisticsWidgetLayoutUiState.ChartContent>> =
MutableStateFlow(emptyMap())

val uiState: StateFlow<BowlerDetailsScreenUiState> = combine(
leaguesListState,
private val widgetLayoutState = combine(
widgets,
widgetCharts,
) { widgets, widgetCharts ->
widgets?.let {
StatisticsWidgetLayoutUiState(
widgets = it,
widgetCharts = widgetCharts,
)
}
}

val uiState: StateFlow<BowlerDetailsScreenUiState> = combine(
leaguesListState,
widgetLayoutState,
bowlersRepository.getBowlerDetails(bowlerId),
gearRepository.getBowlerPreferredGear(bowlerId),
) { leaguesList, widgets, widgetCharts, bowlerDetails, gearList ->
) { leaguesList, widgets, bowlerDetails, gearList ->
BowlerDetailsScreenUiState.Loaded(
bowler = BowlerDetailsUiState(
bowler = bowlerDetails,
leaguesList = leaguesList,
gearList = GearListUiState(gearList, gearToDelete = null),
topBar = BowlerDetailsTopBarUiState(bowlerDetails.name),
widgets = widgets?.let {
StatisticsWidgetLayoutUiState(
widgets = it,
widgetCharts = widgetCharts,
)
},
widgets = widgets,
),
)
}.stateIn(
Expand All @@ -120,8 +127,18 @@ class BowlerDetailsViewModel @Inject constructor(
widgets?.forEach { widget ->
launch {
val chart = statisticsWidgetsRepository.getStatisticsWidgetChart(widget)

widgetCharts.update {
it + (widget.id to chart)
val widgetChart = it[widget.id] ?: StatisticsWidgetLayoutUiState.ChartContent(
chart,
ChartEntryModelProducer(),
)

if (widgetChart.chart.hasModelEntries()) {
widgetChart.modelProducer.setEntries(chart.getModelEntries())
}

it + (widget.id to widgetChart.copy(chart = chart))
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions android/feature/bowlerdetails/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ dependencies {
implementation(project(":feature:gearlist:ui"))
implementation(project(":feature:leagueslist:ui"))
implementation(project(":feature:statisticswidget:ui"))

implementation(libs.vico.compose)
}
2 changes: 2 additions & 0 deletions android/feature/overview/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ android {

dependencies {
implementation(project(":core:statistics"))
implementation(project(":core:statistics:charts"))
implementation(project(":feature:bowlerslist:ui"))
implementation(project(":feature:overview:ui"))
implementation(project(":feature:statisticswidget:ui"))

implementation(libs.kotlinx.datetime)
implementation(libs.vico.compose)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import ca.josephroque.bowlingcompanion.core.data.repository.StatisticsWidgetsRep
import ca.josephroque.bowlingcompanion.core.data.repository.UserDataRepository
import ca.josephroque.bowlingcompanion.core.model.BowlerKind
import ca.josephroque.bowlingcompanion.core.model.BowlerListItem
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticChartContent
import ca.josephroque.bowlingcompanion.core.statistics.charts.utils.getModelEntries
import ca.josephroque.bowlingcompanion.core.statistics.charts.utils.hasModelEntries
import ca.josephroque.bowlingcompanion.feature.bowlerslist.ui.BowlersListUiAction
import ca.josephroque.bowlingcompanion.feature.bowlerslist.ui.BowlersListUiState
import ca.josephroque.bowlingcompanion.feature.overview.ui.OverviewUiAction
import ca.josephroque.bowlingcompanion.feature.overview.ui.OverviewUiState
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.layout.StatisticsWidgetLayoutUiAction
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.layout.StatisticsWidgetLayoutUiState
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.UUID
import javax.inject.Inject
Expand Down Expand Up @@ -66,25 +68,30 @@ class OverviewViewModel @Inject constructor(
}
}

private val widgetCharts: MutableStateFlow<Map<UUID, StatisticChartContent>> = MutableStateFlow(
emptyMap(),
)
private val widgetCharts: MutableStateFlow<Map<UUID, StatisticsWidgetLayoutUiState.ChartContent>> =
MutableStateFlow(emptyMap())

val uiState: StateFlow<OverviewScreenUiState> = combine(
bowlersListState,
private val widgetLayoutState = combine(
widgets,
widgetCharts,
) { widgets, widgetCharts ->
widgets?.let {
StatisticsWidgetLayoutUiState(
widgets = it,
widgetCharts = widgetCharts,
)
}
}

val uiState: StateFlow<OverviewScreenUiState> = combine(
bowlersListState,
widgetLayoutState,
isGameInProgressSnackBarVisible,
) { bowlersList, widgets, widgetCharts, isGameInProgressSnackBarVisible ->
) { bowlersList, widgets, isGameInProgressSnackBarVisible ->
OverviewScreenUiState.Loaded(
overview = OverviewUiState(
bowlersList = bowlersList,
widgets = widgets?.let {
StatisticsWidgetLayoutUiState(
widgets = it,
widgetCharts = widgetCharts,
)
},
widgets = widgets,
),
isGameInProgressSnackBarVisible = isGameInProgressSnackBarVisible,
)
Expand All @@ -95,13 +102,24 @@ class OverviewViewModel @Inject constructor(
)

init {
// FIXME: Share with BowlerDetailsViewModel, StatisticsWidgetLayoutEditorViewModel?
viewModelScope.launch {
widgets.collect { widgets ->
widgets?.forEach { widget ->
launch {
val chart = statisticsWidgetsRepository.getStatisticsWidgetChart(widget)

widgetCharts.update {
it + (widget.id to chart)
val widgetChart = it[widget.id] ?: StatisticsWidgetLayoutUiState.ChartContent(
chart,
ChartEntryModelProducer(),
)

if (widgetChart.chart.hasModelEntries()) {
widgetChart.modelProducer.setEntries(chart.getModelEntries())
}

it + (widget.id to widgetChart.copy(chart = chart))
}
}
}
Expand Down
1 change: 1 addition & 0 deletions android/feature/overview/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ dependencies {

implementation(libs.compose.reorderable)
implementation(libs.swipe)
implementation(libs.vico.compose)
}
3 changes: 3 additions & 0 deletions android/feature/statisticswidget/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ android {

dependencies {
implementation(project(":core:statistics"))
implementation(project(":core:statistics:charts"))
implementation(project(":feature:statisticswidget:ui"))

implementation(libs.vico.compose)
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import ca.josephroque.bowlingcompanion.core.model.LeagueSummary
import ca.josephroque.bowlingcompanion.core.navigation.Route
import ca.josephroque.bowlingcompanion.core.statistics.Statistic
import ca.josephroque.bowlingcompanion.core.statistics.allStatistics
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticChartContent
import ca.josephroque.bowlingcompanion.core.statistics.charts.utils.getModelEntries
import ca.josephroque.bowlingcompanion.core.statistics.charts.utils.hasModelEntries
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticsWidget
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticsWidgetCreate
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticsWidgetSource
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticsWidgetTimeline
import ca.josephroque.bowlingcompanion.core.statistics.trackable.overall.GameAverageStatistic
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.editor.StatisticsWidgetEditorUiAction
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.editor.StatisticsWidgetEditorUiState
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.UUID
import javax.inject.Inject
Expand Down Expand Up @@ -113,9 +115,19 @@ class StatisticsWidgetEditorViewModel @Inject constructor(
)
}

private val preview: Flow<StatisticChartContent?> = widget.map {
private val preview: Flow<StatisticsWidgetEditorUiState.ChartContent?> = widget.map {
it ?: return@map null
statisticsWidgetsRepository.getStatisticsWidgetChart(it)
val chart = statisticsWidgetsRepository.getStatisticsWidgetChart(it)
val modelProducer = ChartEntryModelProducer()

if (chart.hasModelEntries()) {
modelProducer.setEntries(chart.getModelEntries())
}

StatisticsWidgetEditorUiState.ChartContent(
chart = chart,
modelProducer = modelProducer,
)
}

val uiState: StateFlow<StatisticsWidgetEditorScreenUiState> = combine(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import ca.josephroque.bowlingcompanion.core.analytics.trackable.widget.WidgetLay
import ca.josephroque.bowlingcompanion.core.common.viewmodel.ApproachViewModel
import ca.josephroque.bowlingcompanion.core.data.repository.StatisticsWidgetsRepository
import ca.josephroque.bowlingcompanion.core.navigation.Route
import ca.josephroque.bowlingcompanion.core.statistics.charts.utils.getModelEntries
import ca.josephroque.bowlingcompanion.core.statistics.charts.utils.hasModelEntries
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticsWidget
import ca.josephroque.bowlingcompanion.feature.statisticswidget.editor.StatisticsWidgetInitialSource
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.layout.editor.StatisticsWidgetLayoutEditorTopBarUiState
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.layout.editor.StatisticsWidgetLayoutEditorUiAction
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.layout.editor.StatisticsWidgetLayoutEditorUiState
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import dagger.hilt.android.lifecycle.HiltViewModel
import java.util.UUID
import javax.inject.Inject
Expand Down Expand Up @@ -82,9 +85,23 @@ class StatisticsWidgetLayoutEditorViewModel @Inject constructor(
launch {
val chart = statisticsWidgetRepository.getStatisticsWidgetChart(widget)
_uiState.updateWidgets {
val widgetChart = it.layoutEditor.widgetCharts[widget.id]
?: StatisticsWidgetLayoutEditorUiState.ChartContent(
chart,
ChartEntryModelProducer(),
)

if (widgetChart.chart.hasModelEntries()) {
widgetChart.modelProducer.setEntries(chart.getModelEntries())
}

it.copy(
layoutEditor = it.layoutEditor.copy(
widgetCharts = it.layoutEditor.widgetCharts + (widget.id to chart),
widgetCharts = it.layoutEditor.widgetCharts + (
widget.id to widgetChart.copy(
chart = chart,
)
),
),
)
}
Expand Down
2 changes: 2 additions & 0 deletions android/feature/statisticswidget/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ android {

dependencies {
implementation(project(":core:statistics"))
implementation(project(":core:statistics:charts"))

implementation(libs.compose.reorderable)
implementation(libs.vico.compose)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ca.josephroque.bowlingcompanion.feature.statisticswidget.ui

import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticsWidgetTimeline

fun StatisticsWidgetTimeline.titleResourceId(): Int = when (this) {
StatisticsWidgetTimeline.ALL_TIME -> R.string.statistics_widget_timeline_all_time
StatisticsWidgetTimeline.ONE_YEAR -> R.string.statistics_widget_timeline_one_year
StatisticsWidgetTimeline.ONE_MONTH -> R.string.statistics_widget_timeline_one_month
StatisticsWidgetTimeline.SIX_MONTHS -> R.string.statistics_widget_timeline_six_months
StatisticsWidgetTimeline.THREE_MONTHS -> R.string.statistics_widget_timeline_three_months
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ca.josephroque.bowlingcompanion.core.designsystem.components.form.Pickabl
import ca.josephroque.bowlingcompanion.core.designsystem.components.list.ListSectionFooter
import ca.josephroque.bowlingcompanion.core.statistics.models.StatisticsWidgetTimeline
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.R
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.titleResourceId
import ca.josephroque.bowlingcompanion.feature.statisticswidget.ui.widget.StatisticsWidgetCard

@Composable
Expand All @@ -35,7 +36,8 @@ fun StatisticsWidgetEditor(
if (state.widget != null) {
StatisticsWidgetCard(
widget = state.widget,
chart = state.preview,
chart = state.preview?.chart,
chartEntryModelProducer = state.preview?.modelProducer,
modifier = Modifier
.aspectRatio(2f)
.padding(horizontal = 16.dp),
Expand Down Expand Up @@ -90,28 +92,10 @@ fun StatisticsWidgetEditor(
options = StatisticsWidgetTimeline.entries.toTypedArray(),
selected = state.timeline,
titleForOption = {
when (it) {
StatisticsWidgetTimeline.ONE_MONTH -> stringResource(
R.string.statistics_widget_timeline_one_month,
)

StatisticsWidgetTimeline.THREE_MONTHS -> stringResource(
R.string.statistics_widget_timeline_three_months,
)

StatisticsWidgetTimeline.SIX_MONTHS -> stringResource(
R.string.statistics_widget_timeline_six_months,
)

StatisticsWidgetTimeline.ONE_YEAR -> stringResource(
R.string.statistics_widget_timeline_one_year,
)

StatisticsWidgetTimeline.ALL_TIME -> stringResource(
R.string.statistics_widget_timeline_all_time,
)

null -> ""
if (it == null) {
""
} else {
stringResource(it.titleResourceId())
}
},
onOptionSelected = {
Expand Down
Loading

0 comments on commit 4816546

Please sign in to comment.