Skip to content

Commit

Permalink
Ledger/not supported (#409)
Browse files Browse the repository at this point in the history
* Leger not supported bottom sheets

* Fixes

* Code style

* Mova ledger warning to receive button

* remove todo
  • Loading branch information
valentunn authored Sep 2, 2022
1 parent 74b1417 commit f4497cd
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package io.novafoundation.nova.common.mixin.actionAwaitable
import androidx.lifecycle.MutableLiveData
import io.novafoundation.nova.common.utils.Event
import io.novafoundation.nova.common.utils.event
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume

Expand All @@ -18,14 +17,13 @@ internal class ActionAwaitableProvider<P, R> : ActionAwaitableMixin.Presentation

override val awaitableActionLiveData = MutableLiveData<Event<ActionAwaitableMixin.Action<P, R>>>()

@OptIn(ExperimentalCoroutinesApi::class)
override suspend fun awaitAction(payload: P): R = suspendCancellableCoroutine { continuation ->
val action = ActionAwaitableMixin.Action<P, R>(
payload = payload,
onSuccess = { continuation.resume(it) },
onCancel = { continuation.cancel() }
)

awaitableActionLiveData.value = action.event()
awaitableActionLiveData.postValue(action.event())
}
}
5 changes: 5 additions & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="ledger_sign_raw_not_supported">Ledger does not support signing arbitrary messages — only transactions</string>

<string name="assets_receive_ledger_not_supported_message">Do not transfer %s to the Ledger-controlled account since Ledger does not support sending of %s, so assets will be stuck on this account</string>
<string name="assets_receive_ledger_not_supported_title">Ledger does not support this token</string>

<string name="common_signature_invalid">Signature is invalid</string>
<string name="ledger_sign_signature_invalid_message">Please make sure you have selected the right Ledger device for currently approving operation</string>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@ package io.novafoundation.nova.feature_account_impl.data.signer.ledger
import io.novafoundation.nova.common.base.errors.SigningCancelledException
import io.novafoundation.nova.common.utils.MutableSharedState
import io.novafoundation.nova.feature_account_api.presenatation.sign.SignInterScreenRequester
import io.novafoundation.nova.feature_account_impl.R
import io.novafoundation.nova.feature_account_impl.data.signer.SeparateFlowSigner
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.notSupported.ParitySignerSigningNotSupportedPresentable
import io.novafoundation.nova.feature_account_impl.presentation.common.sign.notSupported.SigningNotSupportedPresentable
import jp.co.soramitsu.fearless_utils.encrypt.SignatureWrapper
import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.SignerPayloadExtrinsic
import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.SignerPayloadRaw

class LedgerSigner(
signingSharedState: MutableSharedState<SignerPayloadExtrinsic>,
signFlowRequester: SignInterScreenRequester,
private val messageSigningNotSupported: ParitySignerSigningNotSupportedPresentable
private val messageSigningNotSupported: SigningNotSupportedPresentable
) : SeparateFlowSigner(signingSharedState, signFlowRequester) {

override suspend fun signRaw(payload: SignerPayloadRaw): SignatureWrapper {
messageSigningNotSupported.presentSigningNotSupported()
messageSigningNotSupported.presentSigningNotSupported(
SigningNotSupportedPresentable.Payload(
iconRes = R.drawable.ic_ledger,
messageRes = R.string.ledger_sign_raw_not_supported
)
)

throw SigningCancelledException()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,26 @@ package io.novafoundation.nova.feature_account_impl.data.signer.paritySigner
import io.novafoundation.nova.common.base.errors.SigningCancelledException
import io.novafoundation.nova.common.utils.MutableSharedState
import io.novafoundation.nova.feature_account_api.presenatation.sign.SignInterScreenRequester
import io.novafoundation.nova.feature_account_impl.R
import io.novafoundation.nova.feature_account_impl.data.signer.SeparateFlowSigner
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.notSupported.ParitySignerSigningNotSupportedPresentable
import io.novafoundation.nova.feature_account_impl.presentation.common.sign.notSupported.SigningNotSupportedPresentable
import jp.co.soramitsu.fearless_utils.encrypt.SignatureWrapper
import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.SignerPayloadExtrinsic
import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.SignerPayloadRaw

class ParitySignerSigner(
signingSharedState: MutableSharedState<SignerPayloadExtrinsic>,
signFlowRequester: SignInterScreenRequester,
private val messageSigningNotSupported: ParitySignerSigningNotSupportedPresentable
private val messageSigningNotSupported: SigningNotSupportedPresentable
) : SeparateFlowSigner(signingSharedState, signFlowRequester) {

override suspend fun signRaw(payload: SignerPayloadRaw): SignatureWrapper {
messageSigningNotSupported.presentSigningNotSupported()
messageSigningNotSupported.presentSigningNotSupported(
SigningNotSupportedPresentable.Payload(
iconRes = R.drawable.ic_parity_signer,
messageRes = R.string.account_parity_signer_not_supported_subtitle
)
)

throw SigningCancelledException()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import io.novafoundation.nova.feature_account_impl.data.repository.RealParitySig
import io.novafoundation.nova.feature_account_impl.data.signer.paritySigner.ParitySignerSignCommunicator
import io.novafoundation.nova.feature_account_impl.presentation.AccountRouter
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.common.QrCodeExpiredPresentableFactory
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.notSupported.ParitySignerSigningNotSupportedPresentable
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.notSupported.RealParitySignerSigningNotSupportedPresentable
import io.novafoundation.nova.feature_account_impl.presentation.common.sign.notSupported.SigningNotSupportedPresentable
import io.novafoundation.nova.feature_account_impl.presentation.common.sign.notSupported.RealSigningNotSupportedPresentable
import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.SignerPayloadExtrinsic

@Module
Expand Down Expand Up @@ -46,5 +46,5 @@ class ParitySignerModule {
@FeatureScope
fun provideSigningNotSupportedPresentable(
contextManager: ContextManager
): ParitySignerSigningNotSupportedPresentable = RealParitySignerSigningNotSupportedPresentable(contextManager)
): SigningNotSupportedPresentable = RealSigningNotSupportedPresentable(contextManager)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import io.novafoundation.nova.feature_account_impl.data.signer.paritySigner.Pari
import io.novafoundation.nova.feature_account_impl.data.signer.paritySigner.ParitySignerSigner
import io.novafoundation.nova.feature_account_impl.data.signer.secrets.SecretsSignerFactory
import io.novafoundation.nova.feature_account_impl.data.signer.watchOnly.WatchOnlySigner
import io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.notSupported.ParitySignerSigningNotSupportedPresentable
import io.novafoundation.nova.feature_account_impl.presentation.common.sign.notSupported.SigningNotSupportedPresentable
import jp.co.soramitsu.fearless_utils.runtime.extrinsic.signer.SignerPayloadExtrinsic

@Module
Expand All @@ -40,16 +40,15 @@ class SignersModule {
fun provideParitySignerSigner(
signingSharedState: MutableSharedState<SignerPayloadExtrinsic>,
communicator: ParitySignerSignCommunicator,
signingNotSupportedPresentable: ParitySignerSigningNotSupportedPresentable
signingNotSupportedPresentable: SigningNotSupportedPresentable
) = ParitySignerSigner(signingSharedState, communicator, signingNotSupportedPresentable)

@Provides
@FeatureScope
fun provideLedgerSigner(
signingSharedState: MutableSharedState<SignerPayloadExtrinsic>,
communicator: LedgerSignCommunicator,
// TODO customize for ledger
signingNotSupportedPresentable: ParitySignerSigningNotSupportedPresentable
signingNotSupportedPresentable: SigningNotSupportedPresentable
) = LedgerSigner(signingSharedState, communicator, signingNotSupportedPresentable)

@Provides
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.novafoundation.nova.feature_account_impl.presentation.paritySigner.sign.notSupported
package io.novafoundation.nova.feature_account_impl.presentation.common.sign.notSupported

import android.content.Context
import android.content.DialogInterface
Expand All @@ -9,6 +9,7 @@ import io.novafoundation.nova.feature_account_impl.R

class AcknowledgeSigningNotSupportedBottomSheet(
context: Context,
private val payload: SigningNotSupportedPresentable.Payload,
private val onConfirm: () -> Unit
) : ActionNotAllowedBottomSheet(
context = context,
Expand All @@ -23,8 +24,8 @@ class AcknowledgeSigningNotSupportedBottomSheet(
super.onCreate(savedInstanceState)

title.setText(R.string.account_parity_signer_not_supported_title)
subtitle.setText(R.string.account_parity_signer_not_supported_subtitle)
subtitle.setText(payload.messageRes)

applySolidIconStyle(R.drawable.ic_parity_signer)
applySolidIconStyle(payload.iconRes)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.novafoundation.nova.feature_account_impl.presentation.common.sign.notSupported

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import io.novafoundation.nova.common.resources.ContextManager
import io.novafoundation.nova.feature_account_impl.presentation.common.sign.notSupported.SigningNotSupportedPresentable.Payload
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

interface SigningNotSupportedPresentable {

class Payload(
@DrawableRes val iconRes: Int,
@StringRes val messageRes: Int
)

suspend fun presentSigningNotSupported(payload: Payload)
}

class RealSigningNotSupportedPresentable(
private val contextManager: ContextManager,
) : SigningNotSupportedPresentable {

override suspend fun presentSigningNotSupported(payload: Payload): Unit = withContext(Dispatchers.Main) {
suspendCoroutine {
AcknowledgeSigningNotSupportedBottomSheet(
context = contextManager.getActivity()!!,
onConfirm = { it.resume(Unit) },
payload = payload
).show()
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import io.novafoundation.nova.feature_assets.di.AssetsFeatureComponent
import io.novafoundation.nova.feature_assets.presentation.AssetPayload
import io.novafoundation.nova.feature_assets.presentation.balance.assetActions.buy.setupBuyIntegration
import io.novafoundation.nova.feature_assets.presentation.model.BalanceLocksModel
import io.novafoundation.nova.feature_assets.presentation.receive.view.LedgerNotSupportedWarningBottomSheet
import io.novafoundation.nova.feature_assets.presentation.transaction.history.showState
import io.novafoundation.nova.feature_wallet_api.presentation.view.showAmount
import kotlinx.android.synthetic.main.fragment_balance_detail.balanceDetaiActions
Expand Down Expand Up @@ -135,6 +136,14 @@ class BalanceDetailFragment : BaseFragment<BalanceDetailViewModel>() {
viewModel.showLockedDetailsEvent.observeEvent(::showLockedDetails)

viewModel.sendEnabled.observe(balanceDetaiActions.send::setEnabled)

viewModel.acknowledgeLedgerWarning.awaitableActionLiveData.observeEvent {
LedgerNotSupportedWarningBottomSheet(
context = requireContext(),
onSuccess = { it.onSuccess(Unit) },
message = it.payload
).show()
}
}

private fun setRefreshEnabled(bottomSheetState: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package io.novafoundation.nova.feature_assets.presentation.balance.detail
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import io.novafoundation.nova.common.base.BaseViewModel
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
import io.novafoundation.nova.common.mixin.actionAwaitable.confirmingAction
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.Event
import io.novafoundation.nova.common.utils.inBackground
Expand All @@ -24,14 +26,18 @@ import io.novafoundation.nova.feature_currency_api.domain.CurrencyInteractor
import io.novafoundation.nova.feature_wallet_api.domain.model.Asset
import io.novafoundation.nova.feature_wallet_api.domain.model.BalanceLocks
import io.novafoundation.nova.feature_wallet_api.presentation.model.mapAmountToAmountModel
import java.util.Locale
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain
import io.novafoundation.nova.runtime.multiNetwork.chain.model.Chain.Asset.Type.Orml
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.util.Locale

private typealias LedgerWarningMessage = String

class BalanceDetailViewModel(
private val walletInteractor: WalletInteractor,
Expand All @@ -44,10 +50,13 @@ class BalanceDetailViewModel(
private val accountUseCase: SelectedAccountUseCase,
private val missingKeysPresenter: WatchOnlyMissingKeysPresenter,
private val resourceManager: ResourceManager,
private val currencyInteractor: CurrencyInteractor
private val currencyInteractor: CurrencyInteractor,
private val actionAwaitableMixinFactory: ActionAwaitableMixin.Factory,
) : BaseViewModel(),
TransactionHistoryUi by transactionHistoryMixin {

val acknowledgeLedgerWarning = actionAwaitableMixinFactory.confirmingAction<LedgerWarningMessage>()

private val _hideRefreshEvent = MutableLiveData<Event<Unit>>()
val hideRefreshEvent: LiveData<Event<Unit>> = _hideRefreshEvent

Expand Down Expand Up @@ -119,11 +128,11 @@ class BalanceDetailViewModel(
router.openSend(assetPayload)
}

fun receiveClicked() = requireSecretsWallet {
fun receiveClicked() = checkControllableAsset {
router.openReceive(assetPayload)
}

fun buyClicked() = requireSecretsWallet {
fun buyClicked() = checkControllableAsset {
buyMixin.buyClicked()
}

Expand All @@ -132,13 +141,15 @@ class BalanceDetailViewModel(
_showLockedDetailsEvent.value = Event(balanceLocks)
}

private fun requireSecretsWallet(action: () -> Unit) {
private fun checkControllableAsset(action: () -> Unit) {
launch {
val metaAccount = accountUseCase.getSelectedMetaAccount()
val chainAsset = assetFlow.first().token.configuration

when (metaAccount.type) {
Type.SECRETS, Type.PARITY_SIGNER, Type.LEDGER -> action()
Type.WATCH_ONLY -> missingKeysPresenter.presentNoKeysFound()
when {
metaAccount.type == Type.LEDGER && chainAsset.type is Orml -> showLedgerAssetNotSupportedWarning(chainAsset)
metaAccount.type == Type.WATCH_ONLY -> missingKeysPresenter.presentNoKeysFound()
else -> action()
}
}
}
Expand All @@ -152,7 +163,6 @@ class BalanceDetailViewModel(
)
}

@OptIn(ExperimentalStdlibApi::class)
private fun mapBalanceLocksToUi(balanceLocks: BalanceLocks?, asset: Asset): BalanceLocksModel {
val mappedLocks = balanceLocks?.locks?.map {
BalanceLocksModel.Lock(
Expand Down Expand Up @@ -180,7 +190,14 @@ class BalanceDetailViewModel(
"democrac" -> resourceManager.getString(R.string.assets_balance_details_locks_democrac)
"vesting" -> resourceManager.getString(R.string.assets_balance_details_locks_vesting)
"phrelect" -> resourceManager.getString(R.string.assets_balance_details_locks_phrelect)
else -> id.capitalize(Locale.getDefault())
else -> id.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
}
}

private fun showLedgerAssetNotSupportedWarning(chainAsset: Chain.Asset) = launch {
val assetSymbol = chainAsset.symbol
val warningMessage = resourceManager.getString(R.string.assets_receive_ledger_not_supported_message, assetSymbol, assetSymbol)

acknowledgeLedgerWarning.awaitAction(warningMessage)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import dagger.multibindings.IntoMap
import io.novafoundation.nova.common.di.scope.ScreenScope
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
import io.novafoundation.nova.common.mixin.actionAwaitable.ActionAwaitableMixin
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.core.updater.UpdateSystem
import io.novafoundation.nova.feature_account_api.domain.interfaces.AccountRepository
Expand Down Expand Up @@ -100,7 +101,8 @@ class BalanceDetailModule {
accountUseCase: SelectedAccountUseCase,
missingKeysPresenter: WatchOnlyMissingKeysPresenter,
resourceManager: ResourceManager,
currencyInteractor: CurrencyInteractor
currencyInteractor: CurrencyInteractor,
actionAwaitableMixinFactory: ActionAwaitableMixin.Factory
): ViewModel {
return BalanceDetailViewModel(
walletInteractor = walletInteractor,
Expand All @@ -113,7 +115,8 @@ class BalanceDetailModule {
accountUseCase = accountUseCase,
missingKeysPresenter = missingKeysPresenter,
resourceManager = resourceManager,
currencyInteractor
currencyInteractor = currencyInteractor,
actionAwaitableMixinFactory = actionAwaitableMixinFactory
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class ReceiveViewModel(
private val router: AssetsRouter,
) : BaseViewModel(), ExternalActions by externalActions {

private val selectedMetaAccountFlow = selectedAccountUseCase.selectedMetaAccountFlow()

private val chainWithAssetAsync by lazyAsync {
chainRegistry.chainWithAsset(assetPayload.chainId, assetPayload.chainAssetId)
}
Expand All @@ -54,7 +56,7 @@ class ReceiveViewModel(
.inBackground()
.share()

val receiver = selectedAccountUseCase.selectedMetaAccountFlow()
val receiver = selectedMetaAccountFlow
.map {
val (chain, chainAsset) = chainWithAssetAsync()
val address = it.addressIn(chain)!!
Expand Down
Loading

0 comments on commit f4497cd

Please sign in to comment.