mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-07-22 14:55:48 +00:00
feat: add Mining Simulation card to fill empty space in Pezkuwi Trust Score card
Cosmetic, client-side-only preview of a trust-score-weighted PEZ emission share, meant to incentivize referral usage (higher trust score -> visibly faster mining). Not tied to any real distribution mechanism or backend. - MiningSimulationFormula: pure calc. Rate per minute = (92.5M PEZ / 43200 min era) * (trustScore / 500,000 reference total) - trust score is a direct multiplier so zero trust score always yields zero rate. Diamonds only accrue while a 24h session is active. - MiningSimulationRepository: local-only persistence via the existing Preferences wrapper (same pattern as BannerVisibilityRepository), keyed per accountId. Tapping the square freezes the live total as the new base and starts a fresh 24h session - never resets progress on tap. A full 43200-minute era rolling over resets the total to 0 (that era's pool is considered distributed) independently of session taps. - UI: square button in the dashboard card's previously-empty right column, red when inactive (needs a tap), gold (#FDB813, matching the existing Trust Score color) while actively mining. Small Telegram icon opens https://t.me/DKSKurdistanBot via the existing Browserable mixin.
This commit is contained in:
@@ -2777,6 +2777,7 @@
|
||||
<string name="pezkuwi_dashboard_start_tracking">Start Tracking</string>
|
||||
<string name="pezkuwi_dashboard_tracking_success">Score tracking started!</string>
|
||||
<string name="pezkuwi_dashboard_kurds_title">Hejmara Kurd Le Cihane</string>
|
||||
<string name="pezkuwi_dashboard_mining_simulation_title">Mining Simulation</string>
|
||||
|
||||
<string name="pending_signatures_title">Pending Signatures</string>
|
||||
<string name="pending_signatures_sign">Sign</string>
|
||||
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package io.novafoundation.nova.feature_assets.domain.dashboard
|
||||
|
||||
import java.math.BigInteger
|
||||
|
||||
/**
|
||||
* Cosmetic, client-side-only preview of what a user's PEZ trust-score-weighted emission share
|
||||
* could look like - not tied to any real distribution mechanism or backend. Numbers loosely track
|
||||
* the real era emission (92.5M PEZ per ~30-day era) without claiming accuracy; see the "referral
|
||||
* incentive" mining card this backs.
|
||||
*/
|
||||
object MiningSimulationFormula {
|
||||
|
||||
private const val ERA_DAYS = 30
|
||||
const val ERA_DURATION_MS = ERA_DAYS * 24 * 60 * 60 * 1000L
|
||||
private const val ERA_MINUTES = ERA_DAYS * 24 * 60
|
||||
private const val ERA_POOL_PEZ = 92_500_000.0
|
||||
|
||||
/** Assumed total trust score across the network - unknowable client-side, so this is a
|
||||
* simulation-only reference point, not a real aggregate. */
|
||||
private const val REFERENCE_TOTAL_TRUST = 500_000.0
|
||||
|
||||
const val SESSION_DURATION_MS = 24 * 60 * 60 * 1000L
|
||||
|
||||
private val poolPerMinute = ERA_POOL_PEZ / ERA_MINUTES
|
||||
|
||||
/** Zero trust score always yields zero rate - trust score is a direct multiplier, not an offset. */
|
||||
fun ratePerMinute(trustScore: BigInteger): Double {
|
||||
if (trustScore <= BigInteger.ZERO) return 0.0
|
||||
|
||||
return poolPerMinute * (trustScore.toDouble() / REFERENCE_TOTAL_TRUST)
|
||||
}
|
||||
|
||||
fun isSessionActive(sessionActivatedAt: Long, now: Long): Boolean {
|
||||
return sessionActivatedAt > 0L && now < sessionActivatedAt + SESSION_DURATION_MS
|
||||
}
|
||||
|
||||
/** [baseDiamonds] is the frozen within-era total as of [sessionActivatedAt] (0 = no active
|
||||
* session right now, in which case this is just the frozen total with nothing accruing). */
|
||||
fun diamondsWithinEra(baseDiamonds: Double, sessionActivatedAt: Long, trustScore: BigInteger, now: Long): Double {
|
||||
if (sessionActivatedAt <= 0L) return baseDiamonds
|
||||
|
||||
val sessionEnd = sessionActivatedAt + SESSION_DURATION_MS
|
||||
val effectiveNow = minOf(now, sessionEnd)
|
||||
val elapsedMinutes = (effectiveNow - sessionActivatedAt).coerceAtLeast(0) / 60_000.0
|
||||
|
||||
return baseDiamonds + elapsedMinutes * ratePerMinute(trustScore)
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
package io.novafoundation.nova.feature_assets.domain.dashboard
|
||||
|
||||
import io.novafoundation.nova.common.data.storage.Preferences
|
||||
import java.math.BigInteger
|
||||
|
||||
data class MiningSimulationState(
|
||||
val eraStartedAt: Long, // 0 = era never started (no session ever activated)
|
||||
val sessionActivatedAt: Long, // 0 = no active session right now
|
||||
val baseDiamonds: Double, // frozen total within the current era
|
||||
)
|
||||
|
||||
interface MiningSimulationRepository {
|
||||
|
||||
/** Applies any pending era rollover (see [MiningSimulationFormula.ERA_DURATION_MS]) before
|
||||
* returning the current state - callers should always go through this, never read raw prefs. */
|
||||
fun getState(accountId: Long, now: Long): MiningSimulationState
|
||||
|
||||
/** Freezes the live total (as of [now]) as the new base and starts a fresh session -
|
||||
* never resets [MiningSimulationState.baseDiamonds] to zero except via an era rollover. */
|
||||
fun activate(accountId: Long, trustScore: BigInteger, now: Long): MiningSimulationState
|
||||
}
|
||||
|
||||
private const val MICRO_SCALE = 1_000_000L
|
||||
|
||||
class RealMiningSimulationRepository(
|
||||
private val preferences: Preferences
|
||||
) : MiningSimulationRepository {
|
||||
|
||||
override fun getState(accountId: Long, now: Long): MiningSimulationState {
|
||||
return normalize(accountId, now)
|
||||
}
|
||||
|
||||
override fun activate(accountId: Long, trustScore: BigInteger, now: Long): MiningSimulationState {
|
||||
val normalized = normalize(accountId, now)
|
||||
|
||||
val liveDiamonds = MiningSimulationFormula.diamondsWithinEra(
|
||||
baseDiamonds = normalized.baseDiamonds,
|
||||
sessionActivatedAt = normalized.sessionActivatedAt,
|
||||
trustScore = trustScore,
|
||||
now = now
|
||||
)
|
||||
|
||||
val eraStartedAt = if (normalized.eraStartedAt == 0L) now else normalized.eraStartedAt
|
||||
|
||||
val newState = normalized.copy(
|
||||
eraStartedAt = eraStartedAt,
|
||||
sessionActivatedAt = now,
|
||||
baseDiamonds = liveDiamonds
|
||||
)
|
||||
persist(accountId, newState)
|
||||
return newState
|
||||
}
|
||||
|
||||
/** Detects and applies an era rollover (>= ERA_DURATION_MS since era start) - resets both the
|
||||
* diamond total and the active session, since that era's simulated pool is "distributed". */
|
||||
private fun normalize(accountId: Long, now: Long): MiningSimulationState {
|
||||
val stored = read(accountId)
|
||||
if (stored.eraStartedAt == 0L) return stored
|
||||
|
||||
val erasElapsed = (now - stored.eraStartedAt) / MiningSimulationFormula.ERA_DURATION_MS
|
||||
if (erasElapsed <= 0L) return stored
|
||||
|
||||
val rolledOver = stored.copy(
|
||||
eraStartedAt = stored.eraStartedAt + erasElapsed * MiningSimulationFormula.ERA_DURATION_MS,
|
||||
sessionActivatedAt = 0L,
|
||||
baseDiamonds = 0.0
|
||||
)
|
||||
persist(accountId, rolledOver)
|
||||
return rolledOver
|
||||
}
|
||||
|
||||
private fun read(accountId: Long): MiningSimulationState {
|
||||
return MiningSimulationState(
|
||||
eraStartedAt = preferences.getLong(eraKey(accountId), 0L),
|
||||
sessionActivatedAt = preferences.getLong(sessionKey(accountId), 0L),
|
||||
baseDiamonds = preferences.getLong(diamondsKey(accountId), 0L) / MICRO_SCALE.toDouble()
|
||||
)
|
||||
}
|
||||
|
||||
private fun persist(accountId: Long, state: MiningSimulationState) {
|
||||
val editor = preferences.edit()
|
||||
editor.putLong(eraKey(accountId), state.eraStartedAt)
|
||||
editor.putLong(sessionKey(accountId), state.sessionActivatedAt)
|
||||
editor.putLong(diamondsKey(accountId), (state.baseDiamonds * MICRO_SCALE).toLong())
|
||||
editor.apply()
|
||||
}
|
||||
|
||||
private fun eraKey(accountId: Long) = "MiningSimulationRepository.era.$accountId"
|
||||
private fun sessionKey(accountId: Long) = "MiningSimulationRepository.session.$accountId"
|
||||
private fun diamondsKey(accountId: Long) = "MiningSimulationRepository.diamonds.$accountId"
|
||||
}
|
||||
+12
@@ -143,6 +143,10 @@ class BalanceListFragment :
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.miningSimulationFlow.observe { model ->
|
||||
pezkuwiDashboardAdapter.setMiningModel(model)
|
||||
}
|
||||
|
||||
viewModel.bannersMixin.bindWithAdapter(bannerAdapter) {
|
||||
binder.balanceListAssets.invalidateItemDecorations()
|
||||
}
|
||||
@@ -301,6 +305,14 @@ class BalanceListFragment :
|
||||
viewModel.startTrackingClicked()
|
||||
}
|
||||
|
||||
override fun onMiningSquareClicked() {
|
||||
viewModel.miningSquareClicked()
|
||||
}
|
||||
|
||||
override fun onMiningInfoClicked() {
|
||||
viewModel.miningInfoClicked()
|
||||
}
|
||||
|
||||
private fun setupRecyclerViewSpacing() {
|
||||
binder.balanceListAssets.addSpaceItemDecoration {
|
||||
add(SpaceBetween(AssetsHeaderHolder, PezkuwiDashboardHolder, spaceDp = 8))
|
||||
|
||||
+61
-1
@@ -27,7 +27,10 @@ import io.novafoundation.nova.feature_account_api.domain.model.defaultSubstrateA
|
||||
import io.novafoundation.nova.feature_assets.R
|
||||
import io.novafoundation.nova.feature_assets.domain.WalletInteractor
|
||||
import io.novafoundation.nova.feature_assets.domain.assets.list.AssetsListInteractor
|
||||
import io.novafoundation.nova.feature_assets.domain.dashboard.MiningSimulationFormula
|
||||
import io.novafoundation.nova.feature_assets.domain.dashboard.MiningSimulationRepository
|
||||
import io.novafoundation.nova.feature_assets.domain.dashboard.PezkuwiDashboardInteractor
|
||||
import io.novafoundation.nova.feature_assets.presentation.balance.list.model.MiningSimulationModel
|
||||
import io.novafoundation.nova.feature_assets.presentation.balance.list.model.PezkuwiDashboardModel
|
||||
import io.novafoundation.nova.feature_assets.domain.assets.list.NftPreviews
|
||||
import io.novafoundation.nova.feature_assets.domain.breakdown.BalanceBreakdown
|
||||
@@ -74,18 +77,22 @@ import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
|
||||
import io.novasama.substrate_sdk_android.runtime.extrinsic.call
|
||||
import java.text.NumberFormat
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigInteger
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
private typealias SyncAction = suspend (MetaAccount) -> Unit
|
||||
@@ -113,7 +120,8 @@ class BalanceListViewModel(
|
||||
private val giftsRestrictionCheckMixin: GiftsRestrictionCheckMixin,
|
||||
private val pezkuwiDashboardInteractor: PezkuwiDashboardInteractor,
|
||||
private val extrinsicService: ExtrinsicService,
|
||||
private val chainRegistry: ChainRegistry
|
||||
private val chainRegistry: ChainRegistry,
|
||||
private val miningSimulationRepository: MiningSimulationRepository
|
||||
) : BaseViewModel(), Browserable.Presentation by Browserable() {
|
||||
|
||||
private val maskableAmountFormatterFlow = maskableValueFormatterProvider.provideFormatter()
|
||||
@@ -139,6 +147,11 @@ class BalanceListViewModel(
|
||||
|
||||
private val dashboardRefreshSignal = MutableStateFlow(0)
|
||||
|
||||
private val diamondFormatter = NumberFormat.getNumberInstance().apply {
|
||||
minimumFractionDigits = 2
|
||||
maximumFractionDigits = 2
|
||||
}
|
||||
|
||||
val bannersMixin = promotionBannersMixinFactory.create(bannerSourceFactory.assetsSource(), viewModelScope)
|
||||
|
||||
private val selectedCurrency = currencyInteractor.observeSelectCurrency()
|
||||
@@ -256,6 +269,7 @@ class BalanceListViewModel(
|
||||
PezkuwiDashboardModel(
|
||||
roles = data.roles,
|
||||
trustScore = data.trustScore.toString(),
|
||||
trustScoreRaw = data.trustScore,
|
||||
welatiCount = NumberFormat.getIntegerInstance().format(data.welatiCount),
|
||||
citizenshipStatus = data.citizenshipStatus,
|
||||
isTrackingScore = data.isTrackingScore
|
||||
@@ -265,6 +279,52 @@ class BalanceListViewModel(
|
||||
}
|
||||
.shareInBackground()
|
||||
|
||||
private val miningRefreshSignal = MutableStateFlow(0)
|
||||
|
||||
private val miningTicker = flow {
|
||||
while (true) {
|
||||
emit(Unit)
|
||||
delay(60_000L)
|
||||
}
|
||||
}.onStart { emit(Unit) }
|
||||
|
||||
val miningSimulationFlow = combine(
|
||||
pezkuwiDashboardFlow,
|
||||
selectedMetaAccount,
|
||||
miningTicker,
|
||||
miningRefreshSignal
|
||||
) { dashboard, metaAccount, _, _ ->
|
||||
val trustScore = dashboard?.trustScoreRaw ?: BigInteger.ZERO
|
||||
val now = System.currentTimeMillis()
|
||||
val state = miningSimulationRepository.getState(metaAccount.id, now)
|
||||
val diamonds = MiningSimulationFormula.diamondsWithinEra(state.baseDiamonds, state.sessionActivatedAt, trustScore, now)
|
||||
|
||||
MiningSimulationModel(
|
||||
diamondsText = diamondFormatter.format(diamonds),
|
||||
isActive = MiningSimulationFormula.isSessionActive(state.sessionActivatedAt, now)
|
||||
)
|
||||
}.shareInBackground()
|
||||
|
||||
fun miningSquareClicked() {
|
||||
launch {
|
||||
val metaAccount = selectedMetaAccount.first()
|
||||
val now = System.currentTimeMillis()
|
||||
val alreadyActive = MiningSimulationFormula.isSessionActive(
|
||||
miningSimulationRepository.getState(metaAccount.id, now).sessionActivatedAt,
|
||||
now
|
||||
)
|
||||
if (alreadyActive) return@launch
|
||||
|
||||
val trustScore = pezkuwiDashboardFlow.first()?.trustScoreRaw ?: BigInteger.ZERO
|
||||
miningSimulationRepository.activate(metaAccount.id, trustScore, now)
|
||||
miningRefreshSignal.value++
|
||||
}
|
||||
}
|
||||
|
||||
fun miningInfoClicked() {
|
||||
showBrowser("https://t.me/DKSKurdistanBot")
|
||||
}
|
||||
|
||||
init {
|
||||
selectedCurrency
|
||||
.onEach { fullSync() }
|
||||
|
||||
+12
-1
@@ -7,6 +7,7 @@ import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.multibindings.IntoMap
|
||||
import io.novafoundation.nova.common.data.repository.AssetsViewModeRepository
|
||||
import io.novafoundation.nova.common.data.storage.Preferences
|
||||
import io.novafoundation.nova.common.di.scope.ScreenScope
|
||||
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
|
||||
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
|
||||
@@ -20,7 +21,9 @@ import io.novafoundation.nova.feature_assets.domain.WalletInteractor
|
||||
import io.novafoundation.nova.feature_assets.domain.assets.ExternalBalancesInteractor
|
||||
import io.novafoundation.nova.feature_assets.domain.assets.list.AssetsListInteractor
|
||||
import io.novafoundation.nova.feature_assets.domain.breakdown.BalanceBreakdownInteractor
|
||||
import io.novafoundation.nova.feature_assets.domain.dashboard.MiningSimulationRepository
|
||||
import io.novafoundation.nova.feature_assets.domain.dashboard.PezkuwiDashboardInteractor
|
||||
import io.novafoundation.nova.feature_assets.domain.dashboard.RealMiningSimulationRepository
|
||||
import io.novafoundation.nova.runtime.di.REMOTE_STORAGE_SOURCE
|
||||
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
|
||||
import io.novafoundation.nova.runtime.storage.source.StorageDataSource
|
||||
@@ -103,6 +106,12 @@ class BalanceListModule {
|
||||
return PezkuwiDashboardInteractor(repository, chainRegistry)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@ScreenScope
|
||||
fun provideMiningSimulationRepository(preferences: Preferences): MiningSimulationRepository {
|
||||
return RealMiningSimulationRepository(preferences)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IntoMap
|
||||
@ViewModelKey(BalanceListViewModel::class)
|
||||
@@ -130,6 +139,7 @@ class BalanceListModule {
|
||||
pezkuwiDashboardInteractor: PezkuwiDashboardInteractor,
|
||||
extrinsicService: ExtrinsicService,
|
||||
chainRegistry: ChainRegistry,
|
||||
miningSimulationRepository: MiningSimulationRepository,
|
||||
): ViewModel {
|
||||
return BalanceListViewModel(
|
||||
promotionBannersMixinFactory = promotionBannersMixinFactory,
|
||||
@@ -154,7 +164,8 @@ class BalanceListModule {
|
||||
giftsRestrictionCheckMixin = giftsRestrictionCheckMixin,
|
||||
pezkuwiDashboardInteractor = pezkuwiDashboardInteractor,
|
||||
extrinsicService = extrinsicService,
|
||||
chainRegistry = chainRegistry
|
||||
chainRegistry = chainRegistry,
|
||||
miningSimulationRepository = miningSimulationRepository
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package io.novafoundation.nova.feature_assets.presentation.balance.list.model
|
||||
|
||||
data class MiningSimulationModel(
|
||||
val diamondsText: String,
|
||||
val isActive: Boolean,
|
||||
)
|
||||
+2
@@ -1,10 +1,12 @@
|
||||
package io.novafoundation.nova.feature_assets.presentation.balance.list.model
|
||||
|
||||
import io.novafoundation.nova.feature_assets.presentation.citizenship.CitizenshipStatus
|
||||
import java.math.BigInteger
|
||||
|
||||
data class PezkuwiDashboardModel(
|
||||
val roles: List<String>,
|
||||
val trustScore: String,
|
||||
val trustScoreRaw: BigInteger,
|
||||
val welatiCount: String,
|
||||
val citizenshipStatus: CitizenshipStatus = CitizenshipStatus.NOT_STARTED,
|
||||
val isTrackingScore: Boolean = false
|
||||
|
||||
+20
@@ -14,6 +14,7 @@ import io.novafoundation.nova.common.utils.inflater
|
||||
import io.novafoundation.nova.common.utils.recyclerView.WithViewType
|
||||
import io.novafoundation.nova.feature_assets.R
|
||||
import io.novafoundation.nova.feature_assets.databinding.ItemPezkuwiDashboardBinding
|
||||
import io.novafoundation.nova.feature_assets.presentation.balance.list.model.MiningSimulationModel
|
||||
import io.novafoundation.nova.feature_assets.presentation.balance.list.model.PezkuwiDashboardModel
|
||||
import io.novafoundation.nova.feature_assets.presentation.citizenship.CitizenshipStatus
|
||||
|
||||
@@ -26,10 +27,13 @@ class PezkuwiDashboardAdapter(
|
||||
fun onSignClicked()
|
||||
fun onShareReferralClicked()
|
||||
fun onStartTrackingClicked()
|
||||
fun onMiningSquareClicked()
|
||||
fun onMiningInfoClicked()
|
||||
}
|
||||
|
||||
private var model: PezkuwiDashboardModel? = null
|
||||
private var trackingLoading: Boolean = false
|
||||
private var miningModel: MiningSimulationModel? = null
|
||||
|
||||
// Survives ViewHolder recycling (scroll) within the process, but not process restart —
|
||||
// resets to collapsed (false) whenever the app is freshly opened, by design.
|
||||
@@ -45,6 +49,11 @@ class PezkuwiDashboardAdapter(
|
||||
notifyChangedIfShown()
|
||||
}
|
||||
|
||||
fun setMiningModel(model: MiningSimulationModel) {
|
||||
this.miningModel = model
|
||||
notifyChangedIfShown()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PezkuwiDashboardHolder {
|
||||
val binding = ItemPezkuwiDashboardBinding.inflate(parent.inflater(), parent, false)
|
||||
return PezkuwiDashboardHolder(binding, handler) { expanded -> isExpanded = expanded }
|
||||
@@ -52,6 +61,7 @@ class PezkuwiDashboardAdapter(
|
||||
|
||||
override fun onBindViewHolder(holder: PezkuwiDashboardHolder, position: Int) {
|
||||
model?.let { holder.bind(it, trackingLoading, isExpanded) }
|
||||
miningModel?.let { holder.bindMining(it) }
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
@@ -77,6 +87,9 @@ class PezkuwiDashboardHolder(
|
||||
|
||||
binder.pezkuwiDashboardCollapsedBar.setOnClickListener { setExpanded(true) }
|
||||
binder.pezkuwiDashboardCollapseButton.setOnClickListener { setExpanded(false) }
|
||||
|
||||
binder.pezkuwiDashboardMiningSquare.setOnClickListener { handler.onMiningSquareClicked() }
|
||||
binder.pezkuwiDashboardMiningInfoButton.setOnClickListener { handler.onMiningInfoClicked() }
|
||||
}
|
||||
|
||||
private fun setExpanded(expanded: Boolean) {
|
||||
@@ -86,6 +99,13 @@ class PezkuwiDashboardHolder(
|
||||
onExpandedChanged(expanded)
|
||||
}
|
||||
|
||||
fun bindMining(model: MiningSimulationModel) {
|
||||
binder.pezkuwiDashboardMiningCount.text = model.diamondsText
|
||||
|
||||
val color = if (model.isActive) Color.parseColor("#FDB813") else Color.parseColor("#E2231A")
|
||||
binder.pezkuwiDashboardMiningSquare.background.mutate().setTint(color)
|
||||
}
|
||||
|
||||
fun bind(model: PezkuwiDashboardModel, trackingLoading: Boolean = false, isExpanded: Boolean = false) {
|
||||
bindRoles(model.roles)
|
||||
binder.pezkuwiDashboardTrustValue.text = model.trustScore
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Mining Simulation square - tinted programmatically (red = inactive, gold = actively mining). -->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@android:color/white" />
|
||||
|
||||
<corners android:radius="12dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="#29A9EA"
|
||||
android:pathData="M12,2 C6.48,2 2,6.48 2,12 C2,17.52 6.48,22 12,22 C17.52,22 22,17.52 22,12 C22,6.48 17.52,2 12,2 Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M17.3,7.15 L15.4,17.05 C15.25,17.7 14.85,17.85 14.3,17.55 L11.4,15.4 L10,16.75 C9.85,16.9 9.7,17.05 9.4,17.05 L9.6,13.9 L15.05,9 C15.3,8.8 15,8.65 14.7,8.85 L7.95,13.2 L5.1,12.3 C4.45,12.1 4.45,11.65 5.25,11.35 L16.5,7 C17.05,6.8 17.5,7.15 17.3,7.15 Z" />
|
||||
</vector>
|
||||
@@ -130,6 +130,63 @@
|
||||
android:text="@string/pezkuwi_dashboard_kurds_title"
|
||||
android:textColor="#7AFFFFFF"
|
||||
android:textSize="10sp" />
|
||||
|
||||
<!-- Mining Simulation: cosmetic, client-side-only preview of a trust-score-weighted
|
||||
emission share (see MiningSimulationFormula) - fills the empty space this
|
||||
right-side column otherwise leaves below the citizen-count stat. -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="10dp"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/pezkuwi_dashboard_mining_simulation_title"
|
||||
android:textColor="#7AFFFFFF"
|
||||
android:textSize="10sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pezkuwiDashboardMiningInfoButton"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="4dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:src="@drawable/ic_telegram" />
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/pezkuwiDashboardMiningSquare"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:background="@drawable/bg_pezkuwi_mining_square"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pezkuwiDashboardMiningCount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:paddingHorizontal="4dp"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
|
||||
Reference in New Issue
Block a user