Skip to content

Commit

Permalink
Add fee/liquidity disclaimers in several screens (#539)
Browse files Browse the repository at this point in the history
When requesting liquidity, a confirmation message is displayed
with a cancel/confirm button. The message clarifies that the
liquidity will be consumed when receiving payments. The help
message for the duration has also been updated.

A confirmation message has been also added in the splice-out 
screen, to clarify that sending funds on-chain does impact the 
capacity of the channel (but not liquidity).

---------

Co-authored-by: Robbie Hanson <[email protected]>
  • Loading branch information
dpad85 and robbiehanson authored May 17, 2024
1 parent 1ca073a commit 3275c84
Show file tree
Hide file tree
Showing 24 changed files with 721 additions and 242 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ private fun DisclaimerView(
Text(stringResource(R.string.restore_disclaimer_message))
}
Checkbox(
text = stringResource(R.string.restore_disclaimer_checkbox),
text = stringResource(R.string.utils_ack),
checked = hasCheckedWarning,
onCheckedChange = { hasCheckedWarning = it },
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -57,18 +64,22 @@ import fr.acinq.phoenix.android.components.AmountView
import fr.acinq.phoenix.android.components.AmountWithFiatBelow
import fr.acinq.phoenix.android.components.BackButtonWithBalance
import fr.acinq.phoenix.android.components.BorderButton
import fr.acinq.phoenix.android.components.Button
import fr.acinq.phoenix.android.components.Checkbox
import fr.acinq.phoenix.android.components.FilledButton
import fr.acinq.phoenix.android.components.HSeparator
import fr.acinq.phoenix.android.components.IconPopup
import fr.acinq.phoenix.android.components.ProgressView
import fr.acinq.phoenix.android.components.SatoshiSlider
import fr.acinq.phoenix.android.components.SplashLabelRow
import fr.acinq.phoenix.android.components.SplashLayout
import fr.acinq.phoenix.android.components.enableOrFade
import fr.acinq.phoenix.android.components.feedback.ErrorMessage
import fr.acinq.phoenix.android.components.feedback.InfoMessage
import fr.acinq.phoenix.android.components.feedback.SuccessMessage
import fr.acinq.phoenix.android.payments.spliceout.spliceFailureDetails
import fr.acinq.phoenix.android.utils.Converter.toPrettyString
import fr.acinq.phoenix.android.utils.annotatedStringResource

object LiquidityLimits {
val liquidityOptions = arrayOf(
Expand Down Expand Up @@ -213,17 +224,11 @@ private fun RequestLiquidityBottomSection(
Spacer(modifier = Modifier.height(24.dp))
if (state.fees.serviceFee + state.fees.miningFee.toMilliSatoshi() > balance) {
ErrorMessage(header = stringResource(id = R.string.liquidityads_over_balance))
} else if (isAmountError) {
ErrorMessage(header = stringResource(id = R.string.validation_invalid_amount))
} else {
FilledButton(
text = stringResource(id = R.string.liquidityads_request_button),
icon = R.drawable.ic_check_circle,
enabled = !isAmountError,
onClick = {
vm.requestInboundLiquidity(
amount = state.amount,
feerate = state.actualFeerate,
)
},
ReviewLiquidityRequest(
onConfirm = { vm.requestInboundLiquidity(amount = state.amount, feerate = state.actualFeerate) }
)
}
}
Expand Down Expand Up @@ -306,10 +311,69 @@ private fun LeaseEstimationView(
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ReviewLiquidityRequest(
onConfirm: () -> Unit,
) {
val sheetState = rememberModalBottomSheetState()
var showSheet by remember { mutableStateOf(false) }
var confirmLiquidity by remember { mutableStateOf(false) }
if (showSheet) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = {
// executed when user click outside the sheet, and after sheet has been hidden thru state.
showSheet = false
},
modifier = Modifier.heightIn(max = 700.dp),
containerColor = MaterialTheme.colors.surface,
contentColor = MaterialTheme.colors.onSurface,
scrimColor = MaterialTheme.colors.onBackground.copy(alpha = 0.1f),
) {
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(top = 0.dp, start = 24.dp, end = 24.dp, bottom = 50.dp)
) {
Text(text = annotatedStringResource(id = R.string.liquidityads_disclaimer_body1))
Spacer(modifier = Modifier.height(8.dp))
Text(text = stringResource(id = R.string.liquidityads_disclaimer_body2))
Spacer(modifier = Modifier.height(8.dp))
Checkbox(text = stringResource(id = R.string.utils_ack), checked = confirmLiquidity, onCheckedChange = { confirmLiquidity = it })

Spacer(modifier = Modifier.height(24.dp))
FilledButton(
text = stringResource(id = R.string.btn_confirm),
icon = R.drawable.ic_check,
onClick = onConfirm,
enabled = confirmLiquidity,
modifier = Modifier.align(Alignment.End),
)
Button(
text = stringResource(id = R.string.btn_cancel),
onClick = { showSheet = false },
shape = CircleShape,
modifier = Modifier.align(Alignment.End),
)
}
}
}

BorderButton(
text = stringResource(id = R.string.liquidityads_review_button),
icon = R.drawable.ic_text,
onClick = { showSheet = true },
modifier = Modifier.enableOrFade(!showSheet)
)
}

@Composable
private fun LeaseSuccessDetails(liquidityDetails: ChannelCommand.Commitment.Splice.Response.Created) {
SuccessMessage(
header = stringResource(id = R.string.liquidityads_success),
details = "You added ${liquidityDetails.liquidityLease?.amount?.toPrettyString(unit = LocalBitcoinUnit.current, withUnit = true)}"
details = liquidityDetails.liquidityLease?.amount?.let {
stringResource(id = R.string.liquidityads_success_amount, it.toPrettyString(unit = LocalBitcoinUnit.current, withUnit = true))
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,6 @@ fun BitcoinAddressView(
HSeparator(width = 50.dp)
Spacer(modifier = Modifier.height(16.dp))
QRCodeDetail(label = stringResource(id = R.string.receive_bitcoin_address_label), value = state.currentAddress)

val isFromLegacy by LegacyPrefsDatastore.hasMigratedFromLegacy(context).collectAsState(initial = false)
if (isFromLegacy) {
Spacer(modifier = Modifier.height(24.dp))
InfoMessage(
header = stringResource(id = R.string.receive_onchain_legacy_warning_title),
details = stringResource(id = R.string.receive_onchain_legacy_warning),
detailsStyle = MaterialTheme.typography.subtitle2,
alignment = Alignment.CenterHorizontally
)
}
}
is BitcoinAddressState.Error -> {
ErrorMessage(
Expand Down
Loading

0 comments on commit 3275c84

Please sign in to comment.