Updated on 2026-08-14

This commit is contained in:
Tangem 2020-09-11 20:26:35 +03:00
parent f1b5b1ebc1
commit 14c6876cc9
14 changed files with 291 additions and 5 deletions

View file

@ -4,6 +4,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.features.details.ui.DetailsFragment
import com.tangem.tap.features.home.HomeFragment
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.wallet.ui.WalletFragment
@ -36,5 +37,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.Home -> HomeFragment()
AppScreen.Wallet -> WalletFragment()
AppScreen.Send -> SendFragment()
AppScreen.Details -> DetailsFragment()
}
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.common.redux
import com.tangem.tap.common.redux.global.globalReducer
import com.tangem.tap.common.redux.navigation.NavigationReducer
import com.tangem.tap.features.details.redux.DetailsReducer
import com.tangem.tap.features.send.redux.SendReducer
import com.tangem.tap.features.wallet.redux.WalletReducer
import org.rekotlin.Action
@ -11,10 +12,11 @@ fun appReducer(action: Action, state: AppState?): AppState {
if (action is AppAction.RestoreState) return action.state
return AppState(
navigationState = NavigationReducer.reduce(action, state),
globalState = globalReducer(action, state),
walletState = WalletReducer.reduce(action, state),
sendState = SendReducer.reduce(action, state.sendState)
navigationState = NavigationReducer.reduce(action, state),
globalState = globalReducer(action, state),
walletState = WalletReducer.reduce(action, state),
sendState = SendReducer.reduce(action, state.sendState),
detailsState = DetailsReducer.reduce(action, state)
)
}

View file

@ -3,6 +3,7 @@ package com.tangem.tap.common.redux
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.redux.navigation.NavigationState
import com.tangem.tap.common.redux.navigation.navigationMiddleware
import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.features.home.redux.homeMiddleware
import com.tangem.tap.features.send.redux.SendState
import com.tangem.tap.features.send.redux.sendMiddleware
@ -16,6 +17,7 @@ data class AppState(
val globalState: GlobalState = GlobalState(),
val walletState: WalletState = WalletState(),
val sendState: SendState = SendState(),
val detailsState: DetailsState = DetailsState()
) : StateType {
companion object {

View file

@ -9,4 +9,4 @@ data class NavigationState(
val activity: WeakReference<FragmentActivity>? = null
) : StateType
enum class AppScreen { Home, Wallet, Send }
enum class AppScreen { Home, Wallet, Send, Details }

View file

@ -0,0 +1,10 @@
package com.tangem.tap.features.details.redux
import com.tangem.commands.Card
import org.rekotlin.Action
sealed class DetailsAction : Action {
data class SetCard(val card: Card): DetailsAction()
}

View file

@ -0,0 +1 @@
package com.tangem.tap.features.details.redux

View file

@ -0,0 +1,31 @@
package com.tangem.tap.features.details.redux
import com.tangem.commands.Card
import com.tangem.tap.common.redux.AppState
import org.rekotlin.Action
class DetailsReducer {
companion object {
fun reduce(action: Action, state: AppState): DetailsState = internalReduce(action, state)
}
}
private fun internalReduce(action: Action, state: AppState): DetailsState {
if (action !is DetailsAction) return state.detailsState
var detailsState = state.detailsState
when (action) {
is DetailsAction.SetCard -> {
detailsState = DetailsState(card = action.card, cardInfo = action.card.toCardInfo())
}
}
return detailsState
}
private fun Card.toCardInfo(): CardInfo? {
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
val issuer = this.cardData?.issuerName ?: return null
val signedHashes = this.walletSignedHashes ?: return null
return CardInfo(cardId, issuer, signedHashes)
}

View file

@ -0,0 +1,15 @@
package com.tangem.tap.features.details.redux
import com.tangem.commands.Card
import org.rekotlin.StateType
data class DetailsState(
val card: Card? = null,
val cardInfo: CardInfo? = null
) : StateType
data class CardInfo(
val cardId: String,
val issuer: String,
val signedHashes: Int
)

View file

@ -0,0 +1,67 @@
package com.tangem.tap.features.details.ui
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_details.*
import kotlinx.android.synthetic.main.fragment_wallet.toolbar
import org.rekotlin.StoreSubscriber
class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<DetailsState> {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
store.dispatch(NavigationAction.PopBackTo())
}
})
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(R.transition.slide_right)
exitTransition = inflater.inflateTransition(R.transition.fade)
}
override fun onStart() {
super.onStart()
store.subscribe(this) { state ->
state.skipRepeats { oldState, newState ->
oldState.detailsState == newState.detailsState
}.select { it.detailsState }
}
}
override fun onStop() {
super.onStop()
store.unsubscribe(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
}
}
override fun newState(state: DetailsState) {
if (activity == null) return
if (state.cardInfo != null) {
tv_card_id.text = state.cardInfo.cardId
tv_issuer.text = state.cardInfo.issuer
tv_signed_hashes.text = state.cardInfo.signedHashes.toString()
}
}
}

View file

@ -3,8 +3,12 @@ package com.tangem.tap.features.wallet.ui
import android.app.Dialog
import android.graphics.Bitmap
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionInflater
@ -12,7 +16,9 @@ import com.google.android.material.snackbar.Snackbar
import com.tangem.tap.common.extensions.getDrawable
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog
import com.tangem.tap.features.wallet.ui.dialogs.PayIdDialog
@ -35,6 +41,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
store.dispatch(NavigationAction.PopBackTo())
@ -61,6 +68,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
@ -211,4 +219,22 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.details_menu -> {
store.state.globalState.scanNoteResponse?.card?.let { card ->
store.dispatch(DetailsAction.SetCard(card))
store.dispatch(NavigationAction.NavigateTo(AppScreen.Details))
true
}
false
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.wallet, menu)
}
}

View file

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_wallet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundLightGray"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
style="@style/Widget.MaterialComponents.Toolbar.Surface"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/backgroundLightGray"
android:fitsSystemWindows="true"
app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
app:title="@string/details_toolbar_title" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/sv_wallet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="35dp"
android:fillViewport="true"
android:overScrollMode="never"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_wallet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="33dp">
<TextView
android:id="@+id/tv_card_id_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="14dp"
android:text="Card ID"
android:textColor="@color/darkGray6"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_card_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="14dp"
android:textColor="@color/darkGray1"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="0000 0000 0000 0000" />
<TextView
android:id="@+id/tv_issuer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="14dp"
android:text="Issuer"
android:textColor="@color/darkGray6"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_card_id_title" />
<TextView
android:id="@+id/tv_issuer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="14dp"
android:textColor="@color/darkGray1"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_card_id"
tools:text="Tangem" />
<TextView
android:id="@+id/tv_signed_hashes_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="14dp"
android:text="Signed"
android:textColor="@color/darkGray6"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_issuer_title" />
<TextView
android:id="@+id/tv_signed_hashes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="14dp"
android:textColor="@color/darkGray1"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_issuer"
tools:text="48 hashes" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View file

@ -21,6 +21,7 @@
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:navigationIcon="@drawable/ic_baseline_arrow_back_24"
app:menu="@menu/wallet"
app:title="@string/wallet_toolbar_title" />
</com.google.android.material.appbar.AppBarLayout>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/details_menu"
android:title="@string/details_toolbar_title"
app:showAsAction="never" />
</menu>

View file

@ -64,4 +64,7 @@
<string name="send_balance">Balance: %1s %2s</string>
<string name="send_set_maximum_amount">Maximum amount</string>
<string name="details_toolbar_title">Details</string>
</resources>