Additive rendering of statistics

This commit is contained in:
Vitor Pamplona
2023-05-17 09:34:21 -04:00
parent cb42196889
commit 0823f2e7b5
3 changed files with 172 additions and 89 deletions
@@ -8,6 +8,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
@@ -18,6 +19,7 @@ import com.vitorpamplona.amethyst.ui.dal.HomeConversationsFeedFilter
import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter import com.vitorpamplona.amethyst.ui.dal.HomeNewThreadFeedFilter
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
import com.vitorpamplona.amethyst.ui.dal.VideoFeedFilter import com.vitorpamplona.amethyst.ui.dal.VideoFeedFilter
import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrGlobalFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrGlobalFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrHomeFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrHomeRepliesFeedViewModel
@@ -37,6 +39,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.ProfileScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.SearchScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.ThreadScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.ThreadScreen
import com.vitorpamplona.amethyst.ui.screen.loggedIn.VideoScreen import com.vitorpamplona.amethyst.ui.screen.loggedIn.VideoScreen
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
@@ -50,7 +54,8 @@ fun AppNavigation(
// Avoids creating ViewModels for performance reasons (up to 1 second delays) // Avoids creating ViewModels for performance reasons (up to 1 second delays)
val accountState by accountViewModel.accountLiveData.observeAsState() val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return val account = remember(accountState) { accountState?.account } ?: return
val accountHex = remember(accountState) { accountState?.account?.userProfile()?.pubkeyHex }
HomeNewThreadFeedFilter.account = account HomeNewThreadFeedFilter.account = account
HomeConversationsFeedFilter.account = account HomeConversationsFeedFilter.account = account
@@ -67,6 +72,16 @@ fun AppNavigation(
NotificationFeedFilter.account = account NotificationFeedFilter.account = account
val notifFeedViewModel: NotificationViewModel = viewModel() val notifFeedViewModel: NotificationViewModel = viewModel()
val userReactionsStatsModel: UserReactionsViewModel = viewModel()
val scope = rememberCoroutineScope()
LaunchedEffect(accountHex) {
scope.launch(Dispatchers.IO) {
userReactionsStatsModel.load(account.userProfile())
userReactionsStatsModel.initializeSuspend()
}
}
NavHost(navController, startDestination = Route.Home.route) { NavHost(navController, startDestination = Route.Home.route) {
Route.Video.let { route -> Route.Video.let { route ->
composable(route.route, route.arguments, content = { composable(route.route, route.arguments, content = {
@@ -135,6 +150,7 @@ fun AppNavigation(
NotificationScreen( NotificationScreen(
notifFeedViewModel = notifFeedViewModel, notifFeedViewModel = notifFeedViewModel,
userReactionsStatsModel = userReactionsStatsModel,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
navController = navController, navController = navController,
scrollToTop = scrollToTop scrollToTop = scrollToTop
@@ -25,8 +25,15 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController import androidx.navigation.NavController
import com.patrykandpatrick.vico.core.chart.composed.ComposedChartEntryModel
import com.patrykandpatrick.vico.core.entry.ChartEntryModel
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import com.patrykandpatrick.vico.core.entry.composed.plus
import com.patrykandpatrick.vico.core.entry.entryOf
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.HexKey
import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.service.model.LnZapEvent import com.vitorpamplona.amethyst.service.model.LnZapEvent
import com.vitorpamplona.amethyst.service.model.ReactionEvent import com.vitorpamplona.amethyst.service.model.ReactionEvent
@@ -34,7 +41,6 @@ import com.vitorpamplona.amethyst.service.model.RepostEvent
import com.vitorpamplona.amethyst.service.model.TextNoteEvent import com.vitorpamplona.amethyst.service.model.TextNoteEvent
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -63,7 +69,7 @@ fun UserReactionsRow(model: UserReactionsViewModel, accountViewModel: AccountVie
} }
Row(verticalAlignment = CenterVertically, modifier = Modifier.weight(1f)) { Row(verticalAlignment = CenterVertically, modifier = Modifier.weight(1f)) {
UserLikeReaction(model.replies[model.today]) UserLikeReaction(model.reactions[model.today])
} }
Row(verticalAlignment = CenterVertically, modifier = Modifier.weight(1f)) { Row(verticalAlignment = CenterVertically, modifier = Modifier.weight(1f)) {
@@ -80,22 +86,21 @@ class UserReactionsViewModel : ViewModel() {
var zaps by mutableStateOf<Map<String, BigDecimal>>(emptyMap()) var zaps by mutableStateOf<Map<String, BigDecimal>>(emptyMap())
var replies by mutableStateOf<Map<String, Int>>(emptyMap()) var replies by mutableStateOf<Map<String, Int>>(emptyMap())
var takenIntoAccount = setOf<HexKey>()
val sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd") // SimpleDateFormat() val sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd") // SimpleDateFormat()
val today = sdf.format(LocalDateTime.now()) val today = sdf.format(LocalDateTime.now())
var chartModel by mutableStateOf<ComposedChartEntryModel<ChartEntryModel>?>(null)
var axisLabels by mutableStateOf<List<String>>(emptyList())
fun load(baseUser: User) { fun load(baseUser: User) {
user = baseUser user = baseUser
reactions = emptyMap() reactions = emptyMap()
boosts = emptyMap() boosts = emptyMap()
zaps = emptyMap() zaps = emptyMap()
replies = emptyMap() replies = emptyMap()
} takenIntoAccount = emptySet()
fun refresh() {
val scope = CoroutineScope(Job() + Dispatchers.Default)
scope.launch {
refreshSuspended()
}
} }
fun formatDate(createAt: Long): String { fun formatDate(createAt: Long): String {
@@ -106,46 +111,133 @@ class UserReactionsViewModel : ViewModel() {
) )
} }
fun refreshSuspended() { fun initializeSuspend() {
val currentUser = user?.pubkeyHex ?: return val currentUser = user?.pubkeyHex ?: return
val reactions = mutableMapOf<String, Int>() val reactions = mutableMapOf<String, Int>()
val boosts = mutableMapOf<String, Int>() val boosts = mutableMapOf<String, Int>()
val zaps = mutableMapOf<String, BigDecimal>() val zaps = mutableMapOf<String, BigDecimal>()
val replies = mutableMapOf<String, Int>() val replies = mutableMapOf<String, Int>()
val takenIntoAccount = mutableSetOf<HexKey>()
LocalCache.notes.values.forEach { LocalCache.notes.values.forEach {
val noteEvent = it.event val noteEvent = it.event
if (noteEvent is ReactionEvent) { if (noteEvent != null && !takenIntoAccount.contains(noteEvent.id())) {
if (noteEvent.isTaggedUser(currentUser)) { if (noteEvent is ReactionEvent) {
val netDate = formatDate(noteEvent.createdAt) if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
reactions[netDate] = (reactions[netDate] ?: 0) + 1 val netDate = formatDate(noteEvent.createdAt)
} reactions[netDate] = (reactions[netDate] ?: 0) + 1
} takenIntoAccount.add(noteEvent.id())
if (noteEvent is RepostEvent) { }
if (noteEvent.isTaggedUser(currentUser)) { } else if (noteEvent is RepostEvent) {
val netDate = formatDate(noteEvent.createdAt) if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
boosts[netDate] = (boosts[netDate] ?: 0) + 1 val netDate = formatDate(noteEvent.createdAt)
} boosts[netDate] = (boosts[netDate] ?: 0) + 1
} takenIntoAccount.add(noteEvent.id())
if (noteEvent is LnZapEvent) { }
if (noteEvent.isTaggedUser(currentUser)) { } else if (noteEvent is LnZapEvent) {
val netDate = formatDate(noteEvent.createdAt) if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
zaps[netDate] = (zaps[netDate] ?: BigDecimal.ZERO) + (noteEvent.amount ?: BigDecimal.ZERO) val netDate = formatDate(noteEvent.createdAt)
} zaps[netDate] = (zaps[netDate] ?: BigDecimal.ZERO) + (noteEvent.amount ?: BigDecimal.ZERO)
} takenIntoAccount.add(noteEvent.id())
if (noteEvent is TextNoteEvent) { }
if (noteEvent.isTaggedUser(currentUser)) { } else if (noteEvent is TextNoteEvent) {
val netDate = formatDate(noteEvent.createdAt) if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
replies[netDate] = (replies[netDate] ?: 0) + 1 val netDate = formatDate(noteEvent.createdAt)
replies[netDate] = (replies[netDate] ?: 0) + 1
takenIntoAccount.add(noteEvent.id())
}
} }
} }
} }
this.takenIntoAccount = takenIntoAccount
this.reactions = reactions this.reactions = reactions
this.replies = replies this.replies = replies
this.zaps = zaps this.zaps = zaps
this.boosts = boosts this.boosts = boosts
refreshChartModel()
}
fun addToStatsSuspend(newNotes: Set<Note>) {
val currentUser = user?.pubkeyHex ?: return
val reactions = this.reactions.toMutableMap()
val boosts = this.boosts.toMutableMap()
val zaps = this.zaps.toMutableMap()
val replies = this.replies.toMutableMap()
val takenIntoAccount = this.takenIntoAccount.toMutableSet()
var hasNewElements = false
newNotes.forEach {
val noteEvent = it.event
if (noteEvent != null && !takenIntoAccount.contains(noteEvent.id())) {
if (noteEvent is ReactionEvent) {
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
val netDate = formatDate(noteEvent.createdAt)
reactions[netDate] = (reactions[netDate] ?: 0) + 1
takenIntoAccount.add(noteEvent.id())
hasNewElements = true
}
} else if (noteEvent is RepostEvent) {
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
val netDate = formatDate(noteEvent.createdAt)
boosts[netDate] = (boosts[netDate] ?: 0) + 1
takenIntoAccount.add(noteEvent.id())
hasNewElements = true
}
} else if (noteEvent is LnZapEvent) {
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
val netDate = formatDate(noteEvent.createdAt)
zaps[netDate] = (zaps[netDate] ?: BigDecimal.ZERO) + (noteEvent.amount ?: BigDecimal.ZERO)
takenIntoAccount.add(noteEvent.id())
hasNewElements = true
}
} else if (noteEvent is TextNoteEvent) {
if (noteEvent.isTaggedUser(currentUser) && noteEvent.pubKey != currentUser) {
val netDate = formatDate(noteEvent.createdAt)
replies[netDate] = (replies[netDate] ?: 0) + 1
takenIntoAccount.add(noteEvent.id())
hasNewElements = true
}
}
}
}
if (hasNewElements) {
this.takenIntoAccount = takenIntoAccount
this.reactions = reactions
this.replies = replies
this.zaps = zaps
this.boosts = boosts
refreshChartModel()
}
}
private fun refreshChartModel() {
val day = 24 * 60 * 60L
val now = LocalDateTime.now()
val displayAxisFormatter = DateTimeFormatter.ofPattern("EEE")
val dataAxisLabels = listOf(6, 5, 4, 3, 2, 1, 0).map { sdf.format(now.minusSeconds(day * it)) }
val listOfCountCurves = listOf(
dataAxisLabels.mapIndexed { index, dateStr -> entryOf(index, replies[dateStr]?.toFloat() ?: 0f) },
dataAxisLabels.mapIndexed { index, dateStr -> entryOf(index, boosts[dateStr]?.toFloat() ?: 0f) },
dataAxisLabels.mapIndexed { index, dateStr -> entryOf(index, reactions[dateStr]?.toFloat() ?: 0f) }
)
val listOfValueCurves = listOf(
dataAxisLabels.mapIndexed { index, dateStr -> entryOf(index, zaps[dateStr]?.toFloat() ?: 0f) }
)
val chartEntryModelProducer1 = ChartEntryModelProducer(listOfCountCurves).getModel()
val chartEntryModelProducer2 = ChartEntryModelProducer(listOfValueCurves).getModel()
this.axisLabels = listOf(6, 5, 4, 3, 2, 1, 0).map { displayAxisFormatter.format(now.minusSeconds(day * it)) }
this.chartModel = chartEntryModelProducer1.plus(chartEntryModelProducer2)
} }
var collectorJob: Job? = null var collectorJob: Job? = null
@@ -153,7 +245,7 @@ class UserReactionsViewModel : ViewModel() {
init { init {
collectorJob = viewModelScope.launch(Dispatchers.IO) { collectorJob = viewModelScope.launch(Dispatchers.IO) {
LocalCache.live.newEventBundles.collect { newNotes -> LocalCache.live.newEventBundles.collect { newNotes ->
refresh() addToStatsSuspend(newNotes)
} }
} }
} }
@@ -191,7 +283,7 @@ fun UserBoostReaction(
Icon( Icon(
painter = painterResource(R.drawable.ic_retweeted), painter = painterResource(R.drawable.ic_retweeted),
null, null,
modifier = Modifier.size(20.dp), modifier = Modifier.size(24.dp),
tint = Color.Unspecified tint = Color.Unspecified
) )
@@ -231,7 +323,7 @@ fun UserZapReaction(
Icon( Icon(
imageVector = Icons.Default.Bolt, imageVector = Icons.Default.Bolt,
contentDescription = stringResource(R.string.zaps), contentDescription = stringResource(R.string.zaps),
modifier = Modifier.size(20.dp), modifier = Modifier.size(24.dp),
tint = BitcoinOrange tint = BitcoinOrange
) )
@@ -13,7 +13,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
@@ -35,34 +34,32 @@ import com.patrykandpatrick.vico.compose.style.ProvideChartStyle
import com.patrykandpatrick.vico.core.DefaultAlpha import com.patrykandpatrick.vico.core.DefaultAlpha
import com.patrykandpatrick.vico.core.axis.AxisPosition import com.patrykandpatrick.vico.core.axis.AxisPosition
import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter
import com.patrykandpatrick.vico.core.chart.composed.ComposedChartEntryModel
import com.patrykandpatrick.vico.core.chart.composed.plus import com.patrykandpatrick.vico.core.chart.composed.plus
import com.patrykandpatrick.vico.core.chart.line.LineChart import com.patrykandpatrick.vico.core.chart.line.LineChart
import com.patrykandpatrick.vico.core.chart.values.ChartValues import com.patrykandpatrick.vico.core.chart.values.ChartValues
import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders
import com.patrykandpatrick.vico.core.entry.ChartEntryModel
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
import com.patrykandpatrick.vico.core.entry.composed.plus import com.patrykandpatrick.vico.core.entry.composed.plus
import com.patrykandpatrick.vico.core.entry.entryOf
import com.vitorpamplona.amethyst.service.NostrAccountDataSource import com.vitorpamplona.amethyst.service.NostrAccountDataSource
import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter import com.vitorpamplona.amethyst.ui.dal.NotificationFeedFilter
import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.navigation.Route
import com.vitorpamplona.amethyst.ui.note.OneGiga
import com.vitorpamplona.amethyst.ui.note.OneKilo
import com.vitorpamplona.amethyst.ui.note.OneMega
import com.vitorpamplona.amethyst.ui.note.UserReactionsRow import com.vitorpamplona.amethyst.ui.note.UserReactionsRow
import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel import com.vitorpamplona.amethyst.ui.note.UserReactionsViewModel
import com.vitorpamplona.amethyst.ui.note.showAmount
import com.vitorpamplona.amethyst.ui.note.showCount import com.vitorpamplona.amethyst.ui.note.showCount
import com.vitorpamplona.amethyst.ui.screen.CardFeedView import com.vitorpamplona.amethyst.ui.screen.CardFeedView
import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel import com.vitorpamplona.amethyst.ui.screen.NotificationViewModel
import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys import com.vitorpamplona.amethyst.ui.screen.ScrollStateKeys
import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import kotlinx.coroutines.launch import java.math.BigDecimal
import java.time.LocalDateTime import java.math.RoundingMode
import java.time.format.DateTimeFormatter
import kotlin.math.roundToInt import kotlin.math.roundToInt
@Composable @Composable
fun NotificationScreen( fun NotificationScreen(
notifFeedViewModel: NotificationViewModel, notifFeedViewModel: NotificationViewModel,
userReactionsStatsModel: UserReactionsViewModel,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
navController: NavController, navController: NavController,
scrollToTop: Boolean = false scrollToTop: Boolean = false
@@ -100,7 +97,7 @@ fun NotificationScreen(
Column( Column(
modifier = Modifier.padding(vertical = 0.dp) modifier = Modifier.padding(vertical = 0.dp)
) { ) {
SummaryBar(accountViewModel, navController) SummaryBar(userReactionsStatsModel, accountViewModel, navController)
CardFeedView( CardFeedView(
viewModel = notifFeedViewModel, viewModel = notifFeedViewModel,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
@@ -114,49 +111,11 @@ fun NotificationScreen(
} }
@Composable @Composable
fun SummaryBar(accountViewModel: AccountViewModel, navController: NavController) { fun SummaryBar(model: UserReactionsViewModel, accountViewModel: AccountViewModel, navController: NavController) {
val accountState by accountViewModel.accountLiveData.observeAsState()
val accountUser = remember(accountState) { accountState?.account?.userProfile() } ?: return
val model: UserReactionsViewModel = viewModel()
var chartModel by remember(accountState) { mutableStateOf<ComposedChartEntryModel<ChartEntryModel>?>(null) }
var axisLabels by remember(accountState) { mutableStateOf<List<String>>(emptyList()) }
val scope = rememberCoroutineScope()
var showChart by remember { var showChart by remember {
mutableStateOf(false) mutableStateOf(false)
} }
LaunchedEffect(accountUser.pubkeyHex) {
scope.launch {
model.load(accountUser)
model.refreshSuspended()
val day = 24 * 60 * 60L
val now = LocalDateTime.now()
val displayAxisFormatter = DateTimeFormatter.ofPattern("EEE")
val dataAxisLabels = listOf(6, 5, 4, 3, 2, 1, 0).map { model.sdf.format(now.minusSeconds(day * it)) }
axisLabels = listOf(6, 5, 4, 3, 2, 1, 0).map { displayAxisFormatter.format(now.minusSeconds(day * it)) }
val listOfCountCurves = listOf(
dataAxisLabels.mapIndexed { index, dateStr -> entryOf(index, model.replies[dateStr]?.toFloat() ?: 0f) },
dataAxisLabels.mapIndexed { index, dateStr -> entryOf(index, model.boosts[dateStr]?.toFloat() ?: 0f) },
dataAxisLabels.mapIndexed { index, dateStr -> entryOf(index, model.reactions[dateStr]?.toFloat() ?: 0f) }
)
val listOfValueCurves = listOf(
dataAxisLabels.mapIndexed { index, dateStr -> entryOf(index, model.zaps[dateStr]?.toFloat() ?: 0f) }
)
val chartEntryModelProducer1 = ChartEntryModelProducer(listOfCountCurves).getModel()
val chartEntryModelProducer2 = ChartEntryModelProducer(listOfValueCurves).getModel()
chartModel = chartEntryModelProducer1.plus(chartEntryModelProducer2)
}
}
UserReactionsRow(model, accountViewModel, navController) { UserReactionsRow(model, accountViewModel, navController) {
showChart = !showChart showChart = !showChart
} }
@@ -197,9 +156,13 @@ fun SummaryBar(accountViewModel: AccountViewModel, navController: NavController)
targetVerticalAxisPosition = AxisPosition.Vertical.End targetVerticalAxisPosition = AxisPosition.Vertical.End
) )
chartModel?.let { model.chartModel?.let {
if (showChart) { if (showChart) {
Row(modifier = Modifier.padding(vertical = 10.dp, horizontal = 20.dp).clickable(onClick = { showChart = !showChart })) { Row(
modifier = Modifier
.padding(vertical = 10.dp, horizontal = 20.dp)
.clickable(onClick = { showChart = !showChart })
) {
ProvideChartStyle() { ProvideChartStyle() {
Chart( Chart(
chart = remember(lineChartCount, lineChartZaps) { chart = remember(lineChartCount, lineChartZaps) {
@@ -213,7 +176,7 @@ fun SummaryBar(accountViewModel: AccountViewModel, navController: NavController)
valueFormatter = AmountAxisValueFormatter() valueFormatter = AmountAxisValueFormatter()
), ),
bottomAxis = bottomAxis( bottomAxis = bottomAxis(
valueFormatter = LabelValueFormatter(axisLabels) valueFormatter = LabelValueFormatter(model.axisLabels)
) )
) )
} }
@@ -249,6 +212,18 @@ class AmountAxisValueFormatter() : AxisValueFormatter<AxisPosition.Vertical.End>
value: Float, value: Float,
chartValues: ChartValues chartValues: ChartValues
): String { ): String {
return showAmount(value.toBigDecimal()) return showAmountAxis(value.toBigDecimal())
}
}
fun showAmountAxis(amount: BigDecimal?): String {
if (amount == null) return ""
if (amount.abs() < BigDecimal(0.01)) return ""
return when {
amount >= OneGiga -> "%.0fG".format(amount.div(OneGiga).setScale(0, RoundingMode.HALF_UP))
amount >= OneMega -> "%.0fM".format(amount.div(OneMega).setScale(0, RoundingMode.HALF_UP))
amount >= OneKilo -> "%.0fk".format(amount.div(OneKilo).setScale(0, RoundingMode.HALF_UP))
else -> "%.0f".format(amount)
} }
} }