From 0f6c3b9a3acbfef6528becdf87783a68de130413 Mon Sep 17 00:00:00 2001 From: Vitor Pamplona Date: Fri, 14 Feb 2025 11:05:00 -0500 Subject: [PATCH] Moves to vico chart 2.0 --- ...kt => ThreadDualAxisChartAssemblerTest.kt} | 2 +- .../amethyst/ui/note/ChannelCardCompose.kt | 3 +- .../amethyst/ui/note/ZapFormatter.kt | 7 + .../ui/note/ZapFormatterNoDecimals.kt | 64 ++++++++ .../amethyst/ui/note/ZapNoteCompose.kt | 3 +- .../ui/screen/loggedIn/AccountViewModel.kt | 22 +-- .../notifications/NotificationSummaryState.kt | 130 ++++++--------- .../notifications/NotificationSummaryView.kt | 155 +----------------- .../chart/AmountValueFormatter.kt | 43 +++++ .../chart/CountAxisValueFormatter.kt | 47 ++++++ .../chart/LastWeekLabelFormatter.kt | 51 ++++++ .../loggedIn/notifications/chart/ShowChart.kt | 101 ++++++++++++ .../screen/loggedIn/profile/ProfileScreen.kt | 4 +- .../vitorpamplona/amethyst/ui/theme/Theme.kt | 48 ++++-- gradle/libs.versions.toml | 2 +- 15 files changed, 419 insertions(+), 263 deletions(-) rename amethyst/src/androidTest/java/com/vitorpamplona/amethyst/{ThreadAssemblerTest.kt => ThreadDualAxisChartAssemblerTest.kt} (99%) create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatterNoDecimals.kt create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/AmountValueFormatter.kt create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/CountAxisValueFormatter.kt create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/LastWeekLabelFormatter.kt create mode 100644 amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/ShowChart.kt diff --git a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ThreadAssemblerTest.kt b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ThreadDualAxisChartAssemblerTest.kt similarity index 99% rename from amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ThreadAssemblerTest.kt rename to amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ThreadDualAxisChartAssemblerTest.kt index c45397357..215e089cd 100644 --- a/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ThreadAssemblerTest.kt +++ b/amethyst/src/androidTest/java/com/vitorpamplona/amethyst/ThreadDualAxisChartAssemblerTest.kt @@ -42,7 +42,7 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -class ThreadAssemblerTest { +class ThreadDualAxisChartAssemblerTest { val db = """ [ diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ChannelCardCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ChannelCardCompose.kt index 2ffe22d51..ae66ef69f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ChannelCardCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ChannelCardCompose.kt @@ -86,7 +86,6 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.chatrooms.ScheduledFlag import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.observeAppDefinition import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CheckIfVideoIsOnline import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.equalImmutableLists -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.showAmountAxis import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.theme.HalfPadding @@ -386,7 +385,7 @@ fun InnerRenderClassifiedsThumb( card.price?.let { val priceTag = remember(card) { - val newAmount = it.amount.toBigDecimalOrNull()?.let { showAmountAxis(it) } ?: it.amount + val newAmount = it.amount.toBigDecimalOrNull()?.let { showAmountInteger(it) } ?: it.amount if (it.frequency != null && it.currency != null) { "$newAmount ${it.currency}/${it.frequency}" diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatter.kt index 0ad29ac7f..e5024ad67 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatter.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatter.kt @@ -74,3 +74,10 @@ fun showAmount(amount: BigDecimal?): String { else -> dfN.get().format(amount) } } + +fun showAmountWithZero(amount: BigDecimal?): String { + if (amount == null) return "0" + if (amount.abs() < BigDecimal(0.01)) return "0" + + return showAmount(amount) +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatterNoDecimals.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatterNoDecimals.kt new file mode 100644 index 000000000..d36f53242 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapFormatterNoDecimals.kt @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.note + +import java.math.BigDecimal +import java.math.RoundingMode +import java.text.DecimalFormat + +private val dfG = + object : ThreadLocal() { + override fun initialValue() = DecimalFormat("#G") + } + +private val dfM = + object : ThreadLocal() { + override fun initialValue() = DecimalFormat("#M") + } + +private val dfK = + object : ThreadLocal() { + override fun initialValue() = DecimalFormat("#k") + } + +private val dfN = + object : ThreadLocal() { + override fun initialValue() = DecimalFormat("#") + } + +fun showAmountInteger(amount: BigDecimal?): String { + if (amount == null) return "" + if (amount.abs() < BigDecimal(0.01)) return "" + + return when { + amount >= OneGiga -> dfG.get().format(amount.div(OneGiga).setScale(0, RoundingMode.HALF_UP)) + amount >= OneMega -> dfM.get().format(amount.div(OneMega).setScale(0, RoundingMode.HALF_UP)) + amount >= TenKilo -> dfK.get().format(amount.div(OneKilo).setScale(0, RoundingMode.HALF_UP)) + else -> dfN.get().format(amount) + } +} + +fun showAmountIntegerWithZero(amount: BigDecimal?): String { + if (amount == null) return "0" + if (amount.abs() < BigDecimal(0.01)) return "0" + + return showAmountInteger(amount) +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt index b58a23692..385502963 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/ZapNoteCompose.kt @@ -47,7 +47,6 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.navigation.INav import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.showAmountAxis import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.FollowButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ShowUserButton import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.UnfollowButton @@ -144,7 +143,7 @@ private fun ZapAmount(zapEventNote: Note) { LaunchedEffect(key1 = noteState) { launch(Dispatchers.IO) { - val newZapAmount = showAmountAxis((noteState?.note?.event as? LnZapEvent)?.amount) + val newZapAmount = showAmountInteger((noteState?.note?.event as? LnZapEvent)?.amount) if (zapAmount != newZapAmount) { zapAmount = newZapAmount } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index 7d246191c..b9cbc1ef1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -64,11 +64,11 @@ import com.vitorpamplona.amethyst.ui.navigation.Route import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification import com.vitorpamplona.amethyst.ui.note.ZapraiserStatus import com.vitorpamplona.amethyst.ui.note.showAmount +import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.SettingsState import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CardFeedState import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CombinedZap -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.showAmountAxis import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.tor.TorSettings import com.vitorpamplona.ammolite.relays.BundledInsert @@ -536,7 +536,7 @@ class AccountViewModel( it.request.event ?.content ?.ifBlank { null }, - showAmountAxis((it.response.event as? LnZapEvent)?.amount), + showAmountInteger((it.response.event as? LnZapEvent)?.amount), ) }.toMutableMap() @@ -569,7 +569,7 @@ class AccountViewModel( ZapAmountCommentNotification( LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.request.author, cachedPrivateRequest.content.ifBlank { null }, - showAmountAxis((it.response.event as? LnZapEvent)?.amount), + showAmountInteger((it.response.event as? LnZapEvent)?.amount), ) } else { ZapAmountCommentNotification( @@ -577,7 +577,7 @@ class AccountViewModel( it.request.event ?.content ?.ifBlank { null }, - showAmountAxis((it.response.event as? LnZapEvent)?.amount), + showAmountInteger((it.response.event as? LnZapEvent)?.amount), ) } } else { @@ -586,7 +586,7 @@ class AccountViewModel( it.request.event ?.content ?.ifBlank { null }, - showAmountAxis((it.response.event as? LnZapEvent)?.amount), + showAmountInteger((it.response.event as? LnZapEvent)?.amount), ) } }.toImmutableList() @@ -603,7 +603,7 @@ class AccountViewModel( ZapAmountCommentNotification( LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.first.author, cachedPrivateRequest.content.ifBlank { null }, - showAmountAxis((it.second?.event as? LnZapEvent)?.amount), + showAmountInteger((it.second?.event as? LnZapEvent)?.amount), ) } else { ZapAmountCommentNotification( @@ -611,7 +611,7 @@ class AccountViewModel( it.first.event ?.content ?.ifBlank { null }, - showAmountAxis((it.second?.event as? LnZapEvent)?.amount), + showAmountInteger((it.second?.event as? LnZapEvent)?.amount), ) } } else { @@ -620,7 +620,7 @@ class AccountViewModel( it.first.event ?.content ?.ifBlank { null }, - showAmountAxis((it.second?.event as? LnZapEvent)?.amount), + showAmountInteger((it.second?.event as? LnZapEvent)?.amount), ) } }.toImmutableList() @@ -642,7 +642,7 @@ class AccountViewModel( it.first.event ?.content ?.ifBlank { null }, - showAmountAxis((it.second?.event as? LnZapEvent)?.amount), + showAmountInteger((it.second?.event as? LnZapEvent)?.amount), ) }.toMutableMap() @@ -687,7 +687,7 @@ class AccountViewModel( ZapAmountCommentNotification( newAuthor, decryptedContent.content.ifBlank { null }, - showAmountAxis(amount), + showAmountInteger(amount), ), ) } @@ -698,7 +698,7 @@ class AccountViewModel( ZapAmountCommentNotification( zapRequest.author, zapRequest.event?.content?.ifBlank { null }, - showAmountAxis(amount), + showAmountInteger(amount), ), ) } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryState.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryState.kt index b6ea64b98..aef6b48e7 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryState.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryState.kt @@ -22,16 +22,16 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications import android.util.Log import androidx.compose.runtime.Stable -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.patrykandpatrick.vico.core.cartesian.data.CartesianChartModel +import com.patrykandpatrick.vico.core.cartesian.data.LineCartesianLayerModel +import com.patrykandpatrick.vico.core.common.data.ExtraStore +import com.patrykandpatrick.vico.core.common.data.MutableExtraStore import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.service.checkNotInMainThread +import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.note.showCount import com.vitorpamplona.ammolite.relays.BundledInsert import com.vitorpamplona.quartz.nip01Core.HexKey @@ -52,37 +52,30 @@ import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter +val ShowDecimals = ExtraStore.Key() +val BottomAxisLabelKey = ExtraStore.Key>() + @Stable class NotificationSummaryState( val account: Account, ) { val user: User = account.userProfile() - private var _reactions = MutableStateFlow>(emptyMap()) - private var _boosts = MutableStateFlow>(emptyMap()) - private var _zaps = MutableStateFlow>(emptyMap()) - private var _replies = MutableStateFlow>(emptyMap()) - - private var _chartModel = MutableStateFlow?>(null) - private var _axisLabels = MutableStateFlow>(emptyList()) - - val reactions = _reactions.asStateFlow() - val boosts = _boosts.asStateFlow() - val zaps = _zaps.asStateFlow() - val replies = _replies.asStateFlow() + private var reactions = MutableStateFlow>(emptyMap()) + private var boosts = MutableStateFlow>(emptyMap()) + private var zaps = MutableStateFlow>(emptyMap()) + private var replies = MutableStateFlow>(emptyMap()) + private var _chartModel = MutableStateFlow(null) val chartModel = _chartModel.asStateFlow() - val axisLabels = _axisLabels.asStateFlow() private var takenIntoAccount = setOf() private val sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd") // SimpleDateFormat() - val todaysReplyCount = _replies.map { showCount(it[today()]) }.distinctUntilChanged() - val todaysBoostCount = _boosts.map { showCount(it[today()]) }.distinctUntilChanged() - val todaysReactionCount = _reactions.map { showCount(it[today()]) }.distinctUntilChanged() - val todaysZapAmount = _zaps.map { showAmountAxis(it[today()]) }.distinctUntilChanged() - - var shouldShowDecimalsInAxis = false + val todaysReplyCount = replies.map { showCount(it[today()]) }.distinctUntilChanged() + val todaysBoostCount = boosts.map { showCount(it[today()]) }.distinctUntilChanged() + val todaysReactionCount = reactions.map { showCount(it[today()]) }.distinctUntilChanged() + val todaysZapAmount = zaps.map { showAmountInteger(it[today()]) }.distinctUntilChanged() fun formatDate(createAt: Long): String = sdf.format( @@ -146,10 +139,10 @@ class NotificationSummaryState( } this.takenIntoAccount = takenIntoAccount - this._reactions.emit(reactions) - this._replies.emit(replies) - this._zaps.emit(zaps) - this._boosts.emit(boosts) + this.reactions.emit(reactions) + this.replies.emit(replies) + this.zaps.emit(zaps) + this.boosts.emit(boosts) refreshChartModel() } @@ -159,10 +152,10 @@ class NotificationSummaryState( val currentUser = user.pubkeyHex - val reactions = this._reactions.value.toMutableMap() - val boosts = this._boosts.value.toMutableMap() - val zaps = this._zaps.value.toMutableMap() - val replies = this._replies.value.toMutableMap() + val reactions = this.reactions.value.toMutableMap() + val boosts = this.boosts.value.toMutableMap() + val zaps = this.zaps.value.toMutableMap() + val replies = this.replies.value.toMutableMap() val takenIntoAccount = this.takenIntoAccount.toMutableSet() var hasNewElements = false @@ -217,10 +210,10 @@ class NotificationSummaryState( if (hasNewElements) { this.takenIntoAccount = takenIntoAccount - this._reactions.emit(reactions) - this._replies.emit(replies) - this._zaps.emit(zaps) - this._boosts.emit(boosts) + this.reactions.emit(reactions) + this.replies.emit(replies) + this.zaps.emit(zaps) + this.boosts.emit(boosts) refreshChartModel() } @@ -229,59 +222,42 @@ class NotificationSummaryState( private suspend fun refreshChartModel() { checkNotInMainThread() - 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 dataAxisLabelIndexes = listOf(-6, -5, -4, -3, -2, -1, 0) + val dataAxisLabels = dataAxisLabelIndexes.map { sdf.format(now.plusDays(it.toLong())) } - val listOfCountCurves = - listOf( - dataAxisLabels.mapIndexed { index, dateStr -> - entryOf(index, _replies.value[dateStr]?.toFloat() ?: 0f) - }, - dataAxisLabels.mapIndexed { index, dateStr -> - entryOf(index, _boosts.value[dateStr]?.toFloat() ?: 0f) - }, - dataAxisLabels.mapIndexed { index, dateStr -> - entryOf(index, _reactions.value[dateStr]?.toFloat() ?: 0f) - }, - ) - - val listOfValueCurves = - listOf( - dataAxisLabels.mapIndexed { index, dateStr -> - entryOf(index, _zaps.value[dateStr]?.toFloat() ?: 0f) - }, - ) - - val chartEntryModelProducer1 = ChartEntryModelProducer(listOfCountCurves).getModel() - val chartEntryModelProducer2 = ChartEntryModelProducer(listOfValueCurves).getModel() - - chartEntryModelProducer1?.let { chart1 -> - chartEntryModelProducer2?.let { chart2 -> - this.shouldShowDecimalsInAxis = shouldShowDecimals(chart2.minY, chart2.maxY) - - this._axisLabels.emit( - listOf(6, 5, 4, 3, 2, 1, 0).map { - displayAxisFormatter.format(now.minusSeconds(day * it)) - }, - ) - this._chartModel.emit(chart1.plus(chart2)) + val chart1 = + LineCartesianLayerModel.build { + series(dataAxisLabelIndexes, dataAxisLabels.map { replies.value[it]?.toFloat() ?: 0f }) + series(dataAxisLabelIndexes, dataAxisLabels.map { boosts.value[it]?.toFloat() ?: 0f }) + series(dataAxisLabelIndexes, dataAxisLabels.map { reactions.value[it]?.toFloat() ?: 0f }) } - } + + val chart2 = + LineCartesianLayerModel.build { + series(dataAxisLabelIndexes, dataAxisLabels.map { zaps.value[it]?.toFloat() ?: 0f }) + } + + val model = CartesianChartModel(chart1, chart2) + + val mutableStore = MutableExtraStore() + + mutableStore[ShowDecimals] = shouldShowDecimals(chart2.minY, chart2.maxY) + + this._chartModel.emit(model.copy(mutableStore)) } // determine if the min max are so close that they render to the same number. fun shouldShowDecimals( - min: Float, - max: Float, + min: Double, + max: Double, ): Boolean { val step = (max - min) / 8 - var previous = showAmountAxis(min.toBigDecimal()) + var previous = showAmountInteger(min.toBigDecimal()) for (i in 1..7) { - val current = showAmountAxis((min + (i * step)).toBigDecimal()) + val current = showAmountInteger((min + (i * step)).toBigDecimal()) if (previous == current) { return true } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryView.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryView.kt index 7e16002b2..c8b4717d0 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryView.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/NotificationSummaryView.kt @@ -30,47 +30,17 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Brush -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.patrykandpatrick.vico.compose.axis.axisLabelComponent -import com.patrykandpatrick.vico.compose.axis.horizontal.rememberBottomAxis -import com.patrykandpatrick.vico.compose.axis.vertical.rememberEndAxis -import com.patrykandpatrick.vico.compose.axis.vertical.rememberStartAxis -import com.patrykandpatrick.vico.compose.chart.Chart -import com.patrykandpatrick.vico.compose.chart.line.lineChart -import com.patrykandpatrick.vico.compose.component.shape.shader.fromBrush -import com.patrykandpatrick.vico.compose.style.ProvideChartStyle -import com.patrykandpatrick.vico.core.DefaultAlpha -import com.patrykandpatrick.vico.core.axis.AxisPosition -import com.patrykandpatrick.vico.core.axis.formatter.AxisValueFormatter -import com.patrykandpatrick.vico.core.chart.composed.plus -import com.patrykandpatrick.vico.core.chart.line.LineChart -import com.patrykandpatrick.vico.core.chart.values.ChartValues -import com.patrykandpatrick.vico.core.component.shape.shader.DynamicShaders -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.TenKilo +import com.patrykandpatrick.vico.compose.common.ProvideVicoTheme import com.vitorpamplona.amethyst.ui.note.UserReactionsRow -import com.vitorpamplona.amethyst.ui.note.showAmount -import com.vitorpamplona.amethyst.ui.note.showCount -import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange -import com.vitorpamplona.amethyst.ui.theme.RoyalBlue +import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.chart.ShowChart import com.vitorpamplona.amethyst.ui.theme.chartStyle -import java.math.BigDecimal -import java.math.RoundingMode -import java.text.DecimalFormat -import kotlin.math.roundToInt @Composable fun SummaryBar(state: NotificationSummaryState) { @@ -83,137 +53,24 @@ fun SummaryBar(state: NotificationSummaryState) { enter = slideInVertically() + expandVertically(), exit = slideOutVertically() + shrinkVertically(), ) { - val lineChartCount = - lineChart( - lines = - listOf(RoyalBlue, Color.Green, Color.Red).map { lineChartColor -> - LineChart.LineSpec( - lineColor = lineChartColor.toArgb(), - lineBackgroundShader = - DynamicShaders.fromBrush( - Brush.verticalGradient( - listOf( - lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_START), - lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_END), - ), - ), - ), - ) - }, - targetVerticalAxisPosition = AxisPosition.Vertical.Start, - ) - - val lineChartZaps = - lineChart( - lines = - listOf(BitcoinOrange).map { lineChartColor -> - LineChart.LineSpec( - lineColor = lineChartColor.toArgb(), - lineBackgroundShader = - DynamicShaders.fromBrush( - Brush.verticalGradient( - listOf( - lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_START), - lineChartColor.copy(DefaultAlpha.LINE_BACKGROUND_SHADER_END), - ), - ), - ), - ) - }, - targetVerticalAxisPosition = AxisPosition.Vertical.End, - ) - Row( modifier = Modifier .padding(vertical = 0.dp, horizontal = 20.dp) .clickable(onClick = { showChart = !showChart }), ) { - ProvideChartStyle( - chartStyle = MaterialTheme.colorScheme.chartStyle, - ) { - ObserveAndShowChart(state, lineChartCount, lineChartZaps) + ProvideVicoTheme(MaterialTheme.colorScheme.chartStyle) { + ObserveAndShowChart(state) } } } } @Composable -private fun ObserveAndShowChart( - state: NotificationSummaryState, - lineChartCount: LineChart, - lineChartZaps: LineChart, -) { - val axisModel = state.axisLabels.collectAsStateWithLifecycle() +private fun ObserveAndShowChart(state: NotificationSummaryState) { val chartModel by state.chartModel.collectAsStateWithLifecycle() chartModel?.let { - Chart( - chart = remember(lineChartCount, lineChartZaps) { lineChartCount.plus(lineChartZaps) }, - model = it, - startAxis = - rememberStartAxis( - valueFormatter = CountAxisValueFormatter(), - ), - endAxis = - rememberEndAxis( - label = axisLabelComponent(color = BitcoinOrange), - valueFormatter = AmountAxisValueFormatter(state.shouldShowDecimalsInAxis), - ), - bottomAxis = - rememberBottomAxis( - valueFormatter = LabelValueFormatter(axisModel), - ), - ) - } -} - -@Stable -class LabelValueFormatter( - val axisLabels: State>, -) : AxisValueFormatter { - override fun formatValue( - value: Float, - chartValues: ChartValues, - ): String = axisLabels.value[value.roundToInt()] -} - -@Stable -class CountAxisValueFormatter : AxisValueFormatter { - override fun formatValue( - value: Float, - chartValues: ChartValues, - ): String = showCount(value.roundToInt()) -} - -@Stable -class AmountAxisValueFormatter( - val showDecimals: Boolean, -) : AxisValueFormatter { - override fun formatValue( - value: Float, - chartValues: ChartValues, - ): String = - if (showDecimals) { - showAmount(value.toBigDecimal()) - } else { - showAmountAxis(value.toBigDecimal()) - } -} - -var dfG: DecimalFormat = DecimalFormat("#G") -var dfM: DecimalFormat = DecimalFormat("#M") -var dfK: DecimalFormat = DecimalFormat("#k") -var dfN: DecimalFormat = DecimalFormat("#") - -fun showAmountAxis(amount: BigDecimal?): String { - if (amount == null) return "" - if (amount.abs() < BigDecimal(0.01)) return "" - - return when { - amount >= OneGiga -> dfG.format(amount.div(OneGiga).setScale(0, RoundingMode.HALF_UP)) - amount >= OneMega -> dfM.format(amount.div(OneMega).setScale(0, RoundingMode.HALF_UP)) - amount >= TenKilo -> dfK.format(amount.div(OneKilo).setScale(0, RoundingMode.HALF_UP)) - else -> dfN.format(amount) + ShowChart(it) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/AmountValueFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/AmountValueFormatter.kt new file mode 100644 index 000000000..c1b0a79b0 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/AmountValueFormatter.kt @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.chart + +import androidx.compose.runtime.Stable +import com.patrykandpatrick.vico.core.cartesian.CartesianMeasuringContext +import com.patrykandpatrick.vico.core.cartesian.axis.Axis +import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter +import com.vitorpamplona.amethyst.ui.note.showAmountIntegerWithZero +import com.vitorpamplona.amethyst.ui.note.showAmountWithZero +import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.ShowDecimals + +@Stable +class AmountValueFormatter : CartesianValueFormatter { + override fun format( + context: CartesianMeasuringContext, + value: Double, + verticalAxisPosition: Axis.Position.Vertical?, + ): CharSequence = + if (context.model.extraStore[ShowDecimals]) { + showAmountWithZero(value.toBigDecimal()) + } else { + showAmountIntegerWithZero(value.toBigDecimal()) + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/CountAxisValueFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/CountAxisValueFormatter.kt new file mode 100644 index 000000000..1f64239b7 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/CountAxisValueFormatter.kt @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.chart + +import androidx.compose.runtime.Stable +import com.patrykandpatrick.vico.core.cartesian.CartesianMeasuringContext +import com.patrykandpatrick.vico.core.cartesian.axis.Axis +import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter +import kotlin.math.roundToInt + +@Stable +class CountAxisValueFormatter : CartesianValueFormatter { + private fun showCountChart(count: Int?): String { + if (count == null) return "0" + + return when { + count >= 1000000000 -> "${(count / 1000000000f).roundToInt()}G" + count >= 1000000 -> "${(count / 1000000f).roundToInt()}M" + count >= 10000 -> "${(count / 1000f).roundToInt()}k" + else -> "$count" + } + } + + override fun format( + context: CartesianMeasuringContext, + value: Double, + verticalAxisPosition: Axis.Position.Vertical?, + ): CharSequence = showCountChart(value.roundToInt()) +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/LastWeekLabelFormatter.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/LastWeekLabelFormatter.kt new file mode 100644 index 000000000..d9e44e015 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/LastWeekLabelFormatter.kt @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.chart + +import android.util.LruCache +import androidx.compose.runtime.Stable +import com.patrykandpatrick.vico.core.cartesian.CartesianMeasuringContext +import com.patrykandpatrick.vico.core.cartesian.axis.Axis +import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import kotlin.math.roundToInt + +@Stable +class LastWeekLabelFormatter : CartesianValueFormatter { + private val displayAxisFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("EEE") + private val now = LocalDateTime.now() + + private val cache = LruCache(10) + + override fun format( + context: CartesianMeasuringContext, + value: Double, + verticalAxisPosition: Axis.Position.Vertical?, + ): CharSequence { + val key = value.roundToInt() + cache[key]?.let { return it } + + val text = displayAxisFormatter.format(now.plusDays(key.toLong())) + cache.put(key, text) + return text + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/ShowChart.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/ShowChart.kt new file mode 100644 index 000000000..d3a98858f --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/notifications/chart/ShowChart.kt @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2024 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.chart + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost +import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent +import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottom +import com.patrykandpatrick.vico.compose.cartesian.axis.rememberEnd +import com.patrykandpatrick.vico.compose.cartesian.axis.rememberStart +import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart +import com.patrykandpatrick.vico.compose.common.fill +import com.patrykandpatrick.vico.core.cartesian.axis.Axis +import com.patrykandpatrick.vico.core.cartesian.axis.HorizontalAxis +import com.patrykandpatrick.vico.core.cartesian.axis.VerticalAxis +import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModel +import com.patrykandpatrick.vico.core.cartesian.layer.LineCartesianLayer +import com.patrykandpatrick.vico.core.common.shader.ShaderProvider +import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange +import com.vitorpamplona.amethyst.ui.theme.RoyalBlue + +fun makeLine(color: Color): LineCartesianLayer.Line = + LineCartesianLayer.Line( + fill = LineCartesianLayer.LineFill.single(fill(color)), + areaFill = + LineCartesianLayer.AreaFill.single( + fill( + ShaderProvider.verticalGradient( + color.copy(alpha = 0.4f).toArgb(), + Color.Transparent.toArgb(), + ), + ), + ), + pointConnector = LineCartesianLayer.PointConnector.cubic(), + ) + +val chartLayers = + arrayOf( + LineCartesianLayer( + LineCartesianLayer.LineProvider.series( + makeLine(RoyalBlue), + makeLine(Color.Green), + makeLine(Color.Red), + ), + verticalAxisPosition = Axis.Position.Vertical.Start, + ), + LineCartesianLayer( + LineCartesianLayer.LineProvider.series( + makeLine(BitcoinOrange), + ), + verticalAxisPosition = Axis.Position.Vertical.End, + ), + ) + +@Composable +fun ShowChart(model: CartesianChartModel) { + val chart = + rememberCartesianChart( + layers = chartLayers, + startAxis = + VerticalAxis.rememberStart( + valueFormatter = CountAxisValueFormatter(), + itemPlacer = VerticalAxis.ItemPlacer.count({ 7 }), + ), + endAxis = + VerticalAxis.rememberEnd( + label = rememberAxisLabelComponent(color = BitcoinOrange), + valueFormatter = AmountValueFormatter(), + itemPlacer = VerticalAxis.ItemPlacer.count({ 7 }), + ), + bottomAxis = + HorizontalAxis.rememberBottom( + valueFormatter = LastWeekLabelFormatter(), + ), + ) + + CartesianChartHost( + chart = chart, + model = model, + ) +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt index 988eb7096..2073699c1 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/profile/ProfileScreen.kt @@ -145,6 +145,7 @@ import com.vitorpamplona.amethyst.ui.note.LightningAddressIcon import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote import com.vitorpamplona.amethyst.ui.note.externalLinkForUser import com.vitorpamplona.amethyst.ui.note.payViaIntent +import com.vitorpamplona.amethyst.ui.note.showAmountInteger import com.vitorpamplona.amethyst.ui.screen.NostrUserAppRecommendationsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileBookmarksFeedViewModel import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileConversationsFeedViewModel @@ -159,7 +160,6 @@ import com.vitorpamplona.amethyst.ui.screen.SaveableGridFeedState import com.vitorpamplona.amethyst.ui.screen.UserFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagHeader -import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.showAmountAxis import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.gallery.RenderGalleryFeed import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.ShowQRDialog import com.vitorpamplona.amethyst.ui.stringRes @@ -729,7 +729,7 @@ private fun ZapTabHeader(baseUser: User) { } } - Text(text = "${showAmountAxis(zapAmount)} ${stringRes(id = R.string.zaps)}") + Text(text = "${showAmountInteger(zapAmount)} ${stringRes(id = R.string.zaps)}") } @Composable diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt index e1c66feca..17e6df82c 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/theme/Theme.kt @@ -58,8 +58,8 @@ import androidx.core.view.WindowCompat import androidx.lifecycle.viewmodel.compose.viewModel import com.halilibo.richtext.ui.RichTextStyle import com.halilibo.richtext.ui.resolveDefaults -import com.patrykandpatrick.vico.compose.style.ChartStyle -import com.patrykandpatrick.vico.core.DefaultColors +import com.patrykandpatrick.vico.compose.common.VicoTheme +import com.patrykandpatrick.vico.compose.common.VicoTheme.CandlestickCartesianLayerColors import com.vitorpamplona.amethyst.model.ThemeType import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel @@ -441,22 +441,34 @@ val ColorScheme.largeRelayIconModifier: Modifier val ColorScheme.selectedReactionBoxModifier: Modifier get() = if (isLight) LightSelectedReactionBoxModifier else DarkSelectedReactionBoxModifier -val ColorScheme.chartStyle: ChartStyle - get() { - val defaultColors = if (isLight) DefaultColors.Light else DefaultColors.Dark - return ChartStyle.fromColors( - axisLabelColor = Color(defaultColors.axisLabelColor), - axisGuidelineColor = Color(defaultColors.axisGuidelineColor), - axisLineColor = Color(defaultColors.axisLineColor), - entityColors = - listOf( - defaultColors.entity1Color, - defaultColors.entity2Color, - defaultColors.entity3Color, - ).map(::Color), - elevationOverlayColor = Color(defaultColors.elevationOverlayColor), - ) - } +val chartLightColors = + VicoTheme( + candlestickCartesianLayerColors = + CandlestickCartesianLayerColors( + Color(0xff0ac285), + Color(0xff000000), + Color(0xffe8304f), + ), + columnCartesianLayerColors = listOf(Color(0xff3287ff), Color(0xff0ac285), Color(0xffffab02)), + lineColor = Color(0xffbcbfc2), + textColor = Color(0xff000000), + ) + +val chartDarkColors = + VicoTheme( + candlestickCartesianLayerColors = + CandlestickCartesianLayerColors( + Color(0xff0ac285), + Color(0xffffffff), + Color(0xffe8304f), + ), + columnCartesianLayerColors = listOf(Color(0xff3287ff), Color(0xff0ac285), Color(0xffffab02)), + lineColor = Color(0xff494c50), + textColor = Color(0xffffffff), + ) + +val ColorScheme.chartStyle: VicoTheme + get() = if (isLight) chartLightColors else chartDarkColors @Composable fun AmethystTheme( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5d171da54..8d874cd70 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -46,7 +46,7 @@ torAndroid = "0.4.8.12" translate = "17.0.3" unifiedpush = "2.3.1" urlDetector = "0.1.23" -vico-charts = "1.16.0" +vico-charts = "2.0.1" zelory = "3.0.1" zoomable = "2.0.0" zxing = "3.5.3"