Updated on 2026-08-14
This commit is contained in:
commit
9b3f6994a9
12 changed files with 228 additions and 16 deletions
|
|
@ -6,7 +6,6 @@ import com.google.zxing.BarcodeFormat
|
|||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.text.DecimalFormat
|
||||
|
|
@ -34,16 +33,16 @@ fun String.toQrCode(): Bitmap {
|
|||
return bmp
|
||||
}
|
||||
|
||||
fun BigDecimal.toFormattedString(blockchain: Blockchain): String {
|
||||
fun BigDecimal.toFormattedString(decimals: Int): String {
|
||||
val symbols = DecimalFormatSymbols(Locale.US)
|
||||
symbols.decimalSeparator = '.'
|
||||
val df = DecimalFormat()
|
||||
df.decimalFormatSymbols = symbols
|
||||
df.maximumFractionDigits = blockchain.decimals()
|
||||
df.maximumFractionDigits = decimals
|
||||
df.minimumFractionDigits = 0
|
||||
df.isGroupingUsed = false
|
||||
val bd = BigDecimal(unscaledValue(), scale())
|
||||
bd.setScale(blockchain.decimals(), BigDecimal.ROUND_DOWN)
|
||||
bd.setScale(decimals, BigDecimal.ROUND_DOWN)
|
||||
return df.format(bd)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ fun Fragment.getDrawable(@DrawableRes drawableResId: Int): Drawable? {
|
|||
return ContextCompat.getDrawable(requireContext(), drawableResId)
|
||||
}
|
||||
|
||||
fun Context.getDrawableCompat(@DrawableRes drawableResId: Int): Drawable? {
|
||||
return ContextCompat.getDrawable(this, drawableResId)
|
||||
}
|
||||
|
||||
fun View.show(show: Boolean) {
|
||||
if (show) this.visibility = View.VISIBLE else this.visibility = View.GONE
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.tap.features.wallet.models
|
||||
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
|
||||
data class PendingTransaction(
|
||||
val address: String,
|
||||
val amount: String,
|
||||
val currency: String,
|
||||
val type: PendingTransactionType
|
||||
)
|
||||
|
||||
enum class PendingTransactionType { Incoming, Outcoming }
|
||||
|
||||
fun TransactionData.toPendingTransaction(walletAddress: String): PendingTransaction {
|
||||
val type: PendingTransactionType = if (this.sourceAddress == walletAddress) {
|
||||
PendingTransactionType.Outcoming
|
||||
} else {
|
||||
PendingTransactionType.Incoming
|
||||
}
|
||||
val address = if (this.sourceAddress == walletAddress) {
|
||||
this.destinationAddress
|
||||
} else {
|
||||
this.sourceAddress
|
||||
}
|
||||
return PendingTransaction(
|
||||
address,
|
||||
this.amount.value?.toFormattedString(amount.decimals) ?: "?",
|
||||
this.amount.currencySymbol,
|
||||
type
|
||||
)
|
||||
}
|
||||
|
||||
fun List<TransactionData>.toPendingTransactions(walletAddress: String): List<PendingTransaction>{
|
||||
return this.map { it.toPendingTransaction(walletAddress) }
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.tap.common.extensions.toFiatString
|
|||
import com.tangem.tap.common.extensions.toFormattedString
|
||||
import com.tangem.tap.common.extensions.toQrCode
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.wallet.models.toPendingTransactions
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.features.wallet.ui.TokenData
|
||||
|
|
@ -37,7 +38,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
} else {
|
||||
AddressData(wallet.address, wallet.shareUrl, wallet.exploreUrl)
|
||||
}
|
||||
val currentArtworkId = state.globalState.scanNoteResponse?.verifyResponse?.artworkInfo?.id
|
||||
val currentArtworkId = state.globalState.scanNoteResponse?.verifyResponse?.artworkInfo?.id
|
||||
val cardImage = if (newState.cardImage?.artworkId == currentArtworkId) {
|
||||
newState.cardImage
|
||||
} else {
|
||||
|
|
@ -61,7 +62,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
val tokenFiatRate = state.globalState.fiatRates.getRateForCryptoCurrency(token.currencySymbol)
|
||||
val tokenFiatAmount = tokenFiatRate?.let { token.value?.toFiatString(it) }
|
||||
TokenData(
|
||||
token.value?.toFormattedString(action.wallet.blockchain) ?: "",
|
||||
token.value?.toFormattedString(token.decimals) ?: "",
|
||||
token.currencySymbol, tokenFiatAmount)
|
||||
} else {
|
||||
null
|
||||
|
|
@ -70,16 +71,20 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
val fiatRate = state.globalState.fiatRates.getRateForCryptoCurrency(action.wallet.blockchain.currency)
|
||||
val fiatAmount = fiatRate?.let { amount?.toFiatString(it) }
|
||||
|
||||
val pendingTransactions = action.wallet.transactions
|
||||
.toPendingTransactions(action.wallet.address)
|
||||
|
||||
val sendButtonEnabled = amount?.isZero() == false || token?.value?.isZero() == false
|
||||
newState = newState.copy(
|
||||
state = ProgressState.Done, wallet = action.wallet,
|
||||
currencyData = BalanceWidgetData(
|
||||
BalanceStatus.VerifiedOnline, action.wallet.blockchain.fullName,
|
||||
currencySymbol = action.wallet.blockchain.currency,
|
||||
amount?.toFormattedString(action.wallet.blockchain),
|
||||
amount?.toFormattedString(action.wallet.blockchain.decimals()),
|
||||
token = tokenData,
|
||||
fiatAmount = fiatAmount
|
||||
),
|
||||
pendingTransactions = pendingTransactions,
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled)
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.redux
|
|||
import android.graphics.Bitmap
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import org.rekotlin.StateType
|
||||
|
||||
|
|
@ -10,6 +11,7 @@ data class WalletState(
|
|||
val state: ProgressState = ProgressState.Done,
|
||||
val cardImage: Artwork? = null,
|
||||
val wallet: Wallet? = null,
|
||||
val pendingTransactions: List<PendingTransaction> = emptyList(),
|
||||
val addressData: AddressData? = null,
|
||||
val currencyData: BalanceWidgetData = BalanceWidgetData(),
|
||||
val payIdData: PayIdData = PayIdData(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.tap.features.wallet.ui
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.tangem.tap.common.extensions.getDrawableCompat
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.models.PendingTransactionType
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.android.synthetic.main.layout_pending_transaction.view.*
|
||||
|
||||
class PendingTransactionsAdapter
|
||||
: ListAdapter<PendingTransaction, PendingTransactionsAdapter.TransactionsViewHolder>(DiffUtilCallback) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionsViewHolder {
|
||||
val layout = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.layout_pending_transaction, parent, false)
|
||||
return TransactionsViewHolder(layout)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TransactionsViewHolder, position: Int) {
|
||||
holder.bind(currentList[position])
|
||||
}
|
||||
|
||||
object DiffUtilCallback : DiffUtil.ItemCallback<PendingTransaction>() {
|
||||
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: PendingTransaction, newItem: PendingTransaction
|
||||
) = oldItem == newItem
|
||||
|
||||
override fun areItemsTheSame(
|
||||
oldItem: PendingTransaction, newItem: PendingTransaction
|
||||
) = oldItem == newItem
|
||||
|
||||
}
|
||||
|
||||
|
||||
class TransactionsViewHolder(val view: View) :
|
||||
RecyclerView.ViewHolder(view) {
|
||||
|
||||
fun bind(transaction: PendingTransaction) {
|
||||
val transactionDescriptionRes = when (transaction.type) {
|
||||
PendingTransactionType.Incoming -> R.string.wallet_pending_transaction_incoming
|
||||
PendingTransactionType.Outcoming -> R.string.wallet_pending_transaction_outcoming
|
||||
}
|
||||
val image = when (transaction.type) {
|
||||
PendingTransactionType.Incoming -> R.drawable.ic_arrow_down
|
||||
PendingTransactionType.Outcoming -> R.drawable.ic_arrow_right
|
||||
}
|
||||
view.tv_pending_transaction.text = view.context.getString(
|
||||
transactionDescriptionRes, transaction.amount, transaction.currency
|
||||
)
|
||||
view.tv_pending_transaction_address.text = transaction.address
|
||||
view.iv_pending_transaction.setImageDrawable(view.context.getDrawableCompat(image))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import android.os.Bundle
|
|||
import android.view.View
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.tap.common.extensions.getDrawable
|
||||
import com.tangem.tap.common.extensions.hide
|
||||
|
|
@ -26,6 +27,9 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
private var qrDialog: QrDialog? = null
|
||||
private var payIdDialog: PayIdDialog? = null
|
||||
|
||||
private lateinit var viewAdapter: PendingTransactionsAdapter
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
|
|
@ -55,7 +59,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
||||
toolbar.setNavigationOnClickListener {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
|
|
@ -63,8 +66,17 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
btn_scan.setOnClickListener {
|
||||
store.dispatch(WalletAction.Scan)
|
||||
}
|
||||
setupTransactionsRecyclerView()
|
||||
}
|
||||
|
||||
|
||||
private fun setupTransactionsRecyclerView() {
|
||||
viewAdapter = PendingTransactionsAdapter()
|
||||
rv_pending_transaction.layoutManager = LinearLayoutManager(context)
|
||||
rv_pending_transaction.adapter = viewAdapter
|
||||
}
|
||||
|
||||
|
||||
override fun newState(state: WalletState) {
|
||||
if (activity == null) return
|
||||
|
||||
|
|
@ -76,6 +88,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
setupAddressCard(state)
|
||||
setupCardImage(state.cardImage?.artwork)
|
||||
|
||||
viewAdapter.submitList(state.pendingTransactions)
|
||||
|
||||
if (state.qrCode != null && state.addressData?.shareUrl != null) {
|
||||
if (qrDialog == null) qrDialog = QrDialog(requireContext())
|
||||
qrDialog?.showQr(state.qrCode, state.addressData.shareUrl)
|
||||
|
|
|
|||
14
app/src/main/res/drawable/ic_arrow_down.xml
Normal file
14
app/src/main/res/drawable/ic_arrow_down.xml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="14dp"
|
||||
android:height="14dp"
|
||||
android:viewportWidth="10"
|
||||
android:viewportHeight="12">
|
||||
<group
|
||||
android:scaleX="0.8"
|
||||
android:pivotX="3"
|
||||
>
|
||||
<path
|
||||
android:pathData="M5.853,11.6645L9.7814,8.075C10.0495,7.8439 10.0734,7.3871 9.84,7.1191C9.6067,6.8511 9.1615,6.824 8.9084,7.0724L6.0712,9.6665L6.0712,0.8398C6.0712,0.468 5.7781,0.1667 5.4165,0.1667C5.0549,0.1667 4.7618,0.468 4.7618,0.8398L4.7618,9.6665L1.9246,7.0724C1.6715,6.824 1.2271,6.8576 0.9937,7.1256C0.7604,7.3936 0.7835,7.8439 1.0516,8.075L4.98,11.6645C5.2976,11.8989 5.5827,11.8802 5.853,11.6645L5.853,11.6645Z"
|
||||
android:fillColor="#1C1C1E"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
|
@ -1,9 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="14dp"
|
||||
android:height="11dp"
|
||||
android:viewportWidth="14"
|
||||
android:viewportHeight="11">
|
||||
<path
|
||||
android:fillColor="#F9F9FA"
|
||||
android:pathData="M13.7974,4.9765L9.4899,0.2623C9.2126,-0.0594 8.6644,-0.088 8.3428,0.192C8.0212,0.472 7.9888,1.0063 8.2869,1.3099L11.3997,4.7146L0.8077,4.7146C0.3616,4.7146 0,5.0664 0,5.5003C0,5.9342 0.3616,6.2859 0.8077,6.2859L11.3997,6.2859L8.2869,9.6906C7.9888,9.9942 8.0291,10.5275 8.3507,10.8076C8.6723,11.0876 9.2126,11.0599 9.4899,10.7382L13.7974,6.0241C14.0786,5.6429 14.0562,5.3008 13.7974,4.9765L13.7974,4.9765Z" />
|
||||
android:height="14dp"
|
||||
android:viewportWidth="12"
|
||||
android:viewportHeight="10">
|
||||
<group
|
||||
android:scaleX="1"
|
||||
android:scaleY="0.8"
|
||||
android:pivotY="7"
|
||||
>
|
||||
<path
|
||||
android:pathData="M11.6645,4.147L8.075,0.2186C7.8439,-0.0495 7.3871,-0.0734 7.1191,0.16C6.8511,0.3933 6.824,0.8385 7.0724,1.0916L9.6665,3.9288L0.8398,3.9288C0.468,3.9288 0.1667,4.2219 0.1667,4.5835C0.1667,4.9451 0.468,5.2382 0.8398,5.2382L9.6664,5.2382L7.0724,8.0754C6.824,8.3285 6.8576,8.7729 7.1256,9.0063C7.3936,9.2396 7.8439,9.2165 8.075,8.9485L11.6645,5.02C11.8989,4.7024 11.8802,4.4173 11.6645,4.147L11.6645,4.147Z"
|
||||
android:fillColor="#1C1C1E"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
|
|
|
|||
|
|
@ -34,9 +34,12 @@
|
|||
android:id="@+id/sv_wallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
android:fillViewport="true"
|
||||
android:overScrollMode="never"
|
||||
>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/cl_wallet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="33dp">
|
||||
|
|
@ -54,12 +57,21 @@
|
|||
android:src="@drawable/card_default"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:layout_marginTop="12dp"
|
||||
android:id="@+id/rv_pending_transaction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:nestedScrollingEnabled="false"
|
||||
android:overScrollMode="never"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_card"/>
|
||||
|
||||
<include
|
||||
android:id="@+id/l_card_balance"
|
||||
layout="@layout/card_balance"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_card" />
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
|
||||
|
||||
|
||||
<include
|
||||
|
|
|
|||
55
app/src/main/res/layout/layout_pending_transaction.xml
Normal file
55
app/src/main/res/layout/layout_pending_transaction.xml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/fl_pending_transaction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card_pending_transaction"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="36dp"
|
||||
android:background="@android:color/white"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:elevation="3dp">
|
||||
|
||||
<LinearLayout
|
||||
android:gravity="center_vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_pending_transaction"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_width="11dp"
|
||||
android:layout_height="11dp"
|
||||
android:src="@drawable/ic_arrow_right"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_pending_transaction"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="Receiving 0.001 BTS from "
|
||||
android:textSize="12sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_pending_transaction_address"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:textSize="12sp"
|
||||
android:paddingStart="2dp"
|
||||
tools:text="0x4313bcd3425235bda1232141"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</FrameLayout>
|
||||
|
|
@ -25,6 +25,8 @@
|
|||
<string name="wallet_no_account">Account is not created</string>
|
||||
<string name="wallet_no_account_description">Load %1s+ %2s to create account</string>
|
||||
<string name="wallet_tokens">Tokens</string>
|
||||
<string name="wallet_pending_transaction_incoming">Receiving %1s %2s from </string>
|
||||
<string name="wallet_pending_transaction_outcoming">Sending %1s %2s to </string>
|
||||
|
||||
<string name="wallet_dialog_pay_id_button">Create PayID</string>
|
||||
<string name="wallet_create_payid_empty">Enter desired PayID first</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue