Business logic implementation
This commit is contained in:
parent
088b2c82a5
commit
c8ac10fcdc
|
@ -0,0 +1,23 @@
|
|||
package cash.z.ecc.sdk.model
|
||||
|
||||
import cash.z.ecc.android.sdk.model.WalletAddress
|
||||
|
||||
enum class AddressType {
|
||||
UNIFIED, TRANSPARENT, SAPLING, TEX;
|
||||
|
||||
suspend fun toWalletAddress(address: String) = when (this) {
|
||||
UNIFIED -> WalletAddress.Unified.new(address)
|
||||
TRANSPARENT -> WalletAddress.Transparent.new(address)
|
||||
SAPLING -> WalletAddress.Sapling.new(address)
|
||||
TEX -> WalletAddress.Tex.new(address)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun fromWalletAddress(walletAddress: WalletAddress) = when (walletAddress) {
|
||||
is WalletAddress.Sapling -> SAPLING
|
||||
is WalletAddress.Tex -> TEX
|
||||
is WalletAddress.Transparent -> TRANSPARENT
|
||||
is WalletAddress.Unified -> UNIFIED
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package co.electriccoin.zcash.ui.design.component
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
@ -21,7 +22,7 @@ import co.electriccoin.zcash.ui.design.util.stringRes
|
|||
@Composable
|
||||
fun ZashiBottomBar(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp),
|
||||
|
|
|
@ -38,9 +38,9 @@ class SendViewIntegrationTest {
|
|||
goToQrScanner = {},
|
||||
goBack = {},
|
||||
goBalances = {},
|
||||
goSettings = {},
|
||||
goPaymentRequest = { _, _ -> },
|
||||
goSendConfirmation = {},
|
||||
goReviewKeystoneTransaction = {},
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import org.junit.Test
|
|||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class SupportInfoTest {
|
||||
class SupportFinancialInfoStateTest {
|
||||
@Test
|
||||
fun filter_time() =
|
||||
runTest {
|
|
@ -8,8 +8,10 @@ import co.electriccoin.zcash.ui.common.usecase.DeriveKeystoneAccountUnifiedAddre
|
|||
import co.electriccoin.zcash.ui.common.usecase.GetAddressesUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetBackupPersistableWalletUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetContactByAddressUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetLoadedExchangeRateUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetPersistableWalletUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSelectedEndpointUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSelectedWalletAccountUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSpendingKeyUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSupportUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSynchronizerUseCase
|
||||
|
@ -100,4 +102,6 @@ val useCaseModule =
|
|||
factoryOf(::CreateKeystoneAccountUseCase)
|
||||
factoryOf(::DeriveKeystoneAccountUnifiedAddressUseCase)
|
||||
factoryOf(::DecodeUrToZashiAccountsUseCase)
|
||||
factoryOf(::GetLoadedExchangeRateUseCase)
|
||||
factoryOf(::GetSelectedWalletAccountUseCase)
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import co.electriccoin.zcash.ui.screen.receive.viewmodel.ReceiveViewModel
|
|||
import co.electriccoin.zcash.ui.screen.request.viewmodel.RequestViewModel
|
||||
import co.electriccoin.zcash.ui.screen.restore.viewmodel.RestoreViewModel
|
||||
import co.electriccoin.zcash.ui.screen.restoresuccess.viewmodel.RestoreSuccessViewModel
|
||||
import co.electriccoin.zcash.ui.screen.reviewtransaction.ReviewKeystoneTransaction
|
||||
import co.electriccoin.zcash.ui.screen.reviewtransaction.ReviewKeystoneTransactionViewModel
|
||||
import co.electriccoin.zcash.ui.screen.scan.ScanNavigationArgs
|
||||
import co.electriccoin.zcash.ui.screen.scan.viewmodel.ScanViewModel
|
||||
import co.electriccoin.zcash.ui.screen.scankeystone.viewmodel.ScanKeystoneSignInRequestViewModel
|
||||
|
@ -113,4 +115,11 @@ val viewModelModule =
|
|||
decodeUrToZashiAccounts = get()
|
||||
)
|
||||
}
|
||||
viewModel { (args: ReviewKeystoneTransaction) ->
|
||||
ReviewKeystoneTransactionViewModel(
|
||||
args = args,
|
||||
observeContactByAddress = get(),
|
||||
getLoadedExchangeRate = get(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,8 @@ import co.electriccoin.zcash.ui.screen.paymentrequest.model.PaymentRequestArgume
|
|||
import co.electriccoin.zcash.ui.screen.qrcode.WrapQrCode
|
||||
import co.electriccoin.zcash.ui.screen.receive.model.ReceiveAddressType
|
||||
import co.electriccoin.zcash.ui.screen.request.WrapRequest
|
||||
import co.electriccoin.zcash.ui.screen.reviewtransaction.AndroidReviewKeystoneTransaction
|
||||
import co.electriccoin.zcash.ui.screen.reviewtransaction.ReviewKeystoneTransaction
|
||||
import co.electriccoin.zcash.ui.screen.scan.ScanNavigationArgs
|
||||
import co.electriccoin.zcash.ui.screen.scan.WrapScanValidator
|
||||
import co.electriccoin.zcash.ui.screen.scankeystone.ScanKeystoneNavigationArgs
|
||||
|
@ -394,9 +396,11 @@ internal fun MainActivity.Navigation() {
|
|||
composable(ConnectKeystoneArgs.PATH) {
|
||||
AndroidConnectKeystone()
|
||||
}
|
||||
composable<SelectKeystoneAccount> { backStackEntry ->
|
||||
val args = backStackEntry.toRoute<SelectKeystoneAccount>()
|
||||
AndroidSelectKeystoneAccount(args)
|
||||
composable<SelectKeystoneAccount> {
|
||||
AndroidSelectKeystoneAccount(it.toRoute())
|
||||
}
|
||||
composable<ReviewKeystoneTransaction> {
|
||||
AndroidReviewKeystoneTransaction(it.toRoute())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -417,6 +421,9 @@ private fun MainActivity.NavigationHome(
|
|||
}
|
||||
navController.navigateJustOnce(SEND_CONFIRMATION)
|
||||
},
|
||||
goReviewKeystoneTransaction = {
|
||||
navController.navigate(it)
|
||||
},
|
||||
goPaymentRequest = { zecSend, zip321Uri ->
|
||||
navController.currentBackStackEntry?.savedStateHandle?.let { handle ->
|
||||
fillInHandleForPaymentRequest(handle, zecSend, zip321Uri)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package co.electriccoin.zcash.ui.common.datasource
|
||||
|
||||
import cash.z.ecc.android.sdk.Synchronizer
|
||||
import cash.z.ecc.android.sdk.WalletCoordinator
|
||||
import cash.z.ecc.android.sdk.model.Account
|
||||
import cash.z.ecc.android.sdk.model.PersistableWallet
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.WalletAddress
|
||||
|
@ -14,6 +16,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
@ -23,10 +26,12 @@ import kotlinx.coroutines.flow.emptyFlow
|
|||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
interface AccountDataSource {
|
||||
|
||||
|
@ -73,7 +78,7 @@ class AccountDataSourceImpl(
|
|||
if (synchronizer == null || walletBalances == null || persistableWallet == null) {
|
||||
null
|
||||
} else {
|
||||
synchronizer.getAccounts().mapIndexed { index, account ->
|
||||
synchronizer.getAccountsSafe().mapIndexed { index, account ->
|
||||
val balance = walletBalances.getValue(account)
|
||||
val spendingKey = deriveSpendingKey(persistableWallet)
|
||||
|
||||
|
@ -91,11 +96,26 @@ class AccountDataSourceImpl(
|
|||
)
|
||||
}
|
||||
}
|
||||
}.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT, Duration.ZERO),
|
||||
initialValue = null
|
||||
)
|
||||
}.flowOn(Dispatchers.Default)
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT, Duration.ZERO),
|
||||
initialValue = null
|
||||
)
|
||||
|
||||
private suspend fun Synchronizer.getAccountsSafe(): List<Account> {
|
||||
var accounts: List<Account>? = null
|
||||
|
||||
while (accounts == null) {
|
||||
try {
|
||||
accounts = getAccounts()
|
||||
} catch (_: Throwable) {
|
||||
delay(1.seconds)
|
||||
}
|
||||
}
|
||||
|
||||
return accounts
|
||||
}
|
||||
|
||||
private suspend fun deriveSpendingKey(persistableWallet: PersistableWallet): UnifiedSpendingKey? {
|
||||
// crashes currently
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.common.repository.ExchangeRateRepository
|
||||
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
||||
class GetLoadedExchangeRateUseCase(
|
||||
private val exchangeRateRepository: ExchangeRateRepository
|
||||
) {
|
||||
suspend operator fun invoke() = exchangeRateRepository.state.first {
|
||||
when (it) {
|
||||
is ExchangeRateState.Data -> it.isLoading
|
||||
is ExchangeRateState.OptIn -> true
|
||||
ExchangeRateState.OptedOut -> true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package co.electriccoin.zcash.ui.common.usecase
|
||||
|
||||
import co.electriccoin.zcash.ui.common.datasource.AccountDataSource
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
||||
class GetSelectedWalletAccountUseCase(private val accountDataSource: AccountDataSource) {
|
||||
suspend operator fun invoke() = accountDataSource.getSelectedAccount()
|
||||
}
|
|
@ -31,6 +31,7 @@ import co.electriccoin.zcash.ui.screen.balances.WrapBalances
|
|||
import co.electriccoin.zcash.ui.screen.home.model.TabItem
|
||||
import co.electriccoin.zcash.ui.screen.home.view.Home
|
||||
import co.electriccoin.zcash.ui.screen.receive.WrapReceive
|
||||
import co.electriccoin.zcash.ui.screen.reviewtransaction.ReviewKeystoneTransaction
|
||||
import co.electriccoin.zcash.ui.screen.send.WrapSend
|
||||
import co.electriccoin.zcash.ui.screen.send.model.SendArguments
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
@ -43,6 +44,7 @@ internal fun WrapHome(
|
|||
goMultiTrxSubmissionFailure: () -> Unit,
|
||||
goScan: () -> Unit,
|
||||
goSendConfirmation: (ZecSend) -> Unit,
|
||||
goReviewKeystoneTransaction: (ReviewKeystoneTransaction) -> Unit,
|
||||
goPaymentRequest: (ZecSend, String) -> Unit,
|
||||
sendArguments: SendArguments
|
||||
) {
|
||||
|
@ -92,7 +94,8 @@ internal fun WrapHome(
|
|||
isShowingRestoreSuccess = isShowingRestoreSuccess,
|
||||
sendArguments = sendArguments,
|
||||
setShowingRestoreSuccess = setShowingRestoreSuccess,
|
||||
walletSnapshot = walletSnapshot
|
||||
walletSnapshot = walletSnapshot,
|
||||
goReviewKeystoneTransaction = goReviewKeystoneTransaction,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -102,6 +105,7 @@ internal fun WrapHome(
|
|||
goMultiTrxSubmissionFailure: () -> Unit,
|
||||
goScan: () -> Unit,
|
||||
goSendConfirmation: (ZecSend) -> Unit,
|
||||
goReviewKeystoneTransaction: (ReviewKeystoneTransaction) -> Unit,
|
||||
goPaymentRequest: (ZecSend, String) -> Unit,
|
||||
isKeepScreenOnWhileSyncing: Boolean?,
|
||||
isShowingRestoreSuccess: Boolean,
|
||||
|
@ -180,7 +184,8 @@ internal fun WrapHome(
|
|||
},
|
||||
goSendConfirmation = goSendConfirmation,
|
||||
goPaymentRequest = goPaymentRequest,
|
||||
sendArguments = sendArguments
|
||||
sendArguments = sendArguments,
|
||||
goReviewKeystoneTransaction = goReviewKeystoneTransaction
|
||||
)
|
||||
}
|
||||
),
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package co.electriccoin.zcash.ui.screen.reviewtransaction
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
|
||||
@Composable
|
||||
fun AndroidReviewKeystoneTransaction(args: ReviewKeystoneTransaction) {
|
||||
val viewModel = koinViewModel<ReviewKeystoneTransactionViewModel> { parametersOf(args) }
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler {
|
||||
state?.onBack
|
||||
}
|
||||
|
||||
state?.let {
|
||||
ReviewTransactionView(it)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package co.electriccoin.zcash.ui.screen.reviewtransaction
|
||||
|
||||
import cash.z.ecc.android.sdk.model.Memo
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import cash.z.ecc.sdk.model.AddressType
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReviewKeystoneTransaction(
|
||||
val addressString: String,
|
||||
val addressType: AddressType,
|
||||
val amountLong: Long,
|
||||
val memoString: String?
|
||||
) {
|
||||
val amount: Zatoshi
|
||||
get() = Zatoshi(amountLong)
|
||||
|
||||
val memo: Memo?
|
||||
get() = memoString?.let { Memo(it) }
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package co.electriccoin.zcash.ui.screen.reviewtransaction
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cash.z.ecc.sdk.ANDROID_STATE_FLOW_TIMEOUT
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetLoadedExchangeRateUseCase
|
||||
import co.electriccoin.zcash.ui.common.usecase.ObserveContactByAddressUseCase
|
||||
import co.electriccoin.zcash.ui.design.R
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.WhileSubscribed
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
class ReviewKeystoneTransactionViewModel(
|
||||
args: ReviewKeystoneTransaction,
|
||||
observeContactByAddress: ObserveContactByAddressUseCase,
|
||||
private val getLoadedExchangeRate: GetLoadedExchangeRateUseCase
|
||||
) : ViewModel() {
|
||||
val state = observeContactByAddress(args.addressString).map { addressBookContact ->
|
||||
ReviewTransactionState(
|
||||
title = stringRes("Review"),
|
||||
items = listOfNotNull(
|
||||
AmountState(
|
||||
title = stringRes("Total Amount"),
|
||||
amount = args.amount,
|
||||
exchangeRate = getLoadedExchangeRate(),
|
||||
),
|
||||
ReceiverState(
|
||||
title = stringRes("Sending to"),
|
||||
name = addressBookContact?.name?.let { stringRes(it) },
|
||||
address = stringRes(args.addressString)
|
||||
),
|
||||
SenderState(
|
||||
title = stringRes("Sending from"),
|
||||
icon = R.drawable.ic_item_keystone,
|
||||
name = stringRes("Keystone wallet"),
|
||||
),
|
||||
FinancialInfoState(
|
||||
title = stringRes("Amount"),
|
||||
amount = args.amount
|
||||
),
|
||||
FinancialInfoState(
|
||||
title = stringRes("Fee"),
|
||||
amount = args.amount
|
||||
),
|
||||
args.memo?.let {
|
||||
MessageState(
|
||||
title = stringRes("Message"),
|
||||
message = stringRes(it.value)
|
||||
)
|
||||
}
|
||||
),
|
||||
primaryButton = ButtonState(
|
||||
stringRes("Confirm with Keystone"),
|
||||
onClick = ::onConfirmClick
|
||||
),
|
||||
negativeButton = ButtonState(
|
||||
stringRes("Cancel"),
|
||||
onClick = ::onCancelClick
|
||||
),
|
||||
onBack = ::onBack,
|
||||
)
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null)
|
||||
|
||||
private fun onBack() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
private fun onCancelClick() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
private fun onConfirmClick() {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package co.electriccoin.zcash.ui.screen.reviewtransaction
|
||||
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.util.StringResource
|
||||
|
||||
data class ReviewTransactionState(
|
||||
val title: StringResource,
|
||||
val items: List<ReviewTransactionItemState>,
|
||||
val primaryButton: ButtonState,
|
||||
val negativeButton: ButtonState,
|
||||
val onBack: () -> Unit,
|
||||
)
|
||||
|
||||
sealed interface ReviewTransactionItemState
|
||||
|
||||
data class AmountState(
|
||||
val title: StringResource,
|
||||
val amount: Zatoshi,
|
||||
val exchangeRate: ExchangeRateState,
|
||||
) : ReviewTransactionItemState
|
||||
|
||||
data class ReceiverState(
|
||||
val title: StringResource,
|
||||
val name: StringResource?,
|
||||
val address: StringResource,
|
||||
) : ReviewTransactionItemState
|
||||
|
||||
data class SenderState(
|
||||
val title: StringResource,
|
||||
val icon: Int,
|
||||
val name: StringResource
|
||||
) : ReviewTransactionItemState
|
||||
|
||||
data class FinancialInfoState(
|
||||
val title: StringResource,
|
||||
val amount: Zatoshi,
|
||||
) : ReviewTransactionItemState
|
||||
|
||||
data class MessageState(
|
||||
val title: StringResource,
|
||||
val message: StringResource
|
||||
) : ReviewTransactionItemState
|
|
@ -0,0 +1,321 @@
|
|||
package co.electriccoin.zcash.ui.screen.reviewtransaction
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import cash.z.ecc.android.sdk.model.FiatCurrencyConversion
|
||||
import cash.z.ecc.sdk.extension.toZecStringFull
|
||||
import cash.z.ecc.sdk.fixture.ZatoshiFixture
|
||||
import co.electriccoin.zcash.ui.common.compose.BalanceWidgetBigLineOnly
|
||||
import co.electriccoin.zcash.ui.common.extension.asZecAmountTriple
|
||||
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
|
||||
import co.electriccoin.zcash.ui.design.R
|
||||
import co.electriccoin.zcash.ui.design.component.BlankBgScaffold
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.StyledBalance
|
||||
import co.electriccoin.zcash.ui.design.component.StyledBalanceDefaults
|
||||
import co.electriccoin.zcash.ui.design.component.TextFieldState
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiBottomBar
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiButton
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiTextField
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiTextFieldDefaults
|
||||
import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
|
||||
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
|
||||
import co.electriccoin.zcash.ui.design.util.getValue
|
||||
import co.electriccoin.zcash.ui.design.util.scaffoldPadding
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.exchangerate.widget.StyledExchangeLabel
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@Composable
|
||||
fun ReviewTransactionView(state: ReviewTransactionState) {
|
||||
BlankBgScaffold(
|
||||
topBar = {
|
||||
ZashiSmallTopAppBar(
|
||||
title = state.title.getValue()
|
||||
)
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.scaffoldPadding(it)
|
||||
) {
|
||||
state.items.forEachIndexed { index, item ->
|
||||
when (item) {
|
||||
is AmountState -> {
|
||||
AmountWidget(item)
|
||||
}
|
||||
|
||||
is ReceiverState -> {
|
||||
Spacer(Modifier.height(24.dp))
|
||||
ReceiverWidget(item)
|
||||
}
|
||||
|
||||
is SenderState -> {
|
||||
Spacer(Modifier.height(20.dp))
|
||||
SenderWidget(item)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
|
||||
is FinancialInfoState -> {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
FinancialInfoWidget(item)
|
||||
}
|
||||
|
||||
is MessageState -> {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
MessageWidget(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(32.dp))
|
||||
}
|
||||
BottomBar(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SenderWidget(state: SenderState) {
|
||||
Column {
|
||||
Text(
|
||||
text = state.title.getValue(),
|
||||
style = ZashiTypography.textSm,
|
||||
color = ZashiColors.Text.textTertiary,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.size(32.dp),
|
||||
painter = painterResource(id = state.icon),
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Text(
|
||||
text = state.name.getValue(),
|
||||
style = ZashiTypography.textMd,
|
||||
color = ZashiColors.Text.textPrimary,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ReceiverWidget(state: ReceiverState) {
|
||||
Column {
|
||||
Text(
|
||||
state.title.getValue(),
|
||||
style = ZashiTypography.textSm,
|
||||
color = ZashiColors.Text.textTertiary,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
if (state.name != null) {
|
||||
Text(
|
||||
state.name.getValue(),
|
||||
style = ZashiTypography.textSm,
|
||||
color = ZashiColors.Inputs.Filled.label,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
Text(
|
||||
state.address.getValue(),
|
||||
style = ZashiTypography.textXs,
|
||||
color = ZashiColors.Text.textPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MessageWidget(state: MessageState) {
|
||||
Column {
|
||||
Text(
|
||||
state.title.getValue(),
|
||||
style = ZashiTypography.textSm,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = ZashiColors.Text.textTertiary
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
ZashiTextField(
|
||||
state = TextFieldState(value = state.message, isEnabled = false) {},
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth(),
|
||||
colors =
|
||||
ZashiTextFieldDefaults.defaultColors(
|
||||
disabledTextColor = ZashiColors.Inputs.Filled.text,
|
||||
disabledHintColor = ZashiColors.Inputs.Disabled.hint,
|
||||
disabledBorderColor = Color.Unspecified,
|
||||
disabledContainerColor = ZashiColors.Inputs.Disabled.bg,
|
||||
disabledPlaceholderColor = ZashiColors.Inputs.Disabled.text,
|
||||
),
|
||||
minLines = 4
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FinancialInfoWidget(state: FinancialInfoState) {
|
||||
Row {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = state.title.getValue(),
|
||||
style = ZashiTypography.textSm,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = ZashiColors.Text.textTertiary
|
||||
)
|
||||
|
||||
StyledBalance(
|
||||
balanceParts = state.amount.toZecStringFull().asZecAmountTriple(),
|
||||
isHideBalances = false,
|
||||
textStyle =
|
||||
StyledBalanceDefaults.textStyles(
|
||||
mostSignificantPart = ZashiTypography.textSm.copy(fontWeight = FontWeight.SemiBold),
|
||||
leastSignificantPart = ZashiTypography.textXxs.copy(fontWeight = FontWeight.SemiBold, fontSize = 8.sp)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AmountWidget(state: AmountState) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
text = state.title.getValue(),
|
||||
style = ZashiTypography.textSm,
|
||||
color = ZashiColors.Text.textPrimary
|
||||
)
|
||||
BalanceWidgetBigLineOnly(
|
||||
parts = state.amount.toZecStringFull().asZecAmountTriple(),
|
||||
isHideBalances = false
|
||||
)
|
||||
StyledExchangeLabel(
|
||||
zatoshi = state.amount,
|
||||
state = state.exchangeRate,
|
||||
isHideBalances = false,
|
||||
style = ZashiTypography.textMd.copy(fontWeight = FontWeight.SemiBold),
|
||||
textColor = ZashiColors.Text.textPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomBar(state: ReviewTransactionState) {
|
||||
ZashiBottomBar {
|
||||
ZashiButton(
|
||||
state = state.primaryButton,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
ZashiButton(
|
||||
state = state.negativeButton,
|
||||
colors = ZashiButtonDefaults.secondaryColors(),
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 24.dp)
|
||||
.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewScreens
|
||||
@Composable
|
||||
private fun Preview() = ZcashTheme {
|
||||
ReviewTransactionView(
|
||||
state = ReviewTransactionState(
|
||||
title = stringRes("Review"),
|
||||
items = listOf(
|
||||
AmountState(
|
||||
title = stringRes("Total Amount"),
|
||||
amount = ZatoshiFixture.new(),
|
||||
exchangeRate = ExchangeRateState.Data(
|
||||
currencyConversion = FiatCurrencyConversion(
|
||||
timestamp = Clock.System.now(),
|
||||
priceOfZec = 50.0
|
||||
),
|
||||
isLoading = false,
|
||||
isStale = false,
|
||||
isRefreshEnabled = false,
|
||||
onRefresh = {}
|
||||
),
|
||||
),
|
||||
ReceiverState(
|
||||
title = stringRes("Total Amount"),
|
||||
name = stringRes("Receiver Name"),
|
||||
address = stringRes("Receiver Address")
|
||||
),
|
||||
SenderState(
|
||||
title = stringRes("Sending from"),
|
||||
icon = R.drawable.ic_item_keystone,
|
||||
name = stringRes("Keystone wallet"),
|
||||
),
|
||||
FinancialInfoState(
|
||||
title = stringRes("Amount"),
|
||||
amount = ZatoshiFixture.new()
|
||||
),
|
||||
FinancialInfoState(
|
||||
title = stringRes("Fee"),
|
||||
amount = ZatoshiFixture.new()
|
||||
),
|
||||
MessageState(
|
||||
title = stringRes("Message"),
|
||||
message = stringRes("Message")
|
||||
)
|
||||
),
|
||||
primaryButton = ButtonState(
|
||||
stringRes("Confirm with Keystone")
|
||||
),
|
||||
negativeButton = ButtonState(
|
||||
stringRes("Cancel")
|
||||
),
|
||||
onBack = {},
|
||||
|
||||
)
|
||||
)
|
||||
}
|
|
@ -28,12 +28,16 @@ import co.electriccoin.zcash.spackle.Twig
|
|||
import co.electriccoin.zcash.ui.common.compose.BalanceState
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalActivity
|
||||
import co.electriccoin.zcash.ui.common.compose.LocalNavController
|
||||
import co.electriccoin.zcash.ui.common.model.KeystoneAccount
|
||||
import co.electriccoin.zcash.ui.common.model.WalletSnapshot
|
||||
import co.electriccoin.zcash.ui.common.model.ZashiAccount
|
||||
import co.electriccoin.zcash.ui.common.usecase.GetSelectedWalletAccountUseCase
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.HomeViewModel
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.WalletViewModel
|
||||
import co.electriccoin.zcash.ui.common.viewmodel.ZashiMainTopAppBarViewModel
|
||||
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
|
||||
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
|
||||
import co.electriccoin.zcash.ui.screen.reviewtransaction.ReviewKeystoneTransaction
|
||||
import co.electriccoin.zcash.ui.screen.send.ext.Saver
|
||||
import co.electriccoin.zcash.ui.screen.send.model.AmountState
|
||||
import co.electriccoin.zcash.ui.screen.send.model.MemoState
|
||||
|
@ -43,6 +47,7 @@ import co.electriccoin.zcash.ui.screen.send.model.SendStage
|
|||
import co.electriccoin.zcash.ui.screen.send.view.Send
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.compose.koinInject
|
||||
import org.zecdev.zip321.ZIP321
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -54,6 +59,7 @@ internal fun WrapSend(
|
|||
goBack: () -> Unit,
|
||||
goBalances: () -> Unit,
|
||||
goSendConfirmation: (ZecSend) -> Unit,
|
||||
goReviewKeystoneTransaction: (ReviewKeystoneTransaction) -> Unit,
|
||||
goPaymentRequest: (ZecSend, String) -> Unit,
|
||||
) {
|
||||
val activity = LocalActivity.current
|
||||
|
@ -93,6 +99,7 @@ internal fun WrapSend(
|
|||
hasCameraFeature = hasCameraFeature,
|
||||
monetarySeparators = monetarySeparators,
|
||||
exchangeRateState = exchangeRateState,
|
||||
goReviewKeystoneTransaction = goReviewKeystoneTransaction
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -107,6 +114,7 @@ internal fun WrapSend(
|
|||
goBack: () -> Unit,
|
||||
goBalances: () -> Unit,
|
||||
goSendConfirmation: (ZecSend) -> Unit,
|
||||
goReviewKeystoneTransaction: (ReviewKeystoneTransaction) -> Unit,
|
||||
goPaymentRequest: (ZecSend, String) -> Unit,
|
||||
hasCameraFeature: Boolean,
|
||||
monetarySeparators: MonetarySeparators,
|
||||
|
@ -121,6 +129,8 @@ internal fun WrapSend(
|
|||
|
||||
val viewModel = koinViewModel<SendViewModel>()
|
||||
|
||||
val getSelectedWalletAccount = koinInject<GetSelectedWalletAccountUseCase>()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.navigateCommand.collect {
|
||||
navController.navigate(it)
|
||||
|
@ -263,21 +273,49 @@ internal fun WrapSend(
|
|||
isHideBalances = isHideBalances,
|
||||
sendStage = sendStage,
|
||||
onCreateZecSend = { newZecSend ->
|
||||
goReviewKeystoneTransaction(
|
||||
ReviewKeystoneTransaction(
|
||||
addressString = newZecSend.destination.address,
|
||||
addressType = cash.z.ecc.sdk.model.AddressType.fromWalletAddress(newZecSend.destination),
|
||||
amountLong = newZecSend.amount.value,
|
||||
memoString = newZecSend.memo.value.takeIf { it.isNotEmpty() },
|
||||
)
|
||||
)
|
||||
scope.launch {
|
||||
spendingKey?.let {
|
||||
Twig.debug { "Getting send transaction proposal" }
|
||||
runCatching {
|
||||
synchronizer.proposeSend(spendingKey.account, newZecSend)
|
||||
}.onSuccess { proposal ->
|
||||
Twig.debug { "Transaction proposal successful: ${proposal.toPrettyString()}" }
|
||||
val enrichedZecSend = newZecSend.copy(proposal = proposal)
|
||||
setZecSend(enrichedZecSend)
|
||||
goSendConfirmation(enrichedZecSend)
|
||||
}.onFailure {
|
||||
Twig.error(it) { "Transaction proposal failed" }
|
||||
setSendStage(SendStage.SendFailure(it.message ?: ""))
|
||||
}
|
||||
}
|
||||
// goReviewKeystoneTransaction(
|
||||
// ReviewKeystoneTransaction(
|
||||
// addressString = newZecSend.destination.address,
|
||||
// addressType = cash.z.ecc.sdk.model.AddressType.fromWalletAddress(newZecSend.destination),
|
||||
// amountLong = newZecSend.amount.value,
|
||||
// memoString = newZecSend.memo.value,
|
||||
// )
|
||||
// )
|
||||
// when (getSelectedWalletAccount()) {
|
||||
// is KeystoneAccount -> {
|
||||
// goReviewKeystoneTransaction(
|
||||
// ReviewKeystoneTransaction(
|
||||
// addressString = newZecSend.destination.address,
|
||||
// addressType = cash.z.ecc.sdk.model.AddressType.fromWalletAddress(newZecSend.destination),
|
||||
// amountLong = newZecSend.amount.value,
|
||||
// memoString = newZecSend.memo.value,
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// is ZashiAccount -> spendingKey?.let {
|
||||
// Twig.debug { "Getting send transaction proposal" }
|
||||
// runCatching {
|
||||
// synchronizer.proposeSend(spendingKey.account, newZecSend)
|
||||
// }.onSuccess { proposal ->
|
||||
// Twig.debug { "Transaction proposal successful: ${proposal.toPrettyString()}" }
|
||||
// val enrichedZecSend = newZecSend.copy(proposal = proposal)
|
||||
// setZecSend(enrichedZecSend)
|
||||
// goSendConfirmation(enrichedZecSend)
|
||||
// }.onFailure {
|
||||
// Twig.error(it) { "Transaction proposal failed" }
|
||||
// setSendStage(SendStage.SendFailure(it.message ?: ""))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
},
|
||||
onBack = onBackAction,
|
||||
|
|
Loading…
Reference in New Issue