QR code image logic refactoring (#1732)
* QR code image logic refactor The QR code image logic of the `QrCode` and `Request` screens has been refactored to work upon the newer `ZashiQr` component * Refactor values into state * Changelog update
This commit is contained in:
parent
db983c692f
commit
603178fd67
|
@ -6,6 +6,10 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
- The QR code image logic of the `QrCode`, `Request`, and `SignTransaction` screens has been refactored to work
|
||||
with the newer `ZashiQr` component
|
||||
|
||||
### Fixed
|
||||
- The Disconnected popup trigger when the app is backgrounded has been fixed
|
||||
|
||||
|
|
|
@ -2,16 +2,21 @@ package co.electriccoin.zcash.ui.design.component
|
|||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Surface
|
||||
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.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
|
||||
|
@ -19,31 +24,44 @@ import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions
|
|||
import co.electriccoin.zcash.ui.design.util.AndroidQrCodeImageGenerator
|
||||
import co.electriccoin.zcash.ui.design.util.JvmQrCodeGenerator
|
||||
import co.electriccoin.zcash.ui.design.util.QrCodeColors
|
||||
import co.electriccoin.zcash.ui.design.util.StringResource
|
||||
import co.electriccoin.zcash.ui.design.util.getValue
|
||||
import co.electriccoin.zcash.ui.design.util.orDark
|
||||
|
||||
@Composable
|
||||
fun ZashiQr(
|
||||
qrData: String,
|
||||
state: QrState,
|
||||
modifier: Modifier = Modifier,
|
||||
qrSize: Dp = ZashiQrDefaults.width,
|
||||
colors: QrCodeColors = QrCodeDefaults.colors()
|
||||
colors: QrCodeColors = QrCodeDefaults.colors(),
|
||||
) {
|
||||
val qrSizePx = with(LocalDensity.current) { qrSize.roundToPx() }
|
||||
val bitmap = getQrCode(qrData, qrSizePx, colors)
|
||||
val bitmap = getQrCode(state.qrData, qrSizePx, colors)
|
||||
|
||||
Surface(
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl),
|
||||
border = BorderStroke(width = 1.dp, color = ZashiColors.Surfaces.strokePrimary),
|
||||
color = ZashiColors.Surfaces.bgPrimary
|
||||
color = ZashiColors.Surfaces.bgPrimary,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.padding(all = 12.dp)
|
||||
) {
|
||||
Image(
|
||||
bitmap = bitmap,
|
||||
contentDescription = null,
|
||||
contentDescription = state.contentDescription?.getValue(),
|
||||
Modifier.clickable { state.onClick() }
|
||||
)
|
||||
if (state.centerImageResId != null) {
|
||||
Image(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(64.dp)
|
||||
.align(Alignment.Center),
|
||||
imageVector = ImageVector.vectorResource(state.centerImageResId),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,3 +93,10 @@ object QrCodeDefaults {
|
|||
foreground = foreground
|
||||
)
|
||||
}
|
||||
|
||||
data class QrState(
|
||||
val qrData: String,
|
||||
val contentDescription: StringResource? = null,
|
||||
val onClick: () -> Unit = {},
|
||||
val centerImageResId: Int? = null,
|
||||
)
|
||||
|
|
|
@ -541,20 +541,7 @@ fun NavHostController.popBackStackJustOnce(currentRouteToBePopped: String) {
|
|||
object NavigationArguments {
|
||||
const val SEND_SCAN_RECIPIENT_ADDRESS = "send_scan_recipient_address"
|
||||
const val SEND_SCAN_ZIP_321_URI = "send_scan_zip_321_uri"
|
||||
|
||||
const val SEND_CONFIRM_RECIPIENT_ADDRESS = "send_confirm_recipient_address"
|
||||
const val SEND_CONFIRM_AMOUNT = "send_confirm_amount"
|
||||
const val SEND_CONFIRM_MEMO = "send_confirm_memo"
|
||||
const val SEND_CONFIRM_PROPOSAL = "send_confirm_proposal"
|
||||
const val SEND_CONFIRM_INITIAL_STAGE = "send_confirm_initial_stage"
|
||||
|
||||
const val MULTIPLE_SUBMISSION_CLEAR_FORM = "multiple_submission_clear_form"
|
||||
|
||||
const val PAYMENT_REQUEST_ADDRESS = "payment_request_address"
|
||||
const val PAYMENT_REQUEST_AMOUNT = "payment_request_amount"
|
||||
const val PAYMENT_REQUEST_MEMO = "payment_request_memo"
|
||||
const val PAYMENT_REQUEST_PROPOSAL = "payment_request_proposal"
|
||||
const val PAYMENT_REQUEST_URI = "payment_request_uri"
|
||||
}
|
||||
|
||||
object NavigationTargets {
|
||||
|
|
|
@ -5,7 +5,6 @@ package co.electriccoin.zcash.ui.screen.exchangerate.widget
|
|||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.basicMarquee
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -93,7 +92,6 @@ fun StyledExchangeBalance(
|
|||
}
|
||||
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun ExchangeAvailableRateLabelInternal(
|
||||
style: TextStyle,
|
||||
|
|
|
@ -2,8 +2,10 @@ package co.electriccoin.zcash.ui.screen.qrcode.model
|
|||
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import cash.z.ecc.android.sdk.model.WalletAddress
|
||||
import co.electriccoin.zcash.ui.design.component.QrState
|
||||
import co.electriccoin.zcash.ui.design.util.StringResource
|
||||
|
||||
internal sealed class QrCodeState {
|
||||
sealed class QrCodeState {
|
||||
data object Loading : QrCodeState()
|
||||
|
||||
data class Prepared(
|
||||
|
@ -11,8 +13,19 @@ internal sealed class QrCodeState {
|
|||
val walletAddress: WalletAddress,
|
||||
val onAddressCopy: (String) -> Unit,
|
||||
val onQrCodeShare: (ImageBitmap) -> Unit,
|
||||
val onQrCodeClick: () -> Unit,
|
||||
val onBack: () -> Unit,
|
||||
) : QrCodeState()
|
||||
) : QrCodeState() {
|
||||
fun toQrState(
|
||||
contentDescription: StringResource? = null,
|
||||
centerImageResId: Int? = null
|
||||
) = QrState(
|
||||
qrData = walletAddress.address,
|
||||
onClick = onQrCodeClick,
|
||||
contentDescription = contentDescription,
|
||||
centerImageResId = centerImageResId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class QrCodeType {
|
||||
|
|
|
@ -3,15 +3,10 @@
|
|||
package co.electriccoin.zcash.ui.screen.qrcode.view
|
||||
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
|
@ -21,7 +16,6 @@ import androidx.compose.foundation.layout.height
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
|
@ -33,7 +27,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
|
@ -55,15 +49,16 @@ import co.electriccoin.zcash.ui.design.component.ZashiBadgeColors
|
|||
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.ZashiQr
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar
|
||||
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.dimensions.ZashiDimensions
|
||||
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
|
||||
import co.electriccoin.zcash.ui.design.util.AndroidQrCodeImageGenerator
|
||||
import co.electriccoin.zcash.ui.design.util.JvmQrCodeGenerator
|
||||
import co.electriccoin.zcash.ui.design.util.QrCodeColors
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.qrcode.model.QrCodeState
|
||||
import co.electriccoin.zcash.ui.screen.qrcode.model.QrCodeType
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -91,6 +86,7 @@ private fun ZashiPreview() =
|
|||
walletAddress = runBlocking { WalletAddressFixture.unified() },
|
||||
onAddressCopy = {},
|
||||
onQrCodeShare = {},
|
||||
onQrCodeClick = {},
|
||||
onBack = {},
|
||||
),
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
|
@ -109,6 +105,7 @@ private fun KeystonePreview() =
|
|||
walletAddress = runBlocking { WalletAddressFixture.unified() },
|
||||
onAddressCopy = {},
|
||||
onQrCodeShare = {},
|
||||
onQrCodeClick = {},
|
||||
onBack = {},
|
||||
),
|
||||
snackbarHostState = SnackbarHostState(),
|
||||
|
@ -154,10 +151,7 @@ internal fun QrCodeView(
|
|||
}
|
||||
) { paddingValues ->
|
||||
QrCodeContents(
|
||||
qrCodeType = state.qrCodeType,
|
||||
walletAddress = state.walletAddress,
|
||||
onAddressCopy = state.onAddressCopy,
|
||||
onQrCodeShare = state.onQrCodeShare,
|
||||
state = state,
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
|
@ -239,10 +233,7 @@ private fun QrCodeBottomBar(
|
|||
|
||||
@Composable
|
||||
private fun QrCodeContents(
|
||||
qrCodeType: QrCodeType,
|
||||
walletAddress: WalletAddress,
|
||||
onAddressCopy: (String) -> Unit,
|
||||
onQrCodeShare: (ImageBitmap) -> Unit,
|
||||
state: QrCodeState.Prepared,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
|
@ -254,16 +245,16 @@ private fun QrCodeContents(
|
|||
) {
|
||||
Spacer(Modifier.height(ZcashTheme.dimens.spacingDefault))
|
||||
|
||||
when (walletAddress) {
|
||||
when (state.walletAddress) {
|
||||
// We use the same design for the Sapling address for the Testnet app variant
|
||||
is WalletAddress.Unified, is WalletAddress.Sapling -> {
|
||||
UnifiedQrCodePanel(qrCodeType, walletAddress, onAddressCopy, onQrCodeShare)
|
||||
UnifiedQrCodePanel(state)
|
||||
}
|
||||
is WalletAddress.Transparent -> {
|
||||
TransparentQrCodePanel(qrCodeType, walletAddress, onAddressCopy, onQrCodeShare)
|
||||
TransparentQrCodePanel(state)
|
||||
}
|
||||
else -> {
|
||||
error("Unsupported address type: $walletAddress")
|
||||
error("Unsupported address type: ${state.walletAddress}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,10 +263,7 @@ private fun QrCodeContents(
|
|||
@Composable
|
||||
@Suppress("LongMethod")
|
||||
fun UnifiedQrCodePanel(
|
||||
qrCodeType: QrCodeType,
|
||||
walletAddress: WalletAddress,
|
||||
onAddressCopy: (String) -> Unit,
|
||||
onQrCodeShare: (ImageBitmap) -> Unit,
|
||||
state: QrCodeState.Prepared,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var expandedAddress by rememberSaveable { mutableStateOf(false) }
|
||||
|
@ -284,15 +272,13 @@ fun UnifiedQrCodePanel(
|
|||
modifier =
|
||||
modifier
|
||||
.padding(vertical = ZcashTheme.dimens.spacingDefault),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
horizontalAlignment = CenterHorizontally
|
||||
) {
|
||||
QrCode(
|
||||
walletAddress = walletAddress,
|
||||
onQrImageShare = onQrCodeShare,
|
||||
preparedState = state,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 24.dp),
|
||||
qrCodeType = qrCodeType,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingUpLarge))
|
||||
|
@ -312,18 +298,18 @@ fun UnifiedQrCodePanel(
|
|||
|
||||
Text(
|
||||
text =
|
||||
when (walletAddress) {
|
||||
when (state.walletAddress) {
|
||||
is WalletAddress.Unified ->
|
||||
when (qrCodeType) {
|
||||
when (state.qrCodeType) {
|
||||
QrCodeType.ZASHI -> stringResource(R.string.qr_code_wallet_address_shielded)
|
||||
QrCodeType.KEYSTONE -> stringResource(R.string.qr_code_wallet_address_shielded_keystone)
|
||||
}
|
||||
is WalletAddress.Sapling ->
|
||||
when (qrCodeType) {
|
||||
when (state.qrCodeType) {
|
||||
QrCodeType.ZASHI -> stringResource(id = R.string.qr_code_wallet_address_sapling)
|
||||
QrCodeType.KEYSTONE -> stringResource(id = R.string.qr_code_wallet_address_sapling_keystone)
|
||||
}
|
||||
else -> error("Unsupported address type: $walletAddress")
|
||||
else -> error("Unsupported address type: ${state.walletAddress}")
|
||||
},
|
||||
color = ZashiColors.Text.textPrimary,
|
||||
style = ZashiTypography.textXl,
|
||||
|
@ -335,7 +321,7 @@ fun UnifiedQrCodePanel(
|
|||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
Text(
|
||||
text = walletAddress.address,
|
||||
text = state.walletAddress.address,
|
||||
color = ZashiColors.Text.textTertiary,
|
||||
style = ZashiTypography.textSm,
|
||||
textAlign = TextAlign.Center,
|
||||
|
@ -353,7 +339,7 @@ fun UnifiedQrCodePanel(
|
|||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { expandedAddress = !expandedAddress },
|
||||
onLongClick = { onAddressCopy(walletAddress.address) }
|
||||
onLongClick = { state.onAddressCopy(state.walletAddress.address) }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -362,10 +348,7 @@ fun UnifiedQrCodePanel(
|
|||
@Composable
|
||||
@Suppress("LongMethod")
|
||||
fun TransparentQrCodePanel(
|
||||
qrCodeType: QrCodeType,
|
||||
walletAddress: WalletAddress,
|
||||
onAddressCopy: (String) -> Unit,
|
||||
onQrCodeShare: (ImageBitmap) -> Unit,
|
||||
state: QrCodeState.Prepared,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
var expandedAddress by rememberSaveable { mutableStateOf(false) }
|
||||
|
@ -374,15 +357,13 @@ fun TransparentQrCodePanel(
|
|||
modifier =
|
||||
modifier
|
||||
.padding(vertical = ZcashTheme.dimens.spacingDefault),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
horizontalAlignment = CenterHorizontally
|
||||
) {
|
||||
QrCode(
|
||||
walletAddress = walletAddress,
|
||||
onQrImageShare = onQrCodeShare,
|
||||
preparedState = state,
|
||||
modifier =
|
||||
Modifier
|
||||
.padding(horizontal = 24.dp),
|
||||
qrCodeType = qrCodeType,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingUpLarge))
|
||||
|
@ -412,7 +393,7 @@ fun TransparentQrCodePanel(
|
|||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
Text(
|
||||
text = walletAddress.address,
|
||||
text = state.walletAddress.address,
|
||||
color = ZashiColors.Text.textTertiary,
|
||||
style = ZashiTypography.textSm,
|
||||
textAlign = TextAlign.Center,
|
||||
|
@ -430,7 +411,7 @@ fun TransparentQrCodePanel(
|
|||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { expandedAddress = !expandedAddress },
|
||||
onLongClick = { onAddressCopy(walletAddress.address) }
|
||||
onLongClick = { state.onAddressCopy(state.walletAddress.address) }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -438,51 +419,28 @@ fun TransparentQrCodePanel(
|
|||
|
||||
@Composable
|
||||
private fun ColumnScope.QrCode(
|
||||
qrCodeType: QrCodeType,
|
||||
walletAddress: WalletAddress,
|
||||
onQrImageShare: (ImageBitmap) -> Unit,
|
||||
preparedState: QrCodeState.Prepared,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val sizePixels = with(LocalDensity.current) { DEFAULT_QR_CODE_SIZE.toPx() }.roundToInt()
|
||||
val colors = QrCodeDefaults.colors()
|
||||
val qrCodeImage =
|
||||
remember {
|
||||
qrCodeForAddress(
|
||||
address = walletAddress.address,
|
||||
size = sizePixels,
|
||||
colors = colors
|
||||
)
|
||||
}
|
||||
|
||||
QrCode(
|
||||
qrCodeImage = qrCodeImage,
|
||||
onQrImageBitmapShare = onQrImageShare,
|
||||
contentDescription =
|
||||
stringResource(
|
||||
when (walletAddress) {
|
||||
is WalletAddress.Unified -> R.string.qr_code_unified_content_description
|
||||
is WalletAddress.Sapling -> R.string.qr_code_sapling_content_description
|
||||
is WalletAddress.Transparent -> R.string.qr_code_transparent_content_description
|
||||
else -> error("Unsupported address type: $walletAddress")
|
||||
}
|
||||
ZashiQr(
|
||||
state =
|
||||
preparedState.toQrState(
|
||||
contentDescription =
|
||||
stringRes(
|
||||
when (preparedState.walletAddress) {
|
||||
is WalletAddress.Unified -> R.string.qr_code_unified_content_description
|
||||
is WalletAddress.Sapling -> R.string.qr_code_sapling_content_description
|
||||
is WalletAddress.Transparent -> R.string.qr_code_transparent_content_description
|
||||
else -> error("Unsupported address type: ${preparedState.walletAddress}")
|
||||
}
|
||||
),
|
||||
centerImageResId =
|
||||
when (preparedState.qrCodeType) {
|
||||
QrCodeType.ZASHI -> R.drawable.logo_zec_fill_stroke
|
||||
QrCodeType.KEYSTONE -> co.electriccoin.zcash.ui.design.R.drawable.ic_item_keystone_qr
|
||||
}
|
||||
),
|
||||
modifier =
|
||||
modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.border(
|
||||
border =
|
||||
BorderStroke(
|
||||
width = 1.dp,
|
||||
color = ZashiColors.Surfaces.strokePrimary
|
||||
),
|
||||
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl)
|
||||
)
|
||||
.background(
|
||||
color = ZashiColors.Surfaces.bgPrimary,
|
||||
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl)
|
||||
)
|
||||
.padding(all = 12.dp),
|
||||
qrCodeType = qrCodeType
|
||||
modifier = modifier.align(CenterHorizontally),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -502,45 +460,4 @@ private fun qrCodeForAddress(
|
|||
return AndroidQrCodeImageGenerator.generate(qrCodePixelArray, size, colors)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QrCode(
|
||||
qrCodeType: QrCodeType,
|
||||
contentDescription: String,
|
||||
qrCodeImage: ImageBitmap,
|
||||
onQrImageBitmapShare: (ImageBitmap) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier =
|
||||
Modifier
|
||||
.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { onQrImageBitmapShare(qrCodeImage) },
|
||||
)
|
||||
.then(modifier)
|
||||
) {
|
||||
Image(
|
||||
bitmap = qrCodeImage,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
|
||||
Image(
|
||||
modifier = Modifier.size(64.dp),
|
||||
painter =
|
||||
when (qrCodeType) {
|
||||
QrCodeType.ZASHI -> painterResource(id = R.drawable.logo_zec_fill_stroke)
|
||||
QrCodeType.KEYSTONE ->
|
||||
painterResource(
|
||||
id =
|
||||
co.electriccoin.zcash.ui.design.R.drawable
|
||||
.ic_item_keystone_qr
|
||||
)
|
||||
},
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val DEFAULT_QR_CODE_SIZE = 320.dp
|
||||
|
|
|
@ -57,6 +57,10 @@ class QrCodeViewModel(
|
|||
walletAddress = walletAddress,
|
||||
onAddressCopy = { address -> onAddressCopyClick(address) },
|
||||
onQrCodeShare = { onQrCodeShareClick(it, versionInfo) },
|
||||
onQrCodeClick = {
|
||||
// TODO [#1731]: Allow QR codes colors switching
|
||||
// TODO [#1731]: https://github.com/Electric-Coin-Company/zashi-android/issues/1731
|
||||
},
|
||||
onBack = ::onBack,
|
||||
qrCodeType =
|
||||
when (account) {
|
||||
|
|
|
@ -5,7 +5,9 @@ import cash.z.ecc.android.sdk.model.MonetarySeparators
|
|||
import cash.z.ecc.android.sdk.model.WalletAddress
|
||||
import cash.z.ecc.sdk.type.ZcashCurrency
|
||||
import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState
|
||||
import co.electriccoin.zcash.ui.design.component.QrState
|
||||
import co.electriccoin.zcash.ui.design.util.QrCodeColors
|
||||
import co.electriccoin.zcash.ui.design.util.StringResource
|
||||
|
||||
internal sealed class RequestState {
|
||||
data object Loading : RequestState()
|
||||
|
@ -37,10 +39,21 @@ internal sealed class RequestState {
|
|||
val icon: Int,
|
||||
val request: Request,
|
||||
val walletAddress: WalletAddress,
|
||||
val onQrCodeClick: () -> Unit,
|
||||
val onQrCodeShare: (ImageBitmap) -> Unit,
|
||||
val onQrCodeGenerate: (pixels: Int, colors: QrCodeColors) -> Unit,
|
||||
override val onBack: () -> Unit,
|
||||
val onClose: () -> Unit,
|
||||
val zcashCurrency: ZcashCurrency,
|
||||
) : Prepared(onBack)
|
||||
) : Prepared(onBack) {
|
||||
fun toQrState(
|
||||
contentDescription: StringResource? = null,
|
||||
centerImageResId: Int? = null,
|
||||
) = QrState(
|
||||
qrData = walletAddress.address,
|
||||
onClick = onQrCodeClick,
|
||||
contentDescription = contentDescription,
|
||||
centerImageResId = centerImageResId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,18 @@
|
|||
package co.electriccoin.zcash.ui.screen.request.view
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
|
@ -32,16 +22,14 @@ import androidx.compose.ui.text.withStyle
|
|||
import androidx.compose.ui.unit.dp
|
||||
import cash.z.ecc.android.sdk.model.WalletAddress
|
||||
import co.electriccoin.zcash.ui.R
|
||||
import co.electriccoin.zcash.ui.design.component.CircularScreenProgressIndicator
|
||||
import co.electriccoin.zcash.ui.design.component.QrCodeDefaults
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiBadge
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiBadgeColors
|
||||
import co.electriccoin.zcash.ui.design.component.ZashiQr
|
||||
import co.electriccoin.zcash.ui.design.theme.ZcashTheme
|
||||
import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors
|
||||
import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions
|
||||
import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography
|
||||
import co.electriccoin.zcash.ui.design.util.stringRes
|
||||
import co.electriccoin.zcash.ui.screen.request.model.RequestState
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
internal fun RequestQrCodeView(
|
||||
|
@ -96,7 +84,7 @@ internal fun RequestQrCodeView(
|
|||
Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig))
|
||||
|
||||
QrCode(
|
||||
state = state,
|
||||
requestState = state,
|
||||
modifier = Modifier.padding(horizontal = 24.dp),
|
||||
)
|
||||
|
||||
|
@ -106,71 +94,19 @@ internal fun RequestQrCodeView(
|
|||
|
||||
@Composable
|
||||
private fun ColumnScope.QrCode(
|
||||
state: RequestState.QrCode,
|
||||
requestState: RequestState.QrCode,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val sizePixels = with(LocalDensity.current) { DEFAULT_QR_CODE_SIZE.toPx() }.roundToInt()
|
||||
|
||||
if (state.request.qrCodeState.bitmap == null) {
|
||||
val colors = QrCodeDefaults.colors()
|
||||
state.onQrCodeGenerate(sizePixels, colors)
|
||||
}
|
||||
|
||||
QrCode(
|
||||
state = state,
|
||||
contentDescription = stringResource(id = R.string.request_qr_code_content_description),
|
||||
modifier =
|
||||
modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.border(
|
||||
border =
|
||||
BorderStroke(
|
||||
width = 1.dp,
|
||||
color = ZashiColors.Surfaces.strokePrimary
|
||||
),
|
||||
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl)
|
||||
)
|
||||
.background(
|
||||
color = ZashiColors.Surfaces.bgPrimary,
|
||||
shape = RoundedCornerShape(ZashiDimensions.Radius.radius4xl)
|
||||
)
|
||||
.padding(all = 12.dp)
|
||||
ZashiQr(
|
||||
state =
|
||||
requestState.toQrState(
|
||||
contentDescription = stringRes(R.string.request_qr_code_content_description),
|
||||
centerImageResId = requestState.icon
|
||||
),
|
||||
modifier = modifier.align(CenterHorizontally),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun QrCode(
|
||||
state: RequestState.QrCode,
|
||||
contentDescription: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier =
|
||||
Modifier
|
||||
.clickable(
|
||||
indication = null,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
onClick = { state.request.qrCodeState.bitmap?.let { state.onQrCodeShare(it) } },
|
||||
)
|
||||
.then(modifier)
|
||||
) {
|
||||
if (state.request.qrCodeState.bitmap == null) {
|
||||
CircularScreenProgressIndicator()
|
||||
} else {
|
||||
Image(
|
||||
bitmap = state.request.qrCodeState.bitmap,
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
Image(
|
||||
modifier = Modifier.size(64.dp),
|
||||
painter = painterResource(id = state.icon),
|
||||
contentDescription = contentDescription,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RequestQrCodeZecAmountView(
|
||||
state: RequestState.QrCode,
|
||||
|
@ -196,5 +132,3 @@ private fun RequestQrCodeZecAmountView(
|
|||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
private val DEFAULT_QR_CODE_SIZE = 320.dp
|
||||
|
|
|
@ -144,6 +144,10 @@ class RequestViewModel(
|
|||
colors = colors,
|
||||
)
|
||||
},
|
||||
onQrCodeClick = {
|
||||
// TODO [#1731]: Allow QR codes colors switching
|
||||
// TODO [#1731]: https://github.com/Electric-Coin-Company/zashi-android/issues/1731
|
||||
},
|
||||
onQrCodeShare = { onRequestQrCodeShare(it, shareImageBitmap) },
|
||||
onBack = ::onBack,
|
||||
onClose = ::onClose,
|
||||
|
|
|
@ -2,17 +2,32 @@ package co.electriccoin.zcash.ui.screen.signkeystonetransaction.state
|
|||
|
||||
import androidx.annotation.DrawableRes
|
||||
import co.electriccoin.zcash.ui.design.component.ButtonState
|
||||
import co.electriccoin.zcash.ui.design.component.QrState
|
||||
import co.electriccoin.zcash.ui.design.util.StringResource
|
||||
|
||||
data class SignKeystoneTransactionState(
|
||||
val onBack: () -> Unit,
|
||||
val onQrCodeClick: () -> Unit,
|
||||
val accountInfo: ZashiAccountInfoListItemState,
|
||||
val qrData: String?,
|
||||
val generateNextQrCode: () -> Unit,
|
||||
val shareButton: ButtonState?,
|
||||
val positiveButton: ButtonState,
|
||||
val negativeButton: ButtonState,
|
||||
)
|
||||
) {
|
||||
fun toQrState(
|
||||
contentDescription: StringResource? = null,
|
||||
centerImageResId: Int? = null,
|
||||
): QrState {
|
||||
requireNotNull(qrData) { "The QR code data needs to be set at this point" }
|
||||
return QrState(
|
||||
qrData = qrData,
|
||||
onClick = onQrCodeClick,
|
||||
contentDescription = contentDescription,
|
||||
centerImageResId = centerImageResId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class ZashiAccountInfoListItemState(
|
||||
@DrawableRes val icon: Int,
|
||||
|
|
|
@ -124,14 +124,17 @@ private fun ZashiAccountInfoListItem(
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.QrContent(state: SignKeystoneTransactionState) {
|
||||
state.qrData?.let {
|
||||
ZashiQr(qrData = it, modifier = Modifier.align(CenterHorizontally))
|
||||
private fun ColumnScope.QrContent(ksState: SignKeystoneTransactionState) {
|
||||
ksState.qrData?.let {
|
||||
ZashiQr(
|
||||
state = ksState.toQrState(),
|
||||
modifier = Modifier.align(CenterHorizontally)
|
||||
)
|
||||
}
|
||||
LaunchedEffect(state.qrData) {
|
||||
if (state.qrData != null) {
|
||||
LaunchedEffect(ksState.qrData) {
|
||||
if (ksState.qrData != null) {
|
||||
delay(100.milliseconds)
|
||||
state.generateNextQrCode()
|
||||
ksState.generateNextQrCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,6 +185,7 @@ private fun Preview() =
|
|||
positiveButton = ButtonState(stringRes("Get Signature")),
|
||||
negativeButton = ButtonState(stringRes("Reject")),
|
||||
onBack = {},
|
||||
onQrCodeClick = {},
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -205,6 +209,7 @@ private fun DebugPreview() =
|
|||
positiveButton = ButtonState(stringRes("Get Signature")),
|
||||
negativeButton = ButtonState(stringRes("Reject")),
|
||||
onBack = {},
|
||||
onQrCodeClick = {},
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -75,7 +75,11 @@ class SignKeystoneTransactionViewModel(
|
|||
text = stringRes("Share PCZT"),
|
||||
onClick = ::onSharePCZTClick
|
||||
).takeIf { BuildConfig.DEBUG },
|
||||
onBack = ::onBack
|
||||
onBack = ::onBack,
|
||||
onQrCodeClick = {
|
||||
// TODO [#1731]: Allow QR codes colors switching
|
||||
// TODO [#1731]: https://github.com/Electric-Coin-Company/zashi-android/issues/1731
|
||||
},
|
||||
)
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(ANDROID_STATE_FLOW_TIMEOUT), null)
|
||||
|
||||
|
|
Loading…
Reference in New Issue