You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 

158 lines
6.2 KiB

package cash.z.ecc.android.ui.home
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import cash.z.ecc.android.R
import cash.z.ecc.android.ZcashWalletApp
import cash.z.ecc.android.databinding.FragmentBalanceDetailBinding
import cash.z.ecc.android.ext.goneIf
import cash.z.ecc.android.ext.onClickNavBack
import cash.z.ecc.android.ext.toAppColor
import cash.z.ecc.android.ext.toSplitColorSpan
import cash.z.ecc.android.feedback.Report.Tap.RECEIVE_BACK
import cash.z.ecc.android.sdk.ext.convertZatoshiToZecString
import cash.z.ecc.android.sdk.model.BlockHeight
import cash.z.ecc.android.ui.base.BaseFragment
import cash.z.ecc.android.ui.home.BalanceDetailViewModel.StatusModel
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
class BalanceDetailFragment : BaseFragment<FragmentBalanceDetailBinding>() {
private val viewModel: BalanceDetailViewModel by viewModels()
private var lastSignal: BlockHeight? = null
override fun inflate(inflater: LayoutInflater): FragmentBalanceDetailBinding =
FragmentBalanceDetailBinding.inflate(inflater)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.balances.onEach { onBalanceUpdated(it) }.launchIn(this)
viewModel.statuses.onEach { onStatusUpdated(it) }.launchIn(this)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.hitAreaExit.onClickNavBack() { tapped(RECEIVE_BACK) }
binding.textShieldedHushTitle.text = "SHIELDED ${getString(R.string.symbol)}"
}
private fun onBalanceUpdated(balanceModel: BalanceDetailViewModel.BalanceModel) {
balanceModel.apply {
if (balanceModel.hasData()) {
setBalances(paddedShielded, paddedTransparent, paddedTotal)
} else {
setBalances(" --", " --", " --")
}
}
}
private fun onStatusUpdated(status: StatusModel) {
binding.textStatus.text = status.toStatus()
if (status.missingBlocks > 100) {
binding.textBlockHeightPrefix.text = "Processing "
binding.textBlockHeight.text = String.format(
"%,d",
status.info.lastScannedHeight?.value ?: 0
) + " of " + String.format("%,d", status.info.networkBlockHeight?.value ?: 0)
} else {
status.info.lastScannedHeight.let { height ->
if (height == null) {
binding.textBlockHeightPrefix.text = "Processing..."
binding.textBlockHeight.text = ""
} else {
binding.textBlockHeightPrefix.text = "Balances as of block "
binding.textBlockHeight.text =
String.format("%,d", status.info.lastScannedHeight?.value ?: 0)
sendNewBlockSignal(status.info.lastScannedHeight)
}
}
}
}
private fun sendNewBlockSignal(currentHeight: BlockHeight?) {
// prevent a flood of signals while scanning blocks
if (lastSignal != null && (currentHeight?.value ?: 0) > lastSignal!!.value) {
mainActivity?.vibrate(0, 100, 100, 300)
Toast.makeText(mainActivity, "New block!", Toast.LENGTH_SHORT).show()
}
lastSignal = currentHeight
}
fun setBalances(shielded: String, transparent: String, total: String) {
binding.textShieldAmount.text = shielded.colorize()
}
private fun String.colorize(): CharSequence {
val dotIndex = indexOf('.')
return if (dotIndex < 0 || length < (dotIndex + 4)) {
this
} else {
toSplitColorSpan(R.color.text_light, R.color.zcashWhite_24, indexOf('.') + 4)
}
}
private fun StatusModel.toStatus(): String {
fun String.plural(count: Int) = if (count > 1) "${this}s" else this
if (viewModel.latestBalance?.hasData() == false) {
return "Balance info is not yet available"
}
var status = ""
if (hasUnmined) {
val count = pendingUnmined.count()
status += "Balance excludes $count unconfirmed ${"transaction".plural(count)}. "
}
status += when {
hasPendingTransparentBalance && hasPendingShieldedBalance -> {
"Awaiting ${pendingShieldedBalance.convertZatoshiToZecString(8)} ${
ZcashWalletApp.instance.getString(
R.string.symbol
)
} in shielded funds and {pendingTransparentBalance.convertZatoshiToZecString(8)} ${
ZcashWalletApp.instance.getString(
R.string.symbol
)
} in transparent funds"
}
hasPendingShieldedBalance -> {
"Awaiting ${pendingShieldedBalance.convertZatoshiToZecString(8)} ${
ZcashWalletApp.instance.getString(
R.string.symbol
)
} in shielded funds"
}
hasPendingTransparentBalance -> {
"Awaiting ${pendingTransparentBalance.convertZatoshiToZecString(8)} ${
ZcashWalletApp.instance.getString(
R.string.symbol
)
} in transparent funds"
}
else -> ""
}
pendingUnconfirmed.count().takeUnless { it == 0 }?.let { count ->
if (status.contains("Awaiting")) status += " and "
status += "$count outbound ${"transaction".plural(count)}"
remainingConfirmations().firstOrNull()?.let { remaining ->
status += " with $remaining ${"confirmation".plural(remaining.toInt())} remaining"
}
}
return if (status.isEmpty()) "All funds are available!" else status
}
}