From 3275c84cc2a5b16a4edf496a6cde694c2949dc65 Mon Sep 17 00:00:00 2001 From: Dominique Padiou <5765435+dpad85@users.noreply.github.com> Date: Fri, 17 May 2024 14:58:46 +0200 Subject: [PATCH] Add fee/liquidity disclaimers in several screens (#539) 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 <304604+robbiehanson@users.noreply.github.com> --- .../phoenix/android/init/RestoreWalletView.kt | 2 +- .../liquidity/RequestLiquidityView.kt | 86 ++++++- .../payments/receive/ReceiveOnChainView.kt | 11 - .../payments/spliceout/SpliceOutView.kt | 217 ++++++++++++++---- .../utils/datastore/InternalDataRepository.kt | 4 + .../res/values-b+es+419/important_strings.xml | 24 +- .../main/res/values-cs/important_strings.xml | 23 +- .../main/res/values-de/important_strings.xml | 24 +- .../main/res/values-es/important_strings.xml | 24 +- .../main/res/values-fr/important_strings.xml | 24 +- .../res/values-pt-rBR/important_strings.xml | 24 +- .../main/res/values-sk/important_strings.xml | 24 +- .../main/res/values-vi/important_strings.xml | 24 +- .../src/main/res/values/important_strings.xml | 31 ++- .../phoenix-ios.xcodeproj/project.pbxproj | 8 + phoenix-ios/phoenix-ios/Localizable.xcstrings | 15 ++ phoenix-ios/phoenix-ios/prefs/Prefs.swift | 14 +- .../LiquidityAdsView.swift | 64 +++++- .../phoenix-ios/views/layers/Popover.swift | 5 +- .../phoenix-ios/views/layers/ShortSheet.swift | 5 +- .../views/send/ChannelSizeImpactWarning.swift | 105 +++++++++ .../views/send/LowMinerFeeWarning.swift | 94 ++++++++ .../views/send/MinerFeeSheet.swift | 88 ------- .../phoenix-ios/views/send/ValidateView.swift | 23 ++ 24 files changed, 721 insertions(+), 242 deletions(-) create mode 100644 phoenix-ios/phoenix-ios/views/send/ChannelSizeImpactWarning.swift create mode 100644 phoenix-ios/phoenix-ios/views/send/LowMinerFeeWarning.swift diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/init/RestoreWalletView.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/init/RestoreWalletView.kt index 9c849ca3d..e22e30cc0 100644 --- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/init/RestoreWalletView.kt +++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/init/RestoreWalletView.kt @@ -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 }, ) diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/liquidity/RequestLiquidityView.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/liquidity/RequestLiquidityView.kt index f21d19829..466e20a98 100644 --- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/liquidity/RequestLiquidityView.kt +++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/liquidity/RequestLiquidityView.kt @@ -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 @@ -57,6 +64,8 @@ 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 @@ -64,11 +73,13 @@ 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( @@ -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) } ) } } @@ -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)) + } ) } \ No newline at end of file diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/receive/ReceiveOnChainView.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/receive/ReceiveOnChainView.kt index e44cdc208..e04e6d977 100644 --- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/receive/ReceiveOnChainView.kt +++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/receive/ReceiveOnChainView.kt @@ -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( diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/spliceout/SpliceOutView.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/spliceout/SpliceOutView.kt index de5b4beb0..09fabe68c 100644 --- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/spliceout/SpliceOutView.kt +++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/payments/spliceout/SpliceOutView.kt @@ -16,13 +16,21 @@ package fr.acinq.phoenix.android.payments.spliceout +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.* +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.selection.SelectionContainer +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.* import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -42,13 +50,14 @@ import fr.acinq.phoenix.android.R import fr.acinq.phoenix.android.business import fr.acinq.phoenix.android.components.* import fr.acinq.phoenix.android.components.feedback.ErrorMessage +import fr.acinq.phoenix.android.internalData import fr.acinq.phoenix.android.utils.Converter.toPrettyString import fr.acinq.phoenix.android.utils.annotatedStringResource import fr.acinq.phoenix.data.BitcoinUnit import fr.acinq.phoenix.data.MempoolFeerate +import kotlinx.coroutines.launch -@OptIn(ExperimentalComposeUiApi::class) @Composable fun SendSpliceOutView( requestedAmount: Satoshi?, @@ -86,10 +95,14 @@ fun SendSpliceOutView( balance != null && newAmount > balance.truncateToSatoshi() -> { amountErrorMessage = context.getString(R.string.send_error_amount_over_balance) } + requestedAmount != null && newAmount < requestedAmount -> { - amountErrorMessage = context.getString(R.string.send_error_amount_below_requested, - (requestedAmount).toMilliSatoshi().toPrettyString(prefBtcUnit, withUnit = true)) + amountErrorMessage = context.getString( + R.string.send_error_amount_below_requested, + (requestedAmount).toMilliSatoshi().toPrettyString(prefBtcUnit, withUnit = true) + ) } + newAmount < 546.sat -> { amountErrorMessage = context.getString(R.string.validation_below_min, 546.sat.toPrettyString(BitcoinUnit.Sat, withUnit = true)) } @@ -144,10 +157,12 @@ fun SendSpliceOutView( } ) } + is SpliceOutState.Preparing -> { Spacer(modifier = Modifier.height(24.dp)) ProgressView(text = stringResource(id = R.string.send_spliceout_prepare_in_progress)) } + is SpliceOutState.ReadyToSend -> { SpliceOutReadyView( state = state, @@ -158,13 +173,16 @@ fun SendSpliceOutView( onExecute = { vm.executeSpliceOut(state.userAmount, state.actualFeerate, address) }, ) } + is SpliceOutState.Executing -> { Spacer(modifier = Modifier.height(24.dp)) ProgressView(text = stringResource(id = R.string.send_spliceout_execute_in_progress)) } + is SpliceOutState.Complete.Success -> { LaunchedEffect(key1 = Unit) { onSpliceOutSuccess() } } + is SpliceOutState.Complete.Failure -> { Spacer(modifier = Modifier.height(24.dp)) ErrorMessage( @@ -187,6 +205,7 @@ private fun SpliceOutErrorView(state: SpliceOutState) { alignment = Alignment.CenterHorizontally, ) } + is SpliceOutState.Error.NoChannels -> { ErrorMessage( header = stringResource(id = R.string.send_spliceout_error_failure), @@ -194,6 +213,7 @@ private fun SpliceOutErrorView(state: SpliceOutState) { alignment = Alignment.CenterHorizontally, ) } + else -> {} } } @@ -217,56 +237,16 @@ private fun SpliceOutReadyView( Spacer(modifier = Modifier.height(24.dp)) - var showDisclaimer by remember { mutableStateOf(false) } - if (showDisclaimer) { - Dialog( - onDismiss = { showDisclaimer = false }, - buttons = null, - title = stringResource(id = R.string.send_low_feerate_dialog_title) - ) { - Column(modifier = Modifier.padding(horizontal = 24.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { - Text(text = annotatedStringResource(id = R.string.send_low_feerate_dialog_body1)) - Text(text = stringResource(id = R.string.send_low_feerate_dialog_body2)) - Text(text = stringResource(id = R.string.send_low_feerate_dialog_body3)) - } - Spacer(modifier = Modifier.height(16.dp)) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.End - ) { - Button( - text = stringResource(id = R.string.btn_cancel), - onClick = { showDisclaimer = false }, - ) - Button( - text = stringResource(id = R.string.btn_confirm), - onClick = onExecute, - icon = R.drawable.ic_check_circle, - space = 8.dp, - ) - } - } - } - if (balance == null || total.toMilliSatoshi() > balance) { ErrorMessage( header = stringResource(R.string.send_spliceout_error_cannot_afford_fees), alignment = Alignment.CenterHorizontally, ) } else { - FilledButton( - text = if (!mayDoPayments) stringResource(id = R.string.send_connecting_button) else stringResource(id = R.string.send_pay_button), - icon = R.drawable.ic_send, - enabled = mayDoPayments && isAmountValid, - onClick = { - if (mempoolFeerate != null && FeeratePerByte(state.userFeerate).feerate < mempoolFeerate.hour.feerate) { - showDisclaimer = true - } else { - showDisclaimer = false - onExecute() - } - }, - ) + // low feerate == below 1 hour estimate + val isUsingLowFeerate = mempoolFeerate != null && FeeratePerByte(state.userFeerate).feerate < mempoolFeerate.hour.feerate + val showSpliceoutCapacityDisclaimer = internalData.getSpliceoutCapacityDisclaimer.collectAsState(initial = true).value + ReviewSpliceOutAndConfirm(onExecute, mayDoPayments, isAmountValid, isUsingLowFeerate, showSpliceoutCapacityDisclaimer) } } @@ -289,6 +269,147 @@ private fun SpliceOutFeeSummaryView( // TODO: show a warning if the fee is too large } +@Composable +fun ColumnScope.LowFeerateWarning(onConfirm: () -> Unit, onCancel: () -> Unit) { + Text(text = stringResource(id = R.string.spliceout_low_feerate_dialog_title), style = MaterialTheme.typography.h4) + Spacer(modifier = Modifier.height(12.dp)) + Text(text = annotatedStringResource(id = R.string.spliceout_low_feerate_dialog_body1)) + Spacer(modifier = Modifier.height(8.dp)) + Text(text = stringResource(id = R.string.spliceout_low_feerate_dialog_body2)) + + Spacer(modifier = Modifier.height(24.dp)) + FilledButton( + text = stringResource(id = R.string.btn_confirm), + onClick = onConfirm, + icon = R.drawable.ic_check_circle, + space = 8.dp, + modifier = Modifier.align(Alignment.End), + ) + Spacer(modifier = Modifier.height(8.dp)) + Button( + text = stringResource(id = R.string.btn_cancel), + onClick = onCancel, + modifier = Modifier.align(Alignment.End), + shape = CircleShape, + ) +} + +@Composable +fun ColumnScope.SpliceOutCapacityDisclaimer(onConfirm: () -> Unit, onCancel: () -> Unit) { + val scope = rememberCoroutineScope() + var skipDisclaimerChecked by remember { mutableStateOf(false) } + val internalData = internalData + + Text(text = stringResource(id = R.string.spliceout_capacity_disclaimer_title), style = MaterialTheme.typography.h4) + Spacer(modifier = Modifier.height(12.dp)) + Text(text = stringResource(id = R.string.spliceout_capacity_disclaimer_body1)) + Spacer(modifier = Modifier.height(8.dp)) + Checkbox( + text = stringResource(id = R.string.spliceout_capacity_disclaimer_checkbox), + checked = skipDisclaimerChecked, + onCheckedChange = { + skipDisclaimerChecked = it + scope.launch { internalData.saveSpliceoutCapacityDisclaimer(!it) } + }, + ) + + Spacer(modifier = Modifier.height(24.dp)) + FilledButton( + text = stringResource(id = R.string.btn_confirm), + onClick = onConfirm, + icon = R.drawable.ic_check_circle, + space = 8.dp, + modifier = Modifier.align(Alignment.End), + ) + Spacer(modifier = Modifier.height(8.dp)) + Button( + text = stringResource(id = R.string.btn_cancel), + onClick = onCancel, + modifier = Modifier.align(Alignment.End), + shape = CircleShape, + ) +} + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) +@Composable +private fun ReviewSpliceOutAndConfirm( + onExecute: () -> Unit, + mayDoPayments: Boolean, + isAmountValid: Boolean, + isUsingLowFeerate: Boolean, + showSpliceoutCapacityDisclaimer: Boolean, +) { + val scope = rememberCoroutineScope() + val sheetState = rememberModalBottomSheetState() + var showSheet 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), + ) { + val pagerState = rememberPagerState( + initialPage = when { + isUsingLowFeerate && showSpliceoutCapacityDisclaimer -> 0 + isUsingLowFeerate -> 0 + showSpliceoutCapacityDisclaimer -> 1 + else -> 2 + }, + pageCount = { 2 }, + ) + HorizontalPager( + modifier = Modifier.fillMaxHeight(), + state = pagerState, + verticalAlignment = Alignment.Top, + userScrollEnabled = false, + ) { pageIndex -> + Column( + modifier = Modifier + .verticalScroll(rememberScrollState()) + .padding(top = 0.dp, start = 24.dp, end = 24.dp, bottom = 50.dp) + ) { + when (pageIndex) { + 0 -> LowFeerateWarning( + onConfirm = { + if (showSpliceoutCapacityDisclaimer) { + scope.launch { pagerState.scrollToPage(1, 0f) } + } else { + onExecute() + } + }, + onCancel = { showSheet = false } + ) + 1 -> SpliceOutCapacityDisclaimer(onConfirm = onExecute, onCancel = { showSheet = false }) + else -> showSheet = false + } + } + } + } + } + + FilledButton( + text = if (!mayDoPayments) stringResource(id = R.string.send_connecting_button) else stringResource(id = R.string.send_pay_button), + icon = R.drawable.ic_send, + enabled = mayDoPayments && isAmountValid, + modifier = Modifier.enableOrFade(!showSheet), + onClick = { + if (isUsingLowFeerate || showSpliceoutCapacityDisclaimer) { + showSheet = true + } else { + showSheet = false + onExecute() + } + }, + ) +} + @Composable fun spliceFailureDetails(spliceFailure: ChannelCommand.Commitment.Splice.Response.Failure): String = when (spliceFailure) { is ChannelCommand.Commitment.Splice.Response.Failure.AbortedByPeer -> stringResource(id = R.string.splice_error_aborted_by_peer, spliceFailure.reason) diff --git a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/datastore/InternalDataRepository.kt b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/datastore/InternalDataRepository.kt index d600a84ab..33eb7c6f0 100644 --- a/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/datastore/InternalDataRepository.kt +++ b/phoenix-android/src/main/kotlin/fr/acinq/phoenix/android/utils/datastore/InternalDataRepository.kt @@ -54,6 +54,7 @@ class InternalDataRepository(private val internalData: DataStore) { private val CHANNELS_WATCHER_OUTCOME = stringPreferencesKey("CHANNELS_WATCHER_RESULT") private val LAST_USED_SWAP_INDEX = intPreferencesKey("LAST_USED_SWAP_INDEX") private val INFLIGHT_PAYMENTS_COUNT = intPreferencesKey("INFLIGHT_PAYMENTS_COUNT") + private val SHOW_SPLICEOUT_CAPACITY_DISCLAIMER = booleanPreferencesKey("SHOW_SPLICEOUT_CAPACITY_DISCLAIMER") private val REMOTE_WALLET_NOTICE_READ_INDEX = intPreferencesKey("REMOTE_WALLET_NOTICE_READ_INDEX") } @@ -137,6 +138,9 @@ class InternalDataRepository(private val internalData: DataStore) { val getInFlightPaymentsCount: Flow = safeData.map { it[INFLIGHT_PAYMENTS_COUNT] ?: 0 } suspend fun saveInFlightPaymentsCount(count: Int) = internalData.edit { it[INFLIGHT_PAYMENTS_COUNT] = count } + val getSpliceoutCapacityDisclaimer: Flow = safeData.map { it[SHOW_SPLICEOUT_CAPACITY_DISCLAIMER] ?: true } + suspend fun saveSpliceoutCapacityDisclaimer(show: Boolean) = internalData.edit { it[SHOW_SPLICEOUT_CAPACITY_DISCLAIMER] = show } + val getLastReadWalletNoticeIndex: Flow = safeData.map { it[REMOTE_WALLET_NOTICE_READ_INDEX] ?: -1 } suspend fun saveLastReadWalletNoticeIndex(index: Int) = internalData.edit { it[REMOTE_WALLET_NOTICE_READ_INDEX] = index } } \ No newline at end of file diff --git a/phoenix-android/src/main/res/values-b+es+419/important_strings.xml b/phoenix-android/src/main/res/values-b+es+419/important_strings.xml index 3ac649ef7..f39b2efaf 100644 --- a/phoenix-android/src/main/res/values-b+es+419/important_strings.xml +++ b/phoenix-android/src/main/res/values-b+es+419/important_strings.xml @@ -90,7 +90,7 @@ Restaurar mi billetera Phoenix solo puede restaurar los fondos fuera de la cadena que hayan sido gestionados por esta aplicación.\n\nSi importas una semilla que NO haya sido creada por Phoenix, no se restaurará ningún fondo.\n\nAsegúrate además de no estar usando otra billetera Phoenix con la misma semilla. - Entiendo. + Entiendo. La semilla de la billetera es una lista de 12 palabras en inglés. Escribe cada palabra de esta lista en la siguiente casilla. Ingresa la palabra #%1$s @@ -128,9 +128,6 @@ La liquidez entrante es insuficiente y tienes desactivada la gestión automatizada de canales. - Aviso de migración - Esta es tu nueva dirección. No reutilices las direcciones antiguas de Bitcoin que tenías antes de la migración. - Esta factura está vencida. @@ -335,10 +332,15 @@ La comisión total es superior al 25% del importe solicitado. La comisión total supera el saldo. + Validar + Estás solicitando una cantidad inicial de liquidez. La liquidez no es constante en el tiempo: a medida que reciba fondos a lo largo de Lightning, la liquidez se consumirá y se convertirá en su saldo. + Al cabo de un año, la liquidez restante no utilizada será reclamada por el servicio. + Aceptar Procesando empalme… La liquidez se agregó correctamente. + Importe añadido: %1$s Error al solicitar liquidez Los canales no están disponibles. Vuelve a intentarlo más tarde. @@ -360,9 +362,15 @@ - ¡Baja comisión! - Los pagos con comisiones bajas pueden pasar días o semanas sin confirmarse. - La selección de comisiones es su responsabilidad. Una vez enviado, este pago no puede ser cancelado, sólo acelerado con comisiones más altas. - ¿Está seguro de que desea continuar? + ¡Baja comisión! + Los pagos con comisiones bajas pueden pasar días o semanas sin confirmarse. + La selección de comisiones es su responsabilidad. Una vez enviado, este pago no puede ser cancelado, sólo acelerado con comisiones más altas. + ¿Está seguro de que desea continuar? + + + + Tamaño del canal afectado + Los fondos enviados se toman de tu lado del canal, reduciendo el tamaño del canal en la misma cantidad. Su liquidez entrante permanece sin cambios. + No volver a mostrar ese mensaje. diff --git a/phoenix-android/src/main/res/values-cs/important_strings.xml b/phoenix-android/src/main/res/values-cs/important_strings.xml index 1805cb707..c2dd2469e 100644 --- a/phoenix-android/src/main/res/values-cs/important_strings.xml +++ b/phoenix-android/src/main/res/values-cs/important_strings.xml @@ -93,7 +93,7 @@ Obnovit moji peněženku Phoenix může obnovit pouze Off-Chain prostředky, které byly spravovány touto aplikací.\n\nImportem Seedu, který NEBYL vytvořen aplikací Phoenix, se neobnoví žádné prostředky.\n\nUjistěte se také, že již není spuštěna jiná aplikace Phoenix se stejným Seedem. - Rozumím. + Rozumím. Seed vaší peněženky je seznam 12 anglických slov. Každé slovo z tohoto seznamu napište do níže uvedeného pole. Zadejte slovo #%1$s @@ -130,9 +130,6 @@ Příchozí likvidita není dostatečná a máte zakázanou automatickou správu kanálů. - Oznámení o migraci - Toto je vaše nová adresa. Nepoužívejte znovu své staré Bitcoin adresy z doby před migrací! - Platnost této faktury vypršela. @@ -346,10 +343,15 @@ Celkový poplatek je vyšší než 25 % požadované částky. Celkové poplatky přesahují váš zůstatek. + Review + Žádáte o počáteční množství likvidity. Likvidita není v průběhu času konstantní: jak budete v průběhu Blesku přijímat finanční prostředky, likvidita se bude spotřebovávat a stane se vaším zůstatkem. + Po jednom roce bude zbývající nevyužitá likvidita službou vyžádána zpět. + Přijmout Zpracovává se splice… Likvidita byla úspěšně přidána! + Přidaná částka: %1$s Žádost o likviditu se nezdařila Kanály nejsou k dispozici. Zkuste to později. @@ -371,9 +373,14 @@ - Nízké poplatky! - Platby za nízké poplatky mohou trvat několik dní nebo týdnů bez potvrzení. - Výběr poplatků je na vaši zodpovědnost. Po odeslání nelze tuto platbu zrušit, pouze urychlit vyššími poplatky. - Jste si jisti, že chcete pokračovat? + Nízké poplatky! + Platby za nízké poplatky mohou trvat několik dní nebo týdnů bez potvrzení. + Výběr poplatků je na vaši zodpovědnost. Po odeslání nelze tuto platbu zrušit, pouze urychlit vyššími poplatky. + Jste si jisti, že chcete pokračovat? + + + Vplyv na veľkosť kanála + Financie odoslané sa odoberú z vašej strany kanála, čím sa veľkosť kanála zníži o rovnakú sumu. Vaša prichádzajúca likvidita zostáva nezmenená. + Túto správu už nezobrazujte. diff --git a/phoenix-android/src/main/res/values-de/important_strings.xml b/phoenix-android/src/main/res/values-de/important_strings.xml index 729f3a12d..af8b0f785 100644 --- a/phoenix-android/src/main/res/values-de/important_strings.xml +++ b/phoenix-android/src/main/res/values-de/important_strings.xml @@ -90,7 +90,7 @@ Meine Wallet wiederherstellen Phoenix kann nur off-chain-Guthaben wiederherstellen, das von dieser App verwaltet wurde.\n\nEin Seed, der nicht von Phoenix generiert wurde, kann nicht importiert werden.\n\nStellen Sie außerdem sicher, dass Sie nicht bereits eine andere Phoenix-Wallet mit demselben Seed betreiben. - Ich verstehe. + Ich verstehe. Der Seed für deine Wallet ist eine Liste mit 12 englischen Wörtern. Schreiben Sie jedes Wort dieser Liste in die folgende Box. Wort #%1$s eingeben @@ -127,9 +127,6 @@ Eingangskapazität ist unzureichend und automatisches Kanal-Management ist deaktiviert. - Hinweis zur Migration - Dies ist Ihre neue Adresse. Verwenden Sie Ihre alten Bitcoin-Adressen von vor der Migration nicht wieder! - Die Rechnung ist abgelaufen. @@ -344,10 +341,15 @@ Die Gesamtgebühr beträgt mehr als 25% des angeforderten Betrags. Die gesamten Gebühren übersteigen Ihr Guthaben. + Review + Sie fordern einen anfänglichen Betrag an Liquidität an. Die Liquidität ist im Laufe der Zeit nicht konstant: Wenn Sie über Lightning Geld erhalten, wird die Liquidität verbraucht und zu Ihrem Guthaben. + Nach einem Jahr wird die verbleibende ungenutzte Liquidität vom Dienst zurückverlangt. + Annehmen Bearbeitung des Splice… Liquidität erfolgreich hinzugefügt! + Betrag hinzugefügt: %1$s Liquiditätsanfrage ist fehlgeschlagen Kanäle sind nicht verfügbar. Versuchen Sie es später erneut. @@ -369,9 +371,15 @@ - Niedrige Gebühren! - Zahlungen mit niedrigen Gebühren können sich über Tage oder Wochen hinziehen, ohne bestätigt zu werden. - Die Auswahl der Gebühren liegt in Ihrer Verantwortung. Einmal abgeschickt, kann diese Zahlung nicht mehr storniert, sondern nur noch mit höheren Gebühren beschleunigt werden. - Sind Sie sicher, dass Sie fortfahren möchten? + Niedrige Gebühren! + Zahlungen mit niedrigen Gebühren können sich über Tage oder Wochen hinziehen, ohne bestätigt zu werden. + Die Auswahl der Gebühren liegt in Ihrer Verantwortung. Einmal abgeschickt, kann diese Zahlung nicht mehr storniert, sondern nur noch mit höheren Gebühren beschleunigt werden. + Sind Sie sicher, dass Sie fortfahren möchten? + + + + Kanalgröße beeinträchtigt + Die gesendeten Gelder werden von Ihrer Seite des Kanals genommen, wodurch die Kanalgröße um den gleichen Betrag reduziert wird. Ihre eingehende Liquidität bleibt unverändert. + Diese Meldung nicht mehr anzeigen. \ No newline at end of file diff --git a/phoenix-android/src/main/res/values-es/important_strings.xml b/phoenix-android/src/main/res/values-es/important_strings.xml index 635cec9af..a9deb70cf 100644 --- a/phoenix-android/src/main/res/values-es/important_strings.xml +++ b/phoenix-android/src/main/res/values-es/important_strings.xml @@ -55,7 +55,7 @@ Restaurar mi cartera Phoenix sólo puede restaurar los fondos fuera de la cadena que fueron gestionados por esta aplicación.\n\nImportar una semilla que no fue creada por Phoenix no restaurará ningún fondo.\n\nPor favor, asegúrese también de que no está ejecutando otra cartera Phoenix con la misma semilla. - Entiendo. + Entiendo. La semilla de tu cartera es una lista de 12 palabras en inglés. Escribe cada palabra de esta lista en la casilla de abajo. Introducir palabra #%1$s @@ -131,9 +131,6 @@ La liquidez entrante es insuficiente y tienes desactivada la gestión automatizada de canales. - Aviso de migración - Esta es su nueva dirección. No reutilice sus antiguas direcciones Bitcoin anteriores a la migración. - Esta factura ha caducado. @@ -347,10 +344,15 @@ La tasa total es superior al 25% del importe solicitado. El total de tasas supera tu saldo. + Validar + Estás solicitando una cantidad inicial de liquidez. La liquidez no es constante en el tiempo: a medida que reciba fondos a lo largo de Lightning, la liquidez se consumirá y se convertirá en su saldo. + Al cabo de un año, la liquidez restante no utilizada será reclamada por el servicio. + Aceptar Procesando empalme… ¡Liquidez añadida con éxito! + Importe añadido: %1$s Solicitud de liquidez fallida Los canales no están disponibles. Vuelva a intentarlo más tarde. @@ -372,9 +374,15 @@ - ¡Baja tasa! - Los pagos con tasas bajas pueden pasar días o semanas sin confirmarse. - La selección de comisiones es su responsabilidad. Una vez enviado, este pago no puede ser cancelado, sólo acelerado con tasas más altas. - ¿Está seguro de que desea continuar? + ¡Baja tasa! + Los pagos con tasas bajas pueden pasar días o semanas sin confirmarse. + La selección de comisiones es su responsabilidad. Una vez enviado, este pago no puede ser cancelado, sólo acelerado con tasas más altas. + ¿Está seguro de que desea continuar? + + + + Tamaño del canal afectado + Los fondos enviados se toman de tu lado del canal, reduciendo el tamaño del canal en la misma cantidad. Su liquidez entrante permanece sin cambios. + No volver a mostrar ese mensaje. \ No newline at end of file diff --git a/phoenix-android/src/main/res/values-fr/important_strings.xml b/phoenix-android/src/main/res/values-fr/important_strings.xml index e109bfad7..c0ea39178 100644 --- a/phoenix-android/src/main/res/values-fr/important_strings.xml +++ b/phoenix-android/src/main/res/values-fr/important_strings.xml @@ -93,7 +93,7 @@ Restaurer mon wallet Phoenix ne restaure que les fonds off-chain qu\'il avait déjà gérés.\n\nImporter une seed qui n\'a pas été créée par Phoenix ne restaurera aucun fonds.\n\nVeuillez également vous assurer que cette seed n\'est pas déjà utilisée sur un autre wallet Phoenix. - Je comprends. + Je comprends. La clé (ou seed) de votre wallet est une liste de 12 mots en anglais. Entrez chaque mot de cette liste dans la zone de saisie ci-dessous. Saisie du mot %1$s @@ -131,9 +131,6 @@ Votre liquidité entrante est insuffisante pour ce montant, et la gestion automatisée des channels est désactivée. - Note suite à la migration - Il s\'agit de votre nouvelle adresse. Ne réutilisez pas vos anciennes adresses Bitcoin d\'avant la migration ! - Cette requête a expiré. @@ -347,10 +344,15 @@ Le total des frais est supérieur à 25% du montant de liquidité demandé. Le total des frais dépasse votre solde. + Valider + Cette demande est pour un montant initial de liquidité. Cette liquidité n\'est pas constante dans le temps : au fur et à mesure que vous recevrez des fonds, elle sera consommée et deviendra votre solde. + Au bout d\'un an, la liquidité non consommée sera récupérée par le service. + Accepter Traitement du splice… Liquidité ajoutée avec succès! + Montant ajouté : %1$s La demande de liquidité a échoué Vos canaux ne sont pas disponibles. Réessayez plus tard. @@ -372,9 +374,15 @@ - Frais faibles! - Les transactions utilisant des frais trop faibles peuvent rester en attente de confirmation pendant des jours, voire des semaines. - Le choix des frais est de votre responsabilité. Une fois envoyé, ce paiement ne pourra pas être annulé, seulement accéléré en utilisant des frais plus élevés. - Êtes-vous sûr de vouloir continuer ? + Frais faibles! + Les transactions utilisant des frais trop faibles peuvent rester en attente de confirmation pendant des jours, voire des semaines. + Le choix des frais est de votre responsabilité. Une fois envoyé, ce paiement ne pourra pas être annulé, seulement accéléré en utilisant des frais plus élevés. + Êtes-vous sûr de vouloir continuer ? + + + + Capacité impactée + Les fonds envoyés proviennent de votre côté du canal, ce qui mécaniquement réduit le canal du même montant. La liquidité entrante reste inchangée. + Ne plus afficher ce message. \ No newline at end of file diff --git a/phoenix-android/src/main/res/values-pt-rBR/important_strings.xml b/phoenix-android/src/main/res/values-pt-rBR/important_strings.xml index 4e3a973d7..c032079b5 100644 --- a/phoenix-android/src/main/res/values-pt-rBR/important_strings.xml +++ b/phoenix-android/src/main/res/values-pt-rBR/important_strings.xml @@ -93,7 +93,7 @@ Restaurar minha carteira O Phoenix só pode restaurar fundos fora da cadeia que foram gerenciados por este aplicativo.\n\nA importação de uma semente que NÃO foi criada pelo Phoenix não restaurará nenhum fundo.\n\nVerifique também se você já não está executando outra carteira Phoenix com a mesma semente. - Eu entendo. + Eu entendo. A semente de sua carteira é uma lista de 12 palavras em inglês. Digite cada palavra dessa lista na caixa abaixo. Digite a palavra #%1$s @@ -129,9 +129,6 @@ A liquidez recebida é insuficiente e você desativou o gerenciamento automatizado de canais. - Aviso de migração - Esse é seu novo endereço. Não reutilize seus endereços Bitcoin antigos de antes da migração! - Esta fatura está expirada. @@ -346,10 +343,15 @@ A taxa total é superior a 25% do valor solicitado. O total de taxas excede seu saldo. + Validar + Você está solicitando uma quantia inicial de liquidez. A liquidez não é constante ao longo do tempo: à medida que você recebe fundos durante o Lightning, a liquidez será consumida e se tornará seu saldo. + Após um ano, a liquidez restante não utilizada será recuperada pelo serviço. + Aceitar Processando emenda… Liquidez adicionada com sucesso! + Montante adicionado: %1$s A solicitação de liquidez falhou Os canais não estão disponíveis. Tente novamente mais tarde. @@ -371,9 +373,15 @@ - Taxas baixas! - Os pagamentos com taxas baixas podem permanecer por dias ou semanas sem confirmação. - É sua responsabilidade selecionar a taxa. Uma vez enviado, esse pagamento não pode ser cancelado, somente acelerado com taxas mais altas. - Tem certeza de que deseja prosseguir? + Taxas baixas! + Os pagamentos com taxas baixas podem permanecer por dias ou semanas sem confirmação. + É sua responsabilidade selecionar a taxa. Uma vez enviado, esse pagamento não pode ser cancelado, somente acelerado com taxas mais altas. + Tem certeza de que deseja prosseguir? + + + + Tamanho do canal afetado + Os fundos enviados são retirados do seu lado do canal, reduzindo o tamanho do canal na mesma quantidade. Sua liquidez de entrada permanece inalterada. + Não mostre essa mensagem novamente. \ No newline at end of file diff --git a/phoenix-android/src/main/res/values-sk/important_strings.xml b/phoenix-android/src/main/res/values-sk/important_strings.xml index fb8edda5e..5f609b883 100644 --- a/phoenix-android/src/main/res/values-sk/important_strings.xml +++ b/phoenix-android/src/main/res/values-sk/important_strings.xml @@ -93,7 +93,7 @@ Obnoviť moju peňaženku Phoenix môže obnoviť len off-chain prostriedky, ktoré boli spravované touto aplikáciou.\n\nImportom obnovovacej frázy, ktorá NEBOLA vytvorená aplikáciou Phoenix, sa neobnovia žiadne prostriedky.\n\nUistite sa tiež, že už nie je spustená iná aplikácia Phoenix s tou istou obnovovacou frázou. - Rozumiem. + Rozumiem. Obnovovacia fráza (seed) vašej peňaženky je zoznam 12 anglických slov. Každé slovo z tohto zoznamu napíšte do nižšie uvedeného poľa. Zadajte slovo #%1$s @@ -130,9 +130,6 @@ Prichádzajúca likvidita nie je dostatočná a máte zakázanú automatickú správu kanálov. - Oznámenie o migrácii - Toto je vaša nová adresa. Neopakujte použitie svojich starých Bitcoin adres z doby pred migráciou! - Platnosť tejto faktúry vypršala. @@ -347,10 +344,15 @@ Celkový poplatok je vyšší ako 25 % požadovanej sumy. Celkové poplatky presahujú váš zostatok. + Preskúmanie žiadosti + Žiadate o počiatočnú sumu likvidity. Likvidita nie je v priebehu času konštantná: ako budete prijímať finančné prostriedky v priebehu Blesku, likvidita sa spotrebuje a stane sa vaším zostatkom. + Po jednom roku bude služba zvyšnú nevyužitú likviditu požadovať späť. + Prijať Spracúva sa splice… Likvidita bola úspešne pridaná! + Pridaná suma: %1$s Žiadosť o likviditu zlyhala Kanály nie sú k dispozícii. Skúste to neskôr. @@ -372,9 +374,15 @@ - Nízke poplatky! - Platby s nízkymi poplatkami môžu trvať niekoľko dní alebo týždňov bez potvrdenia. - Výber poplatkov je na vašu zodpovednosť. Po odoslaní nemožno túto platbu zrušiť, iba urýchliť vyššími poplatkami. - Ste si istí, že chcete pokračovať? + Nízke poplatky! + Platby s nízkymi poplatkami môžu trvať niekoľko dní alebo týždňov bez potvrdenia. + Výber poplatkov je na vašu zodpovednosť. Po odoslaní nemožno túto platbu zrušiť, iba urýchliť vyššími poplatkami. + Ste si istí, že chcete pokračovať? + + + + Vplyv na veľkosť kanála + Finančné prostriedky odoslané sa odoberú z vašej strany kanála, čím sa zníži veľkosť kanála o rovnakú sumu. Vaša prichádzajúca likvidita zostáva nezmenená. + Túto správu už nezobrazujte. diff --git a/phoenix-android/src/main/res/values-vi/important_strings.xml b/phoenix-android/src/main/res/values-vi/important_strings.xml index 1594918fa..e272710a6 100644 --- a/phoenix-android/src/main/res/values-vi/important_strings.xml +++ b/phoenix-android/src/main/res/values-vi/important_strings.xml @@ -100,7 +100,7 @@ Khôi phục ví của tôi Phoenix chỉ có thể khôi phục các khoản vốn off-chain được quản lý bởi ứng dụng này. \n\nNhập hạt giống KHÔNG tạo ra bởi Phoenix sẽ không khôi phục được bất cứ khoản tiền nào. \n\nNgoài ra, xin vui lòng đảm bảo rằng bạn chưa dùng ví Phoenix nào khác có chung hạt giống này. - Tôi đã hiểu. + Tôi đã hiểu. Hạt giống ví của bạn là một danh sách gồm 12 từ tiếng Anh. Nhập các từ này vào ô dưới đây. Nhập từ #%1$s @@ -137,9 +137,6 @@ Tính thanh khoản đầu vào không đủ cho khoản tiền này, ngoài ra bạn đã tắt chế độ quản lý kênh tự động. - Thông báo di chuyển dữ liệu - Đây là địa chỉ mới của bạn. Không sử dụng lại địa chỉ Bitcoin cũ trước lúc bạn di chuyển dữ liệu! - Hoá đơn này đã hết hạn. @@ -354,10 +351,15 @@ Tổng phí cao hơn 25% so với khoản thanh khoản đã yêu cầu. Tổng phí vượt quá số dư của bạn. + Xem lại yêu cầu + Bạn đang yêu cầu một lượng thanh khoản ban đầu. Thanh khoản không cố định theo thời gian: khi bạn nhận được tiền qua Lightning, thanh khoản sẽ được tiêu thụ và trở thành số dư của bạn. + Sau một năm, thanh khoản còn lại chưa được sử dụng sẽ được dịch vụ thu hồi. + Chấp nhận Đang xử lý splice… Thanh khoản đã được thêm vào thành công! + Số tiền đã thêm: %1$s Yêu cầu thanh khoản không thành công Các kênh đang bận. Vui lòng thử lại sau. @@ -379,9 +381,15 @@ - Phí thấp! - Các giao dịch không đủ phí có thể ở trong tình trạng không được xác nhận trong nhiều ngày hoặc nhiều tuần. - Lựa chọn mức phí phù hợp là trách nhiệm của bạn. Sau khi gửi yêu cầu, giao dịch này không thể bị hủy, mà chỉ có thể được tăng tốc với mức phí cao hơn. - Bạn có chắc bạn muốn tiếp tục? + Phí thấp! + Các giao dịch không đủ phí có thể ở trong tình trạng không được xác nhận trong nhiều ngày hoặc nhiều tuần. + Lựa chọn mức phí phù hợp là trách nhiệm của bạn. Sau khi gửi yêu cầu, giao dịch này không thể bị hủy, mà chỉ có thể được tăng tốc với mức phí cao hơn. + Bạn có chắc bạn muốn tiếp tục? + + + + Kích thước kênh bị ảnh hưởng + Số tiền gửi được lấy từ phía kênh của bạn, làm giảm quy mô kênh một lượng tương tự. Thanh khoản đầu vào của bạn không thay đổi. + Đừng hiển thị lại thông báo đó. diff --git a/phoenix-android/src/main/res/values/important_strings.xml b/phoenix-android/src/main/res/values/important_strings.xml index 00078928b..8a1643a50 100644 --- a/phoenix-android/src/main/res/values/important_strings.xml +++ b/phoenix-android/src/main/res/values/important_strings.xml @@ -22,6 +22,10 @@ --> + + + I understand. + Phoenix is running in the background @@ -93,7 +97,6 @@ Restore my wallet Phoenix can only restore off-chain funds that were managed by this application.\n\nImporting a seed that was NOT created by Phoenix will not restore any funds.\n\nPlease also make sure you are not already running another Phoenix wallet with the same seed. - I understand. Your wallet\'s seed is a list of 12 English words. Type-in each word of this list in the box below. Enter word #%1$s @@ -130,9 +133,6 @@ Inbound liquidity is insufficient for this amount, and you have disabled automated channel management. - Migration notice - This is your new address. Do not reuse your old Bitcoin addresses from before migration! - This invoice is expired. @@ -343,14 +343,19 @@ This fee goes to the service providing the liquidity. Duration 1 year - The additional capacity will be preserved for that duration. + As you receive funds, liquidity will be consumed and become your balance. After one year, the remaining unused liquidity will be reclaimed by the service. The total fee is more than 25% of the liquidity amount requested. The total fees exceed your balance. + Review + You are requesting an initial amount of liquidity. Liquidity is not constant over time: as you receive funds over Lightning, the liquidity will be consumed and become your balance. + After one year, the remaining unused liquidity will be reclaimed by the service. + Accept Processing splice… Liquidity successfully added! + Amount added: %1$s Liquidity request has failed Channels are not available. Try again later. @@ -370,11 +375,17 @@ This invoice has already been paid. Recipient is not reachable, or does not have enough inbound liquidity. - + + + Low feerate! + Transactions with insufficient feerate may linger for days or weeks without confirming. + Choosing the feerate is your responsibility. Once sent, this transaction cannot be cancelled, only accelerated with higher fees. + Are you sure you want to proceed? + + - Low feerate! - Transactions with insufficient feerate may linger for days or weeks without confirming. - Choosing the feerate is your responsibility. Once sent, this transaction cannot be cancelled, only accelerated with higher fees. - Are you sure you want to proceed? + Channel size impacted + Funds sent on-chain are taken from your side of the channel, reducing the channel size by the same amount. Your inbound liquidity remains unchanged. + Don\'t show that message again. \ No newline at end of file diff --git a/phoenix-ios/phoenix-ios.xcodeproj/project.pbxproj b/phoenix-ios/phoenix-ios.xcodeproj/project.pbxproj index 43749367f..e5f7a2a1d 100644 --- a/phoenix-ios/phoenix-ios.xcodeproj/project.pbxproj +++ b/phoenix-ios/phoenix-ios.xcodeproj/project.pbxproj @@ -191,6 +191,7 @@ DC9E7EC62A1295B100A5F1D0 /* liquidity.html in Resources */ = {isa = PBXBuildFile; fileRef = DC9E7EC82A1295B100A5F1D0 /* liquidity.html */; }; DCA02B9D2BD065BF0080520F /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = DCA02B9C2BD065BF0080520F /* PrivacyInfo.xcprivacy */; }; DCA02B9E2BD069230080520F /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = DCA02B9C2BD065BF0080520F /* PrivacyInfo.xcprivacy */; }; + DCA02BA02BD1A5FC0080520F /* ChannelSizeImpactWarning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA02B9F2BD1A5FC0080520F /* ChannelSizeImpactWarning.swift */; }; DCA125752A27EDDB00DA2F7F /* MempoolRecommendedResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA125742A27EDDB00DA2F7F /* MempoolRecommendedResponse.swift */; }; DCA3B41F2A5471C900E6B231 /* MinerFeeInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCA3B41E2A5471C900E6B231 /* MinerFeeInfo.swift */; }; DCA5391A29F1DDE7001BD3D5 /* SegmentedPicker in Frameworks */ = {isa = PBXBuildFile; productRef = DCA5391929F1DDE7001BD3D5 /* SegmentedPicker */; }; @@ -302,6 +303,7 @@ DCEE3E542931446A00EB4DFF /* Collections+AsInt.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCEE3E532931446A00EB4DFF /* Collections+AsInt.swift */; }; DCEE8998288605FD00FE42DD /* PaymentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCEE8997288605FD00FE42DD /* PaymentCell.swift */; }; DCEFD922276A796800001767 /* SyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCEFD921276A796800001767 /* SyncManager.swift */; }; + DCF8E3AC2BC4968D009299EE /* LowMinerFeeWarning.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCF8E3AB2BC4968D009299EE /* LowMinerFeeWarning.swift */; }; DCF9CFD52862656E001AD33F /* Asserts.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCFC72032862237400D6B293 /* Asserts.swift */; }; DCFA8759260E6F2E00AE8953 /* IntroView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCFA8758260E6F2E00AE8953 /* IntroView.swift */; }; DCFA876D260E91E600AE8953 /* IntroContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCFA876C260E91E600AE8953 /* IntroContainer.swift */; }; @@ -554,6 +556,7 @@ DC9E7EC22A12955300A5F1D0 /* LiquidityHTML.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiquidityHTML.swift; sourceTree = ""; }; DC9E7EC72A1295B100A5F1D0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = Base; path = Base.lproj/liquidity.html; sourceTree = ""; }; DCA02B9C2BD065BF0080520F /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; + DCA02B9F2BD1A5FC0080520F /* ChannelSizeImpactWarning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelSizeImpactWarning.swift; sourceTree = ""; }; DCA125742A27EDDB00DA2F7F /* MempoolRecommendedResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MempoolRecommendedResponse.swift; sourceTree = ""; }; DCA3B41E2A5471C900E6B231 /* MinerFeeInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MinerFeeInfo.swift; sourceTree = ""; }; DCA5391B29F7202F001BD3D5 /* ChannelInfoPopup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelInfoPopup.swift; sourceTree = ""; }; @@ -645,6 +648,7 @@ DCEE3E532931446A00EB4DFF /* Collections+AsInt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collections+AsInt.swift"; sourceTree = ""; }; DCEE8997288605FD00FE42DD /* PaymentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentCell.swift; sourceTree = ""; }; DCEFD921276A796800001767 /* SyncManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncManager.swift; sourceTree = ""; }; + DCF8E3AB2BC4968D009299EE /* LowMinerFeeWarning.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LowMinerFeeWarning.swift; sourceTree = ""; }; DCF9312F279F1BD900FD7776 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = ar; path = ar.lproj/about.html; sourceTree = ""; }; DCFA8758260E6F2E00AE8953 /* IntroView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntroView.swift; sourceTree = ""; }; DCFA876C260E91E600AE8953 /* IntroContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntroContainer.swift; sourceTree = ""; }; @@ -901,6 +905,7 @@ DC118BFF27B4523B0080BBAC /* CommentSheet.swift */, DCA3B41E2A5471C900E6B231 /* MinerFeeInfo.swift */, DC1844022A2690BB004D9578 /* MinerFeeSheet.swift */, + DCF8E3AB2BC4968D009299EE /* LowMinerFeeWarning.swift */, DC118BF727B44E6E0080BBAC /* LoginView.swift */, DC118C0327B454720080BBAC /* PaymentInFlightView.swift */, DC118C0127B453110080BBAC /* PaymentRequestedView.swift */, @@ -910,6 +915,7 @@ DCB62F482A5E09F900912A71 /* SpliceOutProblem.swift */, DC1771B32ABC99CE00B286C7 /* WebsiteLinkPopover.swift */, DC98D3952AF170AC005BD177 /* PaymentWarningPopover.swift */, + DCA02B9F2BD1A5FC0080520F /* ChannelSizeImpactWarning.swift */, ); path = send; sourceTree = ""; @@ -1764,6 +1770,7 @@ DCB493CB269F3B06001B0F09 /* Result+Deugly.swift in Sources */, DC71E7302723240E0063613D /* KotlinObservables.swift in Sources */, DC0D2EA52939269800284608 /* KotlinExtensions+CloudKit.swift in Sources */, + DCA02BA02BD1A5FC0080520F /* ChannelSizeImpactWarning.swift in Sources */, DC355E232A45FAF2008E8A8E /* NestedObservableObject.swift in Sources */, DC5F1C4A28DDAA49007A55ED /* ResetWalletView_Confirm.swift in Sources */, DC1B325029FC3D5900F7F45F /* OnChainDetails.swift in Sources */, @@ -1872,6 +1879,7 @@ DC5CA4ED28F83C3B0048A737 /* DrainWalletView.swift in Sources */, DC784A112B31EA180018DC4A /* LiquidityAdsView.swift in Sources */, DC6D26E329E76557006A7814 /* AnimatedClock.swift in Sources */, + DCF8E3AC2BC4968D009299EE /* LowMinerFeeWarning.swift in Sources */, DCAC9FC329675E1A0098D769 /* NavigationWrapper.swift in Sources */, DC65D86428E2F7D700686355 /* ResetWalletView_Action.swift in Sources */, DC71E7332728645B0063613D /* CurrencyConverterView.swift in Sources */, diff --git a/phoenix-ios/phoenix-ios/Localizable.xcstrings b/phoenix-ios/phoenix-ios/Localizable.xcstrings index ca557185d..1eb49c393 100644 --- a/phoenix-ios/phoenix-ios/Localizable.xcstrings +++ b/phoenix-ios/phoenix-ios/Localizable.xcstrings @@ -5229,6 +5229,9 @@ } } } + }, + "As you receive funds, liquidity will be consumed and become your balance. After one year, the remaining unused liquidity will be reclaimed by the service." : { + }, "Attention: if the Bitcoin mempool feerate is high, incoming LN payments requiring an on-chain operation could be expensive." : { "localizations" : { @@ -7237,6 +7240,9 @@ } } } + }, + "Channel size impacted" : { + }, "Channels are not available, try again later" : { "localizations" : { @@ -11489,6 +11495,9 @@ } } } + }, + "Don't show this message again" : { + }, "Don't use addresses from the previous version." : { "localizations" : { @@ -14385,6 +14394,9 @@ } } } + }, + "Funds sent on-chain are taken from your side of the channel, reducing the channel size by the same amount. Your inbound liquidity remains the same." : { + }, "Funds will eventually be sent to:" : { "localizations" : { @@ -34292,6 +34304,9 @@ } } } + }, + "You are requesting an **initial** amount of liquidity. Liquidity is not constant over time: as you receive funds over Lightning, the liquidity will be consumed and become your balance.\n\nAfter one year, the remaining unused liquidity will be reclaimed by the service." : { + }, "You are responsible for backing up your recovery phrase." : { "extractionState" : "manual", diff --git a/phoenix-ios/phoenix-ios/prefs/Prefs.swift b/phoenix-ios/phoenix-ios/prefs/Prefs.swift index 378d8e81a..287280196 100644 --- a/phoenix-ios/phoenix-ios/prefs/Prefs.swift +++ b/phoenix-ios/phoenix-ios/prefs/Prefs.swift @@ -24,6 +24,7 @@ fileprivate enum Key: String { case hasUpgradedSeedCloudBackups case serverMessageReadIndex case allowOverpayment + case doNotShowChannelImpactWarning } fileprivate enum KeyDeprecated: String { @@ -134,7 +135,7 @@ class Prefs { set { defaults.hasMergedChannelsForSplicing = newValue } } - var hasUpgradedSeedCloudBackups: Bool { + var hasUpgradedSeedCloudBackups: Bool { get { defaults.hasUpgradedSeedCloudBackups } set { defaults.hasUpgradedSeedCloudBackups = newValue } } @@ -169,6 +170,11 @@ class Prefs { get { defaults.allowOverpayment } set { defaults.allowOverpayment = newValue } } + + var doNotShowChannelImpactWarning: Bool { + get { defaults.doNotShowChannelImpactWarning } + set { defaults.doNotShowChannelImpactWarning = newValue } + } // -------------------------------------------------- // MARK: Wallet State @@ -256,6 +262,7 @@ class Prefs { defaults.removeObject(forKey: Key.hasUpgradedSeedCloudBackups.rawValue) defaults.removeObject(forKey: Key.serverMessageReadIndex.rawValue) defaults.removeObject(forKey: Key.allowOverpayment.rawValue) + defaults.removeObject(forKey: Key.doNotShowChannelImpactWarning.rawValue) self.backupTransactions.resetWallet(encryptedNodeId: encryptedNodeId) self.backupSeed.resetWallet(encryptedNodeId: encryptedNodeId) @@ -370,4 +377,9 @@ extension UserDefaults { get { bool(forKey: Key.allowOverpayment.rawValue) } set { set(newValue, forKey: Key.allowOverpayment.rawValue) } } + + @objc fileprivate var doNotShowChannelImpactWarning: Bool { + get { bool(forKey: Key.doNotShowChannelImpactWarning.rawValue) } + set { set(newValue, forKey: Key.doNotShowChannelImpactWarning.rawValue) } + } } diff --git a/phoenix-ios/phoenix-ios/views/configuration/fees/liquidity management/LiquidityAdsView.swift b/phoenix-ios/phoenix-ios/views/configuration/fees/liquidity management/LiquidityAdsView.swift index 79335b99a..c5ae83b89 100644 --- a/phoenix-ios/phoenix-ios/views/configuration/fees/liquidity management/LiquidityAdsView.swift +++ b/phoenix-ios/phoenix-ios/views/configuration/fees/liquidity management/LiquidityAdsView.swift @@ -30,6 +30,7 @@ struct LiquidityAdsView: View { @State var isPurchasing: Bool = false @State var isPurchased: Bool = false @State var channelsNotAvailable: Bool = false + @State var iUnderstand: Bool = false @State var showHelpSheet = false @@ -285,6 +286,11 @@ struct LiquidityAdsView: View { VStack(alignment: HorizontalAlignment.center, spacing: 20) { subsection_costDetails(feeInfo) + Divider() + .frame(maxWidth: 175) + + subsection_finePrint() + Divider() .frame(maxWidth: 175) @@ -298,11 +304,11 @@ struct LiquidityAdsView: View { @ViewBuilder func subsection_costDetails(_ feeInfo: LiquidityFeeInfo) -> some View { - // if #available(iOS 16.0, *) { - // subsection_costDetails_ios16(feeInfo) - // } else { + if #available(iOS 16.0, *) { + subsection_costDetails_ios16(feeInfo) + } else { subsection_costDetails_ios15(feeInfo) - // } + } } @ViewBuilder @@ -379,7 +385,12 @@ struct LiquidityAdsView: View { } .popover(present: $popoverPresent_duration) { InfoPopoverWindow { - Text("The additional capacity will be preserved for that duration.") + Text( + """ + As you receive funds, liquidity will be consumed and become your balance. \ + After one year, the remaining unused liquidity will be reclaimed by the service. + """ + ) } } } // @@ -400,6 +411,35 @@ struct LiquidityAdsView: View { ) } + @ViewBuilder + func subsection_finePrint() -> some View { + + VStack(alignment: HorizontalAlignment.center, spacing: 15) { + Text( + """ + You are requesting an **initial** amount of liquidity. \ + Liquidity is not constant over time: as you receive funds \ + over Lightning, the liquidity will be consumed and become \ + your balance. + + After one year, the remaining unused liquidity will be \ + reclaimed by the service. + """ + ) + .multilineTextAlignment(.center) + + Toggle(isOn: $iUnderstand) { + Text("I understand") + .foregroundColor(.appAccent) + .bold() + } + .toggleStyle(CheckboxToggleStyle( + onImage: onImage(), + offImage: offImage() + )) + } + } + @ViewBuilder func subsection_purchaseButton() -> some View { @@ -419,7 +459,7 @@ struct LiquidityAdsView: View { } } .buttonStyle(.borderless) // prevents trigger when row tapped - .disabled(isPurchasing || popoverPresent || _insufficientFunds) + .disabled(isPurchasing || popoverPresent || _insufficientFunds || !iUnderstand) .font(.headline) if channelsNotAvailable { @@ -596,6 +636,18 @@ struct LiquidityAdsView: View { } } + @ViewBuilder + func onImage() -> some View { + Image(systemName: "checkmark.square.fill") + .imageScale(.large) + } + + @ViewBuilder + func offImage() -> some View { + Image(systemName: "square") + .imageScale(.large) + } + // -------------------------------------------------- // MARK: View Helpers // -------------------------------------------------- diff --git a/phoenix-ios/phoenix-ios/views/layers/Popover.swift b/phoenix-ios/phoenix-ios/views/layers/Popover.swift index 452a65257..bbd84726d 100644 --- a/phoenix-ios/phoenix-ios/views/layers/Popover.swift +++ b/phoenix-ios/phoenix-ios/views/layers/Popover.swift @@ -96,7 +96,10 @@ public class PopoverState: ObservableObject { itemPublisher.sink { (item: PopoverItem?) in if item == nil { - action() + // Attempting to display another Popover won't work until the next RunLoop cycle. + DispatchQueue.main.async { + action() + } cancellables.removeAll() } diff --git a/phoenix-ios/phoenix-ios/views/layers/ShortSheet.swift b/phoenix-ios/phoenix-ios/views/layers/ShortSheet.swift index cc5fcbf2d..c86000bb1 100644 --- a/phoenix-ios/phoenix-ios/views/layers/ShortSheet.swift +++ b/phoenix-ios/phoenix-ios/views/layers/ShortSheet.swift @@ -96,7 +96,10 @@ public class ShortSheetState: ObservableObject { itemPublisher.sink { (item: ShortSheetItem?) in if item == nil { - action() + // Attempting to display another ShortSheet won't work until the next RunLoop cycle. + DispatchQueue.main.async { + action() + } cancellables.removeAll() } diff --git a/phoenix-ios/phoenix-ios/views/send/ChannelSizeImpactWarning.swift b/phoenix-ios/phoenix-ios/views/send/ChannelSizeImpactWarning.swift new file mode 100644 index 000000000..bb00c56a0 --- /dev/null +++ b/phoenix-ios/phoenix-ios/views/send/ChannelSizeImpactWarning.swift @@ -0,0 +1,105 @@ +import SwiftUI + +fileprivate let filename = "ChannelSizeImpactWarning" +#if DEBUG && true +fileprivate var log = LoggerFactory.shared.logger(filename, .trace) +#else +fileprivate var log = LoggerFactory.shared.logger(filename, .warning) +#endif + +struct ChannelSizeImpactWarning: View { + + @State var doNotShowAgain = false + + @EnvironmentObject var smartModalState: SmartModalState + + @ViewBuilder + var body: some View { + + VStack(alignment: HorizontalAlignment.center, spacing: 0) { + header() + content() + footer() + } + } + + @ViewBuilder + func header() -> some View { + + HStack(alignment: VerticalAlignment.center, spacing: 0) { + Text("Channel size impacted") + .font(.title3) + .accessibilityAddTraits(.isHeader) + .accessibilitySortPriority(100) + Spacer() + } + .padding(.horizontal) + .padding(.vertical, 8) + .background( + Color(UIColor.secondarySystemBackground) + .cornerRadius(15, corners: [.topLeft, .topRight]) + ) + .padding(.bottom, 4) + } + + @ViewBuilder + func content() -> some View { + + VStack(alignment: HorizontalAlignment.leading, spacing: 20) { + + Text( + """ + Funds sent on-chain are taken from your side of the channel, \ + reducing the channel size by the same amount. Your inbound \ + liquidity remains the same. + """ + ) + + Toggle(isOn: $doNotShowAgain) { + Text("Don't show this message again") + .foregroundColor(.appAccent) + } + .toggleStyle(CheckboxToggleStyle( + onImage: onImage(), + offImage: offImage() + )) + } + .padding(.horizontal) + .padding(.top) + } + + @ViewBuilder + func footer() -> some View { + + HStack(alignment: .center, spacing: 0) { + Spacer() + Button("OK") { + close() + } + } + .font(.title3) + .padding() + .padding(.top) + } + + @ViewBuilder + func onImage() -> some View { + Image(systemName: "checkmark.square.fill") + .imageScale(.large) + } + + @ViewBuilder + func offImage() -> some View { + Image(systemName: "square") + .imageScale(.large) + } + + func close() { + log.trace("close()") + + if doNotShowAgain { + Prefs.shared.doNotShowChannelImpactWarning = true + } + smartModalState.close() + } +} diff --git a/phoenix-ios/phoenix-ios/views/send/LowMinerFeeWarning.swift b/phoenix-ios/phoenix-ios/views/send/LowMinerFeeWarning.swift new file mode 100644 index 000000000..8607a4388 --- /dev/null +++ b/phoenix-ios/phoenix-ios/views/send/LowMinerFeeWarning.swift @@ -0,0 +1,94 @@ +import SwiftUI + +fileprivate let filename = "LowMinerFeeWarning" +#if DEBUG && true +fileprivate var log = LoggerFactory.shared.logger(filename, .trace) +#else +fileprivate var log = LoggerFactory.shared.logger(filename, .warning) +#endif + +struct LowMinerFeeWarning: View { + + @Binding var showLowMinerFeeWarning: Bool + + @EnvironmentObject var smartModalState: SmartModalState + + @ViewBuilder + var body: some View { + + VStack(alignment: HorizontalAlignment.center, spacing: 0) { + header() + content() + footer() + } + } + + @ViewBuilder + func header() -> some View { + + HStack(alignment: VerticalAlignment.center, spacing: 0) { + Text("Low feerate!") + .font(.title3) + .accessibilityAddTraits(.isHeader) + .accessibilitySortPriority(100) + Spacer() + } + .padding(.horizontal) + .padding(.vertical, 8) + .background( + Color(UIColor.secondarySystemBackground) + .cornerRadius(15, corners: [.topLeft, .topRight]) + ) + .padding(.bottom, 4) + } + + @ViewBuilder + func content() -> some View { + + VStack(alignment: HorizontalAlignment.center, spacing: 0) { + + Text( + """ + Transactions with insufficient feerate may linger for days or weeks without confirming. + + Choosing the feerate is your responsibility. \ + Once sent, this transaction cannot be cancelled, only accelerated with higer fees. + + Are you sure you want to proceed? + """ + ) + } + .padding(.horizontal) + .padding(.top) + } + + @ViewBuilder + func footer() -> some View { + + HStack(alignment: .center, spacing: 0) { + Spacer() + + Button("Back") { + cancelButtonTapped() + } + .padding(.trailing) + + Button("I understand") { + confirmButtonTapped() + } + } + .font(.title3) + .padding() + .padding(.top) + } + + func cancelButtonTapped() { + log.trace("cancelButtonTapped()") + showLowMinerFeeWarning = false + } + + func confirmButtonTapped() { + log.trace("confirmButtonTapped()") + smartModalState.close() + } +} diff --git a/phoenix-ios/phoenix-ios/views/send/MinerFeeSheet.swift b/phoenix-ios/phoenix-ios/views/send/MinerFeeSheet.swift index 7aef22b4f..554607004 100644 --- a/phoenix-ios/phoenix-ios/views/send/MinerFeeSheet.swift +++ b/phoenix-ios/phoenix-ios/views/send/MinerFeeSheet.swift @@ -571,94 +571,6 @@ struct MinerFeeSheet: View { // MARK: - -struct LowMinerFeeWarning: View { - - @Binding var showLowMinerFeeWarning: Bool - - @EnvironmentObject var smartModalState: SmartModalState - - @ViewBuilder - var body: some View { - - VStack(alignment: HorizontalAlignment.center, spacing: 0) { - header() - content() - footer() - } - } - - @ViewBuilder - func header() -> some View { - - HStack(alignment: VerticalAlignment.center, spacing: 0) { - Text("Low feerate!") - .font(.title3) - .accessibilityAddTraits(.isHeader) - .accessibilitySortPriority(100) - Spacer() - } - .padding(.horizontal) - .padding(.vertical, 8) - .background( - Color(UIColor.secondarySystemBackground) - .cornerRadius(15, corners: [.topLeft, .topRight]) - ) - .padding(.bottom, 4) - } - - @ViewBuilder - func content() -> some View { - - VStack(alignment: HorizontalAlignment.center, spacing: 0) { - - Text( - """ - Transactions with insufficient feerate may linger for days or weeks without confirming. - - Choosing the feerate is your responsibility. \ - Once sent, this transaction cannot be cancelled, only accelerated with higer fees. - - Are you sure you want to proceed? - """ - ) - } - .padding(.horizontal) - .padding(.top) - } - - @ViewBuilder - func footer() -> some View { - - HStack(alignment: .center, spacing: 0) { - Spacer() - - Button("Back") { - cancelButtonTapped() - } - .padding(.trailing) - - Button("I understand") { - confirmButtonTapped() - } - } - .font(.title3) - .padding() - .padding(.top) - } - - func cancelButtonTapped() { - log.trace("[LowMinerFeeWarning] cancelButtonTapped()") - showLowMinerFeeWarning = false - } - - func confirmButtonTapped() { - log.trace("[LowMinerFeeWarning] confirmButtonTapped()") - smartModalState.close() - } -} - -// MARK: - - struct PriorityBoxStyle: GroupBoxStyle { let width: CGFloat? diff --git a/phoenix-ios/phoenix-ios/views/send/ValidateView.swift b/phoenix-ios/phoenix-ios/views/send/ValidateView.swift index a06318144..5142cb127 100644 --- a/phoenix-ios/phoenix-ios/views/send/ValidateView.swift +++ b/phoenix-ios/phoenix-ios/views/send/ValidateView.swift @@ -61,6 +61,7 @@ struct ValidateView: View { @State var comment: String = "" @State var hasPromptedForComment = false + @State var hasShownChannelCapacityWarning = false @State var hasPickedSwapOutMode = false @State var didAppear = false @@ -1456,6 +1457,28 @@ struct ValidateView: View { mempoolRecommendedResponse: $mempoolRecommendedResponse ) } + smartModalState.onNextDidDisappear { + maybeShowCapacityImpactWarning() + } + } + + func maybeShowCapacityImpactWarning() { + log.trace("maybeShowCapacityImpactWarning()") + + guard !Prefs.shared.doNotShowChannelImpactWarning else { + log.debug("Prefs.shared.doNotShowChannelImpact = true") + return + } + guard !hasShownChannelCapacityWarning else { + log.debug("hasShownChannelCapacityWarning = true") + return + } + + smartModalState.display(dismissable: false) { + ChannelSizeImpactWarning() + } onWillDisappear: { + hasShownChannelCapacityWarning = true + } } func sendPayment() {