Skip to content

Commit

Permalink
(android) add payer-key toggle in send-offer screen
Browse files Browse the repository at this point in the history
This toggle init state depends on the offer paid. If recipient
is a known contact, then use the offer key by default.

Also fix a few issues with the scan-data screen
  • Loading branch information
dpad85 committed Jul 1, 2024
1 parent 544438b commit ac8ec65
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,10 @@ fun AppView(
paymentId = paymentId,
onBackClick = {
val previousNav = navController.previousBackStackEntry
if (!navController.popBackStack() || (fromEvent && previousNav?.destination?.route == Screen.ScanData.route)) {
if (fromEvent && previousNav?.destination?.route == Screen.ScanData.route) {
popToHome(navController)
} else {
navController.popBackStack()
}
},
fromEvent = fromEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,11 @@ fun HSeparator(

@Composable
fun VSeparator(
padding: PaddingValues = PaddingValues(0.dp)
padding: PaddingValues = PaddingValues(0.dp),
height: Dp? = null
) {
Box(
Modifier
.fillMaxHeight()
(height?.run { Modifier.height(height) } ?: Modifier.fillMaxHeight())
.width(1.dp)
.padding(padding)
.background(color = borderColor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,22 @@ fun SwitchView(
val interactionSource = remember { MutableInteractionSource() }

Row(
modifier = modifier.clickable(
interactionSource = interactionSource,
indication = null,
role = Role.Checkbox,
enabled = enabled
) {
internalChecked = !internalChecked
onCheckedChange(internalChecked)
}.padding(vertical = 6.dp),
modifier = modifier
.clickable(
interactionSource = interactionSource,
indication = null,
role = Role.Checkbox,
enabled = enabled
) {
internalChecked = !internalChecked
onCheckedChange(internalChecked)
}
.padding(vertical = 6.dp),
) {
Column(
modifier = Modifier.weight(1f).padding(end = 16.dp)
modifier = Modifier
.weight(1f)
.padding(end = 16.dp)
) {
Text(text = text, style = textStyle)
if (description != null) {
Expand All @@ -80,3 +84,44 @@ fun SwitchView(
)
}
}

@Composable
fun BasicSwitchWithText(
text: String,
checked: Boolean,
onCheckedChange: ((Boolean) -> Unit),
modifier: Modifier = Modifier,
textStyle: TextStyle = MaterialTheme.typography.body1,
enabled: Boolean = true,
) {
var internalChecked by rememberSaveable { mutableStateOf(checked) }
val interactionSource = remember { MutableInteractionSource() }

Row(
modifier = modifier
.clickable(
interactionSource = interactionSource,
indication = null,
role = Role.Checkbox,
enabled = enabled
) {
internalChecked = !internalChecked
onCheckedChange(internalChecked)
}
.padding(vertical = 6.dp),
) {
Switch(
checked = checked,
onCheckedChange = null,
modifier = Modifier
.enableOrFade(enabled)
.offset(y = (-2).dp)
.indication(
interactionSource = interactionSource,
indication = rememberRipple(bounded = false, color = if (isDarkTheme) gray300 else gray600, radius = 28.dp)
)
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = text, style = textStyle)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ fun BottomBar(
Box(
modifier
.fillMaxWidth()
.height(78.dp)
.height(82.dp)
.clip(RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp))
.background(MaterialTheme.colors.surface)
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,38 @@ package fr.acinq.phoenix.android.payments

import android.Manifest
import android.content.Intent
import android.net.*
import android.provider.*
import android.net.Uri
import android.provider.Settings
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
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.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
Expand All @@ -41,23 +59,35 @@ import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.permissions.*
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import com.google.zxing.BarcodeFormat
import com.google.zxing.ResultPoint
import com.google.zxing.client.android.Intents
import com.journeyapps.barcodescanner.BarcodeCallback
import com.journeyapps.barcodescanner.BarcodeResult
import com.journeyapps.barcodescanner.DecoratedBarcodeView
import fr.acinq.phoenix.android.*
import fr.acinq.phoenix.android.CF
import fr.acinq.phoenix.android.R
import fr.acinq.phoenix.android.components.*
import fr.acinq.phoenix.android.business
import fr.acinq.phoenix.android.components.Button
import fr.acinq.phoenix.android.components.Card
import fr.acinq.phoenix.android.components.Clickable
import fr.acinq.phoenix.android.components.Dialog
import fr.acinq.phoenix.android.components.PhoenixIcon
import fr.acinq.phoenix.android.components.ProgressView
import fr.acinq.phoenix.android.components.TextInput
import fr.acinq.phoenix.android.components.VSeparator
import fr.acinq.phoenix.android.components.contact.ContactsListModal
import fr.acinq.phoenix.android.components.mvi.MVIControllerViewModel
import fr.acinq.phoenix.android.components.mvi.MVIView
import fr.acinq.phoenix.android.controllerFactory
import fr.acinq.phoenix.android.databinding.ScanViewBinding
import fr.acinq.phoenix.android.payments.offer.SendOfferView
import fr.acinq.phoenix.android.payments.spliceout.SendSpliceOutView
import fr.acinq.phoenix.android.utils.*
import fr.acinq.phoenix.android.utils.readClipboard
import fr.acinq.phoenix.controllers.ControllerFactory
import fr.acinq.phoenix.controllers.ScanController
import fr.acinq.phoenix.controllers.payments.Scan
Expand Down Expand Up @@ -108,7 +138,8 @@ fun ScanDataView(
initialInput = ""
postIntent(Scan.Intent.Reset)
},
onScannedText = { postIntent(Scan.Intent.Parse(request = it)) }
onScannedText = { postIntent(Scan.Intent.Parse(request = it)) },
onBackClick = onBackClick,
)
}
is Scan.Model.Bolt11InvoiceFlow.Bolt11InvoiceRequest -> {
Expand Down Expand Up @@ -188,6 +219,7 @@ fun ReadDataView(
model: Scan.Model,
onFeedbackDismiss: () -> Unit,
onScannedText: (String) -> Unit,
onBackClick: () -> Unit,
) {
val context = LocalContext.current.applicationContext

Expand All @@ -213,32 +245,42 @@ fun ReadDataView(
}

// buttons at the bottom of the screen
Column(
Modifier
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(24.dp)
.clip(RoundedCornerShape(24.dp))
.background(MaterialTheme.colors.surface)
.height(IntrinsicSize.Min),
verticalAlignment = Alignment.CenterVertically,
) {
if (model is Scan.Model.Ready) {
Button(
modifier = Modifier.fillMaxWidth(),
icon = R.drawable.ic_user_search,
text = "My contacts...",
onClick = { showContactsList = true },
)
Button(
modifier = Modifier.fillMaxWidth(),
icon = R.drawable.ic_clipboard,
text = stringResource(id = R.string.scan_paste_button),
onClick = { readClipboard(context)?.let { onScannedText(it) } },
)
Button(
modifier = Modifier.fillMaxWidth(),
icon = R.drawable.ic_input,
text = stringResource(id = R.string.scan_manual_input_button),
onClick = { showManualInputDialog = true },
)
BackHandler(onBack = onBackClick)
Button(icon = R.drawable.ic_arrow_back, onClick = onBackClick, modifier = Modifier.fillMaxHeight(), padding = PaddingValues(horizontal = 12.dp))
VSeparator(height = 48.dp)
Column(modifier = Modifier.weight(1f)) {
if (model is Scan.Model.Ready) {
Button(
modifier = Modifier.fillMaxWidth(),
icon = R.drawable.ic_user_search,
text = stringResource(id = R.string.settings_contacts),
onClick = { showContactsList = true },
maxLines = 1,
)
Button(
modifier = Modifier.fillMaxWidth(),
icon = R.drawable.ic_clipboard,
text = stringResource(id = R.string.lipsum_short),
onClick = { readClipboard(context)?.let { onScannedText(it) } },
maxLines = 1,
)
Button(
modifier = Modifier.fillMaxWidth(),
icon = R.drawable.ic_input,
text = stringResource(id = R.string.scan_manual_input_button),
onClick = { showManualInputDialog = true },
maxLines = 1,
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,12 +301,10 @@ private fun DetailsForLightningOutgoingPayment(
val details = payment.details
val status = payment.status

// -- recipient's public key
TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_pubkey_label), value = payment.recipient.toHex())

// -- details of the payment
when (details) {
is LightningOutgoingPayment.Details.Normal -> {
TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_pubkey_label), value = payment.recipient.toHex())
Bolt11InvoiceSection(invoice = details.paymentRequest)
}
is LightningOutgoingPayment.Details.SwapOut -> {
Expand Down Expand Up @@ -529,7 +527,6 @@ private fun Bolt11InvoiceSection(
Text(text = description)
}
}

TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payment_hash_label), value = invoice.paymentHash.toHex())
TechnicalRowSelectable(label = stringResource(id = R.string.paymentdetails_payment_request_label), value = invoice.write())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import fr.acinq.phoenix.android.business
import fr.acinq.phoenix.android.components.AmountHeroInput
import fr.acinq.phoenix.android.components.AmountWithFiatRowView
import fr.acinq.phoenix.android.components.BackButtonWithBalance
import fr.acinq.phoenix.android.components.BasicSwitchWithText
import fr.acinq.phoenix.android.components.Clickable
import fr.acinq.phoenix.android.components.FilledButton
import fr.acinq.phoenix.android.components.ProgressView
Expand All @@ -79,7 +80,7 @@ fun SendOfferView(
val balance = business.balanceManager.balance.collectAsState(null).value
val prefBitcoinUnit = LocalBitcoinUnit.current

val vm = viewModel<SendOfferViewModel>(factory = SendOfferViewModel.Factory(business.peerManager, business.nodeParamsManager))
val vm = viewModel<SendOfferViewModel>(factory = SendOfferViewModel.Factory(offer, business.contactsManager, business.peerManager, business.nodeParamsManager))
val requestedAmount = offer.amount
var amount by remember { mutableStateOf(requestedAmount) }
val amountErrorMessage: String = remember(amount) {
Expand Down Expand Up @@ -157,12 +158,20 @@ fun SendOfferView(
AmountWithFiatRowView(amount = trampolineFees.calculateFees(amt))
}
}

Spacer(modifier = Modifier.height(16.dp))
SplashLabelRow(label = stringResource(id = R.string.send_offer_payer_key_label), helpMessage = stringResource(id = R.string.send_offer_payer_key_help)) {
vm.useOfferKey?.let {
BasicSwitchWithText(text = if (it) "Use your key" else "Use a random key", checked = it, onCheckedChange = { vm.useOfferKey = it })
} ?: ProgressView(text = stringResource(id = R.string.utils_loading_data))
}
Spacer(modifier = Modifier.height(36.dp))

SendOfferStateButton(
state = vm.state,
offer = offer,
amount = amount,
useOfferKey = vm.useOfferKey,
isAmountInError = amountErrorMessage.isNotBlank(),
onSendClick = { amount, offer -> vm.sendOffer(amount, message, offer) },
onPaymentSent = onPaymentSent,
Expand All @@ -179,6 +188,7 @@ private fun SendOfferStateButton(
state: OfferState,
offer: OfferTypes.Offer,
amount: MilliSatoshi?,
useOfferKey: Boolean?,
isAmountInError: Boolean,
onSendClick: (MilliSatoshi, OfferTypes.Offer) -> Unit,
onPaymentSent: () -> Unit,
Expand Down Expand Up @@ -210,7 +220,7 @@ private fun SendOfferStateButton(
stringResource(id = R.string.send_pay_button)
},
icon = R.drawable.ic_send,
enabled = mayDoPayments && amount != null && !isAmountInError,
enabled = mayDoPayments && amount != null && useOfferKey != null && !isAmountInError,
) {
amount?.let { onSendClick(it, offer) }
}
Expand Down
Loading

0 comments on commit ac8ec65

Please sign in to comment.