Improve flow in create/take offer process.

When creating or taking an offer the user gets the choice to fund the
trade from an external wallet for potentially better privacy (avoid
coin merge if inputs from ext. wallet are kept separated). From
feedback it seems that most users are not using that option and then
they need to do that extra click on the "Fund from internal Bisq
wallet" button.
We offer a way to avoid that extra step if the user chooses
to use the internal wallet. We show at the first time a popup
with background info why funding from an external wallet could
improve privacy and if the user prefers to not use that to give them
an option to skip that step in future. In the settings we could add
a toggle to re-enable it again.
The review screen can be skipped if user chooses this feature,
leading to data entry > confirm rather than
data entry > fund > review > confirm.
This commit is contained in:
jmacxx 2023-09-04 13:35:46 -05:00
parent 9813ae7574
commit e9ec3d72e1
No known key found for this signature in database
GPG Key ID: 155297BABFE94A1B
9 changed files with 91 additions and 18 deletions

View File

@ -151,6 +151,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
// Added at 1.9.11
private boolean isFullBMAccountingNode = false;
private boolean useBisqWalletFunding = false;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -225,7 +226,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
.setUserDefinedTradeLimit(userDefinedTradeLimit)
.setUserHasRaisedTradeLimit(userHasRaisedTradeLimit)
.setProcessBurningManAccountingData(processBurningManAccountingData)
.setIsFullBMAccountingNode(isFullBMAccountingNode);
.setIsFullBMAccountingNode(isFullBMAccountingNode)
.setUseBisqWalletFunding(useBisqWalletFunding);
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((protobuf.TradeCurrency) e.toProtoMessage()));
@ -334,7 +336,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
proto.getUserHasRaisedTradeLimit() ? proto.getUserDefinedTradeLimit() : Preferences.INITIAL_TRADE_LIMIT,
proto.getUserHasRaisedTradeLimit(),
proto.getProcessBurningManAccountingData(),
proto.getIsFullBMAccountingNode()
proto.getIsFullBMAccountingNode(),
proto.getUseBisqWalletFunding()
);
}
}

View File

@ -108,6 +108,10 @@ shared.nextStep=Next step
shared.fundFromSavingsWalletButton=Transfer funds from Bisq wallet
shared.fundFromExternalWalletButton=Open your external wallet for funding
shared.openDefaultWalletFailed=Failed to open a Bitcoin wallet application. Are you sure you have one installed?
shared.question.useBisqWalletForFunding=Would you like to always use your Bisq wallet for funding?\n\n\
The make/take offer process will take less steps by choosing this. \
However, funding from an external wallet could potentially be better for privacy.\n\n\
(This can be also be controlled in Settings: "Fund offer/trades from Bisq wallet").
shared.belowInPercent=Below % from market price
shared.aboveInPercent=Above % from market price
shared.enterPercentageValue=Enter % value
@ -1405,6 +1409,8 @@ setting.preferences.useBitcoinUris.tooltip=Bisq's QR codes can either store bitc
with some extra parameters (amount & label) or plain bitcoin addresses\n\
with no additional data. Disable this option if your external wallet's\n\
scanner cannot handle the bitcoin URI scheme format.
setting.preferences.useBisqWalletForFunding=Fund offer/trades from Bisq wallet
setting.preferences.useBisqWalletForFunding.tooltip=The make/take offer process will be streamlined to take fewer steps if this option is enabled.
setting.preferences.currenciesInList=Currencies in market price feed list
setting.preferences.prefCurrency=Preferred currency
setting.preferences.displayFiat=Display national currencies

View File

@ -430,7 +430,12 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
advancedOptionsBox.setManaged(false);
model.onShowPayFundsScreen(() -> {
if (!DevEnv.isDevMode()) {
totalToPayTextField.setFundsStructure(model.getFundsStructure());
totalToPayTextField.setContentForInfoPopOver(createInfoPopover());
if (preferences.isUseBisqWalletFunding()) {
model.fundFromSavingsWallet();
} else {
String key = "createOfferFundWalletInfo";
String tradeAmountText = model.isSellOffer() ?
Res.get("createOffer.createOfferFundWalletInfo.tradeAmount", model.getTradeAmount()) : "";
@ -447,9 +452,6 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
.dontShowAgainId(key)
.show();
}
totalToPayTextField.setFundsStructure(model.getFundsStructure());
totalToPayTextField.setContentForInfoPopOver(createInfoPopover());
});
paymentAccountsComboBox.setDisable(true);
@ -467,7 +469,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
balanceTextField.setTargetAmount(model.getDataModel().totalToPayAsCoinProperty().get());
if (!DevEnv.isDevMode()) {
if (!preferences.isUseBisqWalletFunding()) {
String key = "securityDepositInfo";
new Popup().backgroundInfo(Res.get("popup.info.securityDepositInfo"))
.actionButtonText(Res.get("shared.faq"))
@ -822,6 +824,9 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
.autoClose();
walletFundedNotification.show();
if (preferences.isUseBisqWalletFunding()) { // potentially bypass review step to the confirmation popup
UserThread.execute(this::onPlaceOffer);
}
}
};
@ -1254,7 +1259,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
fundFromSavingsWalletButton = new AutoTooltipButton(Res.get("shared.fundFromSavingsWalletButton"));
fundFromSavingsWalletButton.setDefaultButton(true);
fundFromSavingsWalletButton.getStyleClass().add("action-button");
fundFromSavingsWalletButton.setOnAction(e -> model.fundFromSavingsWallet());
fundFromSavingsWalletButton.setOnAction(e -> GUIUtil.maybeAskAboutStreamliningOrderFunding(
model::savePreferenceAndFundFromSavingsWallet, model::fundFromSavingsWallet));
Label label = new AutoTooltipLabel(Res.get("shared.OR"));
label.setPadding(new Insets(5, 0, 0, 0));
Button fundFromExternalWalletButton = new AutoTooltipButton(Res.get("shared.fundFromExternalWalletButton"));

View File

@ -690,6 +690,11 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
updateSpinnerInfo();
}
void savePreferenceAndFundFromSavingsWallet() {
preferences.setUseBisqWalletFunding(true);
fundFromSavingsWallet();
}
void fundFromSavingsWallet() {
dataModel.fundFromSavingsWallet();
if (dataModel.getIsBtcWalletFunded().get()) {

View File

@ -59,6 +59,7 @@ import bisq.core.payment.FasterPaymentsAccount;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.user.DontShowAgainLookup;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
@ -126,6 +127,7 @@ import static javafx.beans.binding.Bindings.createStringBinding;
@FxmlView
public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOfferViewModel> implements ClosableView, InitializableViewWithTakeOfferData, SelectableView {
private final Navigation navigation;
private final Preferences preferences;
private final CoinFormatter formatter;
private final BsqFormatter bsqFormatter;
private final OfferDetailsWindow offerDetailsWindow;
@ -184,6 +186,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
@Inject
private TakeOfferView(TakeOfferViewModel model,
Navigation navigation,
Preferences preferences,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
BsqFormatter bsqFormatter,
OfferDetailsWindow offerDetailsWindow,
@ -191,6 +194,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
super(model);
this.navigation = navigation;
this.preferences = preferences;
this.formatter = formatter;
this.bsqFormatter = bsqFormatter;
this.offerDetailsWindow = offerDetailsWindow;
@ -224,6 +228,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
.autoClose();
walletFundedNotification.show();
if (preferences.isUseBisqWalletFunding()) { // potentially bypass review step to the confirmation popup
UserThread.execute(this::onTakeOffer);
}
}
};
@ -504,7 +511,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
balanceTextField.setTargetAmount(model.dataModel.getTotalToPayAsCoin().get());
if (!DevEnv.isDevMode()) {
if (preferences.isUseBisqWalletFunding()) {
model.fundFromSavingsWallet();
} else {
String key = "securityDepositInfo";
new Popup().backgroundInfo(Res.get("popup.info.securityDepositInfo"))
.actionButtonText(Res.get("shared.faq"))
@ -995,7 +1004,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
fundFromSavingsWalletButton = new AutoTooltipButton(Res.get("shared.fundFromSavingsWalletButton"));
fundFromSavingsWalletButton.setDefaultButton(true);
fundFromSavingsWalletButton.getStyleClass().add("action-button");
fundFromSavingsWalletButton.setOnAction(e -> model.fundFromSavingsWallet());
fundFromSavingsWalletButton.setOnAction(e -> GUIUtil.maybeAskAboutStreamliningOrderFunding(
model::savePreferenceAndFundFromSavingsWallet, model::fundFromSavingsWallet));
Label label = new AutoTooltipLabel(Res.get("shared.OR"));
label.setPadding(new Insets(5, 0, 0, 0));
Button fundFromExternalWalletButton = new AutoTooltipButton(Res.get("shared.fundFromExternalWalletButton"));

View File

@ -41,6 +41,7 @@ import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.model.bisq_v1.Trade;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.BsqFormatter;
@ -86,6 +87,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
private final P2PService p2PService;
private final AccountAgeWitnessService accountAgeWitnessService;
private final Navigation navigation;
private final Preferences preferences;
private final CoinFormatter btcFormatter;
private final BsqFormatter bsqFormatter;
@ -145,6 +147,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
P2PService p2PService,
AccountAgeWitnessService accountAgeWitnessService,
Navigation navigation,
Preferences preferences,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter,
BsqFormatter bsqFormatter) {
super(dataModel);
@ -154,6 +157,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
this.p2PService = p2PService;
this.accountAgeWitnessService = accountAgeWitnessService;
this.navigation = navigation;
this.preferences = preferences;
this.btcFormatter = btcFormatter;
this.bsqFormatter = bsqFormatter;
createListeners();
@ -266,6 +270,11 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
updateSpinnerInfo();
}
void savePreferenceAndFundFromSavingsWallet() {
preferences.setUseBisqWalletFunding(true);
fundFromSavingsWallet();
}
boolean fundFromSavingsWallet() {
dataModel.fundFromSavingsWallet();
if (dataModel.getIsBtcWalletFunded().get()) {

View File

@ -134,7 +134,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
avoidStandbyMode, useCustomFee, autoConfirmXmrToggle, hideNonAccountPaymentMethodsToggle, denyApiTakerToggle,
notifyOnPreReleaseToggle, isDaoFullNodeToggleButton,
fullModeDaoMonitorToggleButton, useBitcoinUrisToggle, tradeLimitToggle, processBurningManAccountingDataToggleButton,
isFullBMAccountingDataNodeToggleButton;
isFullBMAccountingDataNodeToggleButton, useBisqWalletForFundingToggle;
private int gridRow = 0;
private int displayCurrenciesGridRowIndex = 0;
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, ignoreDustThresholdInputTextField,
@ -286,7 +286,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
///////////////////////////////////////////////////////////////////////////////////////////
private void initializeGeneralOptions() {
int titledGroupBgRowSpan = displayStandbyModeFeature ? 11 : 10;
int titledGroupBgRowSpan = displayStandbyModeFeature ? 12 : 11;
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, titledGroupBgRowSpan, Res.get("setting.preferences.general"));
GridPane.setColumnSpan(titledGroupBg, 1);
@ -440,12 +440,23 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
Res.get("setting.preferences.avoidStandbyMode"));
}
useBitcoinUrisToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.useBitcoinUris"));
Tooltip tooltip = new Tooltip(Res.get("setting.preferences.useBitcoinUris.tooltip"));
tooltip.setShowDuration(Duration.millis(8000));
tooltip.setShowDelay(Duration.millis(300));
tooltip.setHideDelay(Duration.millis(0));
Tooltip.install(useBitcoinUrisToggle, tooltip);
{
useBitcoinUrisToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.useBitcoinUris"));
Tooltip tooltip = new Tooltip(Res.get("setting.preferences.useBitcoinUris.tooltip"));
tooltip.setShowDuration(Duration.millis(8000));
tooltip.setShowDelay(Duration.millis(300));
tooltip.setHideDelay(Duration.millis(0));
Tooltip.install(useBitcoinUrisToggle, tooltip);
}
{
useBisqWalletForFundingToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.useBisqWalletForFunding"));
Tooltip tooltip = new Tooltip(Res.get("setting.preferences.useBisqWalletForFunding.tooltip"));
tooltip.setShowDuration(Duration.millis(8000));
tooltip.setShowDelay(Duration.millis(300));
tooltip.setHideDelay(Duration.millis(0));
Tooltip.install(useBisqWalletForFundingToggle, tooltip);
}
}
private void initializeSeparator() {
@ -990,6 +1001,10 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
useBitcoinUrisToggle.setOnAction(e ->
preferences.setUseBitcoinUrisInQrCodes(useBitcoinUrisToggle.isSelected()));
useBisqWalletForFundingToggle.setSelected(preferences.isUseBisqWalletFunding());
useBisqWalletForFundingToggle.setOnAction(e ->
preferences.setUseBisqWalletFunding(useBisqWalletForFundingToggle.isSelected()));
btcExplorerTextField.setText(preferences.getBlockChainExplorer().name);
bsqExplorerTextField.setText(preferences.getBsqBlockChainExplorer().name);
@ -1362,6 +1377,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
editCustomBtcExplorer.setOnAction(null);
editCustomBsqExplorer.setOnAction(null);
useBitcoinUrisToggle.setOnAction(null);
useBisqWalletForFundingToggle.setOnAction(null);
deviationInputTextField.textProperty().removeListener(deviationListener);
deviationInputTextField.focusedProperty().removeListener(deviationFocusedListener);
transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);

View File

@ -803,6 +803,23 @@ public class GUIUtil {
return false;
}
public static void maybeAskAboutStreamliningOrderFunding(Runnable actionHandlerYes, Runnable actionHandlerNo) {
String key = "ask_always_use_bisq_wallet";
if (!preferences.isUseBisqWalletFunding() && DontShowAgainLookup.showAgain(key)) {
// user clicks on "Fund from Bisq wallet",
// ask user if they want to in the future always fund from Bisq wallet
new Popup().instruction(Res.get("shared.question.useBisqWalletForFunding"))
.actionButtonText(Res.get("shared.yes"))
.onAction(actionHandlerYes)
.closeButtonText(Res.get("shared.no"))
.onClose(actionHandlerNo)
.dontShowAgainId(key)
.show();
} else {
actionHandlerNo.run();
}
}
public static boolean isReadyForTxBroadcastOrShowPopup(P2PService p2PService, WalletsSetup walletsSetup) {
if (!GUIUtil.isBootstrappedOrShowPopup(p2PService)) {
return false;

View File

@ -1973,6 +1973,7 @@ message PreferencesPayload {
bool user_has_raised_trade_limit = 68;
bool process_burning_man_accounting_data = 69;
bool is_full_b_m_accounting_node = 70;
bool use_bisq_wallet_funding = 71;
}
message AutoConfirmSettings {