Moves to vico chart 2.0
This commit is contained in:
+1
-1
@@ -42,7 +42,7 @@ import org.junit.Test
|
|||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class ThreadAssemblerTest {
|
class ThreadDualAxisChartAssemblerTest {
|
||||||
val db =
|
val db =
|
||||||
"""
|
"""
|
||||||
[
|
[
|
||||||
@@ -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.dvms.observeAppDefinition
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.home.CheckIfVideoIsOnline
|
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.equalImmutableLists
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.showAmountAxis
|
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
|
||||||
import com.vitorpamplona.amethyst.ui.theme.HalfPadding
|
import com.vitorpamplona.amethyst.ui.theme.HalfPadding
|
||||||
@@ -386,7 +385,7 @@ fun InnerRenderClassifiedsThumb(
|
|||||||
card.price?.let {
|
card.price?.let {
|
||||||
val priceTag =
|
val priceTag =
|
||||||
remember(card) {
|
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) {
|
if (it.frequency != null && it.currency != null) {
|
||||||
"$newAmount ${it.currency}/${it.frequency}"
|
"$newAmount ${it.currency}/${it.frequency}"
|
||||||
|
|||||||
@@ -74,3 +74,10 @@ fun showAmount(amount: BigDecimal?): String {
|
|||||||
else -> dfN.get().format(amount)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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<DecimalFormat>() {
|
||||||
|
override fun initialValue() = DecimalFormat("#G")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val dfM =
|
||||||
|
object : ThreadLocal<DecimalFormat>() {
|
||||||
|
override fun initialValue() = DecimalFormat("#M")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val dfK =
|
||||||
|
object : ThreadLocal<DecimalFormat>() {
|
||||||
|
override fun initialValue() = DecimalFormat("#k")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val dfN =
|
||||||
|
object : ThreadLocal<DecimalFormat>() {
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -47,7 +47,6 @@ import com.vitorpamplona.amethyst.model.Note
|
|||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.INav
|
import com.vitorpamplona.amethyst.ui.navigation.INav
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
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.FollowButton
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ShowUserButton
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.ShowUserButton
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.UnfollowButton
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.profile.UnfollowButton
|
||||||
@@ -144,7 +143,7 @@ private fun ZapAmount(zapEventNote: Note) {
|
|||||||
|
|
||||||
LaunchedEffect(key1 = noteState) {
|
LaunchedEffect(key1 = noteState) {
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
val newZapAmount = showAmountAxis((noteState?.note?.event as? LnZapEvent)?.amount)
|
val newZapAmount = showAmountInteger((noteState?.note?.event as? LnZapEvent)?.amount)
|
||||||
if (zapAmount != newZapAmount) {
|
if (zapAmount != newZapAmount) {
|
||||||
zapAmount = newZapAmount
|
zapAmount = newZapAmount
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -64,11 +64,11 @@ import com.vitorpamplona.amethyst.ui.navigation.Route
|
|||||||
import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification
|
import com.vitorpamplona.amethyst.ui.note.ZapAmountCommentNotification
|
||||||
import com.vitorpamplona.amethyst.ui.note.ZapraiserStatus
|
import com.vitorpamplona.amethyst.ui.note.ZapraiserStatus
|
||||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
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.SettingsState
|
||||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CardFeedState
|
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.CombinedZap
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.showAmountAxis
|
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorSettings
|
import com.vitorpamplona.amethyst.ui.tor.TorSettings
|
||||||
import com.vitorpamplona.ammolite.relays.BundledInsert
|
import com.vitorpamplona.ammolite.relays.BundledInsert
|
||||||
@@ -536,7 +536,7 @@ class AccountViewModel(
|
|||||||
it.request.event
|
it.request.event
|
||||||
?.content
|
?.content
|
||||||
?.ifBlank { null },
|
?.ifBlank { null },
|
||||||
showAmountAxis((it.response.event as? LnZapEvent)?.amount),
|
showAmountInteger((it.response.event as? LnZapEvent)?.amount),
|
||||||
)
|
)
|
||||||
}.toMutableMap()
|
}.toMutableMap()
|
||||||
|
|
||||||
@@ -569,7 +569,7 @@ class AccountViewModel(
|
|||||||
ZapAmountCommentNotification(
|
ZapAmountCommentNotification(
|
||||||
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.request.author,
|
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.request.author,
|
||||||
cachedPrivateRequest.content.ifBlank { null },
|
cachedPrivateRequest.content.ifBlank { null },
|
||||||
showAmountAxis((it.response.event as? LnZapEvent)?.amount),
|
showAmountInteger((it.response.event as? LnZapEvent)?.amount),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ZapAmountCommentNotification(
|
ZapAmountCommentNotification(
|
||||||
@@ -577,7 +577,7 @@ class AccountViewModel(
|
|||||||
it.request.event
|
it.request.event
|
||||||
?.content
|
?.content
|
||||||
?.ifBlank { null },
|
?.ifBlank { null },
|
||||||
showAmountAxis((it.response.event as? LnZapEvent)?.amount),
|
showAmountInteger((it.response.event as? LnZapEvent)?.amount),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -586,7 +586,7 @@ class AccountViewModel(
|
|||||||
it.request.event
|
it.request.event
|
||||||
?.content
|
?.content
|
||||||
?.ifBlank { null },
|
?.ifBlank { null },
|
||||||
showAmountAxis((it.response.event as? LnZapEvent)?.amount),
|
showAmountInteger((it.response.event as? LnZapEvent)?.amount),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
@@ -603,7 +603,7 @@ class AccountViewModel(
|
|||||||
ZapAmountCommentNotification(
|
ZapAmountCommentNotification(
|
||||||
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.first.author,
|
LocalCache.getUserIfExists(cachedPrivateRequest.pubKey) ?: it.first.author,
|
||||||
cachedPrivateRequest.content.ifBlank { null },
|
cachedPrivateRequest.content.ifBlank { null },
|
||||||
showAmountAxis((it.second?.event as? LnZapEvent)?.amount),
|
showAmountInteger((it.second?.event as? LnZapEvent)?.amount),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
ZapAmountCommentNotification(
|
ZapAmountCommentNotification(
|
||||||
@@ -611,7 +611,7 @@ class AccountViewModel(
|
|||||||
it.first.event
|
it.first.event
|
||||||
?.content
|
?.content
|
||||||
?.ifBlank { null },
|
?.ifBlank { null },
|
||||||
showAmountAxis((it.second?.event as? LnZapEvent)?.amount),
|
showAmountInteger((it.second?.event as? LnZapEvent)?.amount),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -620,7 +620,7 @@ class AccountViewModel(
|
|||||||
it.first.event
|
it.first.event
|
||||||
?.content
|
?.content
|
||||||
?.ifBlank { null },
|
?.ifBlank { null },
|
||||||
showAmountAxis((it.second?.event as? LnZapEvent)?.amount),
|
showAmountInteger((it.second?.event as? LnZapEvent)?.amount),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.toImmutableList()
|
}.toImmutableList()
|
||||||
@@ -642,7 +642,7 @@ class AccountViewModel(
|
|||||||
it.first.event
|
it.first.event
|
||||||
?.content
|
?.content
|
||||||
?.ifBlank { null },
|
?.ifBlank { null },
|
||||||
showAmountAxis((it.second?.event as? LnZapEvent)?.amount),
|
showAmountInteger((it.second?.event as? LnZapEvent)?.amount),
|
||||||
)
|
)
|
||||||
}.toMutableMap()
|
}.toMutableMap()
|
||||||
|
|
||||||
@@ -687,7 +687,7 @@ class AccountViewModel(
|
|||||||
ZapAmountCommentNotification(
|
ZapAmountCommentNotification(
|
||||||
newAuthor,
|
newAuthor,
|
||||||
decryptedContent.content.ifBlank { null },
|
decryptedContent.content.ifBlank { null },
|
||||||
showAmountAxis(amount),
|
showAmountInteger(amount),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -698,7 +698,7 @@ class AccountViewModel(
|
|||||||
ZapAmountCommentNotification(
|
ZapAmountCommentNotification(
|
||||||
zapRequest.author,
|
zapRequest.author,
|
||||||
zapRequest.event?.content?.ifBlank { null },
|
zapRequest.event?.content?.ifBlank { null },
|
||||||
showAmountAxis(amount),
|
showAmountInteger(amount),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-77
@@ -22,16 +22,16 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications
|
|||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import com.patrykandpatrick.vico.core.chart.composed.ComposedChartEntryModel
|
import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModel
|
||||||
import com.patrykandpatrick.vico.core.entry.ChartEntryModel
|
import com.patrykandpatrick.vico.core.cartesian.data.LineCartesianLayerModel
|
||||||
import com.patrykandpatrick.vico.core.entry.ChartEntryModelProducer
|
import com.patrykandpatrick.vico.core.common.data.ExtraStore
|
||||||
import com.patrykandpatrick.vico.core.entry.composed.plus
|
import com.patrykandpatrick.vico.core.common.data.MutableExtraStore
|
||||||
import com.patrykandpatrick.vico.core.entry.entryOf
|
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
import com.vitorpamplona.amethyst.model.LocalCache
|
||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.model.User
|
import com.vitorpamplona.amethyst.model.User
|
||||||
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
import com.vitorpamplona.amethyst.service.checkNotInMainThread
|
||||||
|
import com.vitorpamplona.amethyst.ui.note.showAmountInteger
|
||||||
import com.vitorpamplona.amethyst.ui.note.showCount
|
import com.vitorpamplona.amethyst.ui.note.showCount
|
||||||
import com.vitorpamplona.ammolite.relays.BundledInsert
|
import com.vitorpamplona.ammolite.relays.BundledInsert
|
||||||
import com.vitorpamplona.quartz.nip01Core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.HexKey
|
||||||
@@ -52,37 +52,30 @@ import java.time.LocalDateTime
|
|||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
val ShowDecimals = ExtraStore.Key<Boolean>()
|
||||||
|
val BottomAxisLabelKey = ExtraStore.Key<List<String>>()
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class NotificationSummaryState(
|
class NotificationSummaryState(
|
||||||
val account: Account,
|
val account: Account,
|
||||||
) {
|
) {
|
||||||
val user: User = account.userProfile()
|
val user: User = account.userProfile()
|
||||||
|
|
||||||
private var _reactions = MutableStateFlow<Map<String, Int>>(emptyMap())
|
private var reactions = MutableStateFlow<Map<String, Int>>(emptyMap())
|
||||||
private var _boosts = MutableStateFlow<Map<String, Int>>(emptyMap())
|
private var boosts = MutableStateFlow<Map<String, Int>>(emptyMap())
|
||||||
private var _zaps = MutableStateFlow<Map<String, BigDecimal>>(emptyMap())
|
private var zaps = MutableStateFlow<Map<String, BigDecimal>>(emptyMap())
|
||||||
private var _replies = MutableStateFlow<Map<String, Int>>(emptyMap())
|
private var replies = MutableStateFlow<Map<String, Int>>(emptyMap())
|
||||||
|
|
||||||
private var _chartModel = MutableStateFlow<ComposedChartEntryModel<ChartEntryModel>?>(null)
|
|
||||||
private var _axisLabels = MutableStateFlow<List<String>>(emptyList())
|
|
||||||
|
|
||||||
val reactions = _reactions.asStateFlow()
|
|
||||||
val boosts = _boosts.asStateFlow()
|
|
||||||
val zaps = _zaps.asStateFlow()
|
|
||||||
val replies = _replies.asStateFlow()
|
|
||||||
|
|
||||||
|
private var _chartModel = MutableStateFlow<CartesianChartModel?>(null)
|
||||||
val chartModel = _chartModel.asStateFlow()
|
val chartModel = _chartModel.asStateFlow()
|
||||||
val axisLabels = _axisLabels.asStateFlow()
|
|
||||||
|
|
||||||
private var takenIntoAccount = setOf<HexKey>()
|
private var takenIntoAccount = setOf<HexKey>()
|
||||||
private val sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd") // SimpleDateFormat()
|
private val sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd") // SimpleDateFormat()
|
||||||
|
|
||||||
val todaysReplyCount = _replies.map { showCount(it[today()]) }.distinctUntilChanged()
|
val todaysReplyCount = replies.map { showCount(it[today()]) }.distinctUntilChanged()
|
||||||
val todaysBoostCount = _boosts.map { showCount(it[today()]) }.distinctUntilChanged()
|
val todaysBoostCount = boosts.map { showCount(it[today()]) }.distinctUntilChanged()
|
||||||
val todaysReactionCount = _reactions.map { showCount(it[today()]) }.distinctUntilChanged()
|
val todaysReactionCount = reactions.map { showCount(it[today()]) }.distinctUntilChanged()
|
||||||
val todaysZapAmount = _zaps.map { showAmountAxis(it[today()]) }.distinctUntilChanged()
|
val todaysZapAmount = zaps.map { showAmountInteger(it[today()]) }.distinctUntilChanged()
|
||||||
|
|
||||||
var shouldShowDecimalsInAxis = false
|
|
||||||
|
|
||||||
fun formatDate(createAt: Long): String =
|
fun formatDate(createAt: Long): String =
|
||||||
sdf.format(
|
sdf.format(
|
||||||
@@ -146,10 +139,10 @@ class NotificationSummaryState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.takenIntoAccount = takenIntoAccount
|
this.takenIntoAccount = takenIntoAccount
|
||||||
this._reactions.emit(reactions)
|
this.reactions.emit(reactions)
|
||||||
this._replies.emit(replies)
|
this.replies.emit(replies)
|
||||||
this._zaps.emit(zaps)
|
this.zaps.emit(zaps)
|
||||||
this._boosts.emit(boosts)
|
this.boosts.emit(boosts)
|
||||||
|
|
||||||
refreshChartModel()
|
refreshChartModel()
|
||||||
}
|
}
|
||||||
@@ -159,10 +152,10 @@ class NotificationSummaryState(
|
|||||||
|
|
||||||
val currentUser = user.pubkeyHex
|
val currentUser = user.pubkeyHex
|
||||||
|
|
||||||
val reactions = this._reactions.value.toMutableMap()
|
val reactions = this.reactions.value.toMutableMap()
|
||||||
val boosts = this._boosts.value.toMutableMap()
|
val boosts = this.boosts.value.toMutableMap()
|
||||||
val zaps = this._zaps.value.toMutableMap()
|
val zaps = this.zaps.value.toMutableMap()
|
||||||
val replies = this._replies.value.toMutableMap()
|
val replies = this.replies.value.toMutableMap()
|
||||||
val takenIntoAccount = this.takenIntoAccount.toMutableSet()
|
val takenIntoAccount = this.takenIntoAccount.toMutableSet()
|
||||||
var hasNewElements = false
|
var hasNewElements = false
|
||||||
|
|
||||||
@@ -217,10 +210,10 @@ class NotificationSummaryState(
|
|||||||
|
|
||||||
if (hasNewElements) {
|
if (hasNewElements) {
|
||||||
this.takenIntoAccount = takenIntoAccount
|
this.takenIntoAccount = takenIntoAccount
|
||||||
this._reactions.emit(reactions)
|
this.reactions.emit(reactions)
|
||||||
this._replies.emit(replies)
|
this.replies.emit(replies)
|
||||||
this._zaps.emit(zaps)
|
this.zaps.emit(zaps)
|
||||||
this._boosts.emit(boosts)
|
this.boosts.emit(boosts)
|
||||||
|
|
||||||
refreshChartModel()
|
refreshChartModel()
|
||||||
}
|
}
|
||||||
@@ -229,59 +222,42 @@ class NotificationSummaryState(
|
|||||||
private suspend fun refreshChartModel() {
|
private suspend fun refreshChartModel() {
|
||||||
checkNotInMainThread()
|
checkNotInMainThread()
|
||||||
|
|
||||||
val day = 24 * 60 * 60L
|
|
||||||
val now = LocalDateTime.now()
|
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 =
|
val chart1 =
|
||||||
listOf(
|
LineCartesianLayerModel.build {
|
||||||
dataAxisLabels.mapIndexed { index, dateStr ->
|
series(dataAxisLabelIndexes, dataAxisLabels.map { replies.value[it]?.toFloat() ?: 0f })
|
||||||
entryOf(index, _replies.value[dateStr]?.toFloat() ?: 0f)
|
series(dataAxisLabelIndexes, dataAxisLabels.map { boosts.value[it]?.toFloat() ?: 0f })
|
||||||
},
|
series(dataAxisLabelIndexes, dataAxisLabels.map { reactions.value[it]?.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 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.
|
// determine if the min max are so close that they render to the same number.
|
||||||
fun shouldShowDecimals(
|
fun shouldShowDecimals(
|
||||||
min: Float,
|
min: Double,
|
||||||
max: Float,
|
max: Double,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val step = (max - min) / 8
|
val step = (max - min) / 8
|
||||||
|
|
||||||
var previous = showAmountAxis(min.toBigDecimal())
|
var previous = showAmountInteger(min.toBigDecimal())
|
||||||
for (i in 1..7) {
|
for (i in 1..7) {
|
||||||
val current = showAmountAxis((min + (i * step)).toBigDecimal())
|
val current = showAmountInteger((min + (i * step)).toBigDecimal())
|
||||||
if (previous == current) {
|
if (previous == current) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-149
@@ -30,47 +30,17 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
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.Color
|
|
||||||
import androidx.compose.ui.graphics.toArgb
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import com.patrykandpatrick.vico.compose.axis.axisLabelComponent
|
import com.patrykandpatrick.vico.compose.common.ProvideVicoTheme
|
||||||
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.vitorpamplona.amethyst.ui.note.UserReactionsRow
|
import com.vitorpamplona.amethyst.ui.note.UserReactionsRow
|
||||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.chart.ShowChart
|
||||||
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.theme.chartStyle
|
import com.vitorpamplona.amethyst.ui.theme.chartStyle
|
||||||
import java.math.BigDecimal
|
|
||||||
import java.math.RoundingMode
|
|
||||||
import java.text.DecimalFormat
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SummaryBar(state: NotificationSummaryState) {
|
fun SummaryBar(state: NotificationSummaryState) {
|
||||||
@@ -83,137 +53,24 @@ fun SummaryBar(state: NotificationSummaryState) {
|
|||||||
enter = slideInVertically() + expandVertically(),
|
enter = slideInVertically() + expandVertically(),
|
||||||
exit = slideOutVertically() + shrinkVertically(),
|
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(
|
Row(
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(vertical = 0.dp, horizontal = 20.dp)
|
.padding(vertical = 0.dp, horizontal = 20.dp)
|
||||||
.clickable(onClick = { showChart = !showChart }),
|
.clickable(onClick = { showChart = !showChart }),
|
||||||
) {
|
) {
|
||||||
ProvideChartStyle(
|
ProvideVicoTheme(MaterialTheme.colorScheme.chartStyle) {
|
||||||
chartStyle = MaterialTheme.colorScheme.chartStyle,
|
ObserveAndShowChart(state)
|
||||||
) {
|
|
||||||
ObserveAndShowChart(state, lineChartCount, lineChartZaps)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ObserveAndShowChart(
|
private fun ObserveAndShowChart(state: NotificationSummaryState) {
|
||||||
state: NotificationSummaryState,
|
|
||||||
lineChartCount: LineChart,
|
|
||||||
lineChartZaps: LineChart,
|
|
||||||
) {
|
|
||||||
val axisModel = state.axisLabels.collectAsStateWithLifecycle()
|
|
||||||
val chartModel by state.chartModel.collectAsStateWithLifecycle()
|
val chartModel by state.chartModel.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
chartModel?.let {
|
chartModel?.let {
|
||||||
Chart(
|
ShowChart(it)
|
||||||
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<List<String>>,
|
|
||||||
) : AxisValueFormatter<AxisPosition.Horizontal.Bottom> {
|
|
||||||
override fun formatValue(
|
|
||||||
value: Float,
|
|
||||||
chartValues: ChartValues,
|
|
||||||
): String = axisLabels.value[value.roundToInt()]
|
|
||||||
}
|
|
||||||
|
|
||||||
@Stable
|
|
||||||
class CountAxisValueFormatter : AxisValueFormatter<AxisPosition.Vertical.Start> {
|
|
||||||
override fun formatValue(
|
|
||||||
value: Float,
|
|
||||||
chartValues: ChartValues,
|
|
||||||
): String = showCount(value.roundToInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Stable
|
|
||||||
class AmountAxisValueFormatter(
|
|
||||||
val showDecimals: Boolean,
|
|
||||||
) : AxisValueFormatter<AxisPosition.Vertical.End> {
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
+47
@@ -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())
|
||||||
|
}
|
||||||
+51
@@ -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<Int, String>(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
|
||||||
|
}
|
||||||
|
}
|
||||||
+101
@@ -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,
|
||||||
|
)
|
||||||
|
}
|
||||||
+2
-2
@@ -145,6 +145,7 @@ import com.vitorpamplona.amethyst.ui.note.LightningAddressIcon
|
|||||||
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
import com.vitorpamplona.amethyst.ui.note.LoadAddressableNote
|
||||||
import com.vitorpamplona.amethyst.ui.note.externalLinkForUser
|
import com.vitorpamplona.amethyst.ui.note.externalLinkForUser
|
||||||
import com.vitorpamplona.amethyst.ui.note.payViaIntent
|
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.NostrUserAppRecommendationsFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileBookmarksFeedViewModel
|
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileBookmarksFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.NostrUserProfileConversationsFeedViewModel
|
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.UserFeedViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.hashtag.HashtagHeader
|
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.profile.gallery.RenderGalleryFeed
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.ShowQRDialog
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.qrcode.ShowQRDialog
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
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
|
@Composable
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ import androidx.core.view.WindowCompat
|
|||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.halilibo.richtext.ui.RichTextStyle
|
import com.halilibo.richtext.ui.RichTextStyle
|
||||||
import com.halilibo.richtext.ui.resolveDefaults
|
import com.halilibo.richtext.ui.resolveDefaults
|
||||||
import com.patrykandpatrick.vico.compose.style.ChartStyle
|
import com.patrykandpatrick.vico.compose.common.VicoTheme
|
||||||
import com.patrykandpatrick.vico.core.DefaultColors
|
import com.patrykandpatrick.vico.compose.common.VicoTheme.CandlestickCartesianLayerColors
|
||||||
import com.vitorpamplona.amethyst.model.ThemeType
|
import com.vitorpamplona.amethyst.model.ThemeType
|
||||||
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
import com.vitorpamplona.amethyst.ui.screen.SharedPreferencesViewModel
|
||||||
|
|
||||||
@@ -441,22 +441,34 @@ val ColorScheme.largeRelayIconModifier: Modifier
|
|||||||
val ColorScheme.selectedReactionBoxModifier: Modifier
|
val ColorScheme.selectedReactionBoxModifier: Modifier
|
||||||
get() = if (isLight) LightSelectedReactionBoxModifier else DarkSelectedReactionBoxModifier
|
get() = if (isLight) LightSelectedReactionBoxModifier else DarkSelectedReactionBoxModifier
|
||||||
|
|
||||||
val ColorScheme.chartStyle: ChartStyle
|
val chartLightColors =
|
||||||
get() {
|
VicoTheme(
|
||||||
val defaultColors = if (isLight) DefaultColors.Light else DefaultColors.Dark
|
candlestickCartesianLayerColors =
|
||||||
return ChartStyle.fromColors(
|
CandlestickCartesianLayerColors(
|
||||||
axisLabelColor = Color(defaultColors.axisLabelColor),
|
Color(0xff0ac285),
|
||||||
axisGuidelineColor = Color(defaultColors.axisGuidelineColor),
|
Color(0xff000000),
|
||||||
axisLineColor = Color(defaultColors.axisLineColor),
|
Color(0xffe8304f),
|
||||||
entityColors =
|
),
|
||||||
listOf(
|
columnCartesianLayerColors = listOf(Color(0xff3287ff), Color(0xff0ac285), Color(0xffffab02)),
|
||||||
defaultColors.entity1Color,
|
lineColor = Color(0xffbcbfc2),
|
||||||
defaultColors.entity2Color,
|
textColor = Color(0xff000000),
|
||||||
defaultColors.entity3Color,
|
)
|
||||||
).map(::Color),
|
|
||||||
elevationOverlayColor = Color(defaultColors.elevationOverlayColor),
|
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
|
@Composable
|
||||||
fun AmethystTheme(
|
fun AmethystTheme(
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ torAndroid = "0.4.8.12"
|
|||||||
translate = "17.0.3"
|
translate = "17.0.3"
|
||||||
unifiedpush = "2.3.1"
|
unifiedpush = "2.3.1"
|
||||||
urlDetector = "0.1.23"
|
urlDetector = "0.1.23"
|
||||||
vico-charts = "1.16.0"
|
vico-charts = "2.0.1"
|
||||||
zelory = "3.0.1"
|
zelory = "3.0.1"
|
||||||
zoomable = "2.0.0"
|
zoomable = "2.0.0"
|
||||||
zxing = "3.5.3"
|
zxing = "3.5.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user