Skip to content

Commit

Permalink
Merge pull request #2819 from HenrikJannsen/offer-improvements
Browse files Browse the repository at this point in the history
Offer improvements
  • Loading branch information
HenrikJannsen authored Sep 12, 2024
2 parents 7ee5d46 + a30864b commit ce17e20
Show file tree
Hide file tree
Showing 15 changed files with 154 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ protected void updateItem(T item, boolean empty) {
selectedPin.unsubscribe();
selectedPin = null;
}
setId(null);
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ private void applySliderTrackStyle() {
maxRangeMonetary != null) {
double repAmount = reputationBasedQuoteSideAmount.getValue() - minRangeMonetary.getValue();
double range = model.getMaxRangeMonetary().get().getValue() - minRangeMonetary.getValue();
double reputationBasedAmountOnSlider = repAmount / range;
double reputationBasedAmountOnSlider = range != 0 ? repAmount / range : 0;
String rightSideColor = "-bisq-dark-grey-50";
model.getSliderTrackStyle().set(String.format(
"-track-color: linear-gradient(to right, " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ public class TakeOfferController extends NavigationController implements InitWit
@ToString
public static class InitData {
private final BisqEasyOffer bisqEasyOffer;
private final Optional<Monetary> takerAsSellersMaxAllowedAmount;
private final Optional<Monetary> reputationBasedQuoteSideAmount;

public InitData(BisqEasyOffer bisqEasyOffer, Optional<Monetary> takerAsSellersMaxAllowedAmount) {
public InitData(BisqEasyOffer bisqEasyOffer, Optional<Monetary> reputationBasedQuoteSideAmount) {
this.bisqEasyOffer = bisqEasyOffer;
this.takerAsSellersMaxAllowedAmount = takerAsSellersMaxAllowedAmount;
this.reputationBasedQuoteSideAmount = reputationBasedQuoteSideAmount;
}
}

Expand Down Expand Up @@ -94,7 +94,7 @@ public boolean useCaching() {
@Override
public void initWithData(InitData initData) {
BisqEasyOffer bisqEasyOffer = initData.getBisqEasyOffer();
takeOfferAmountController.init(bisqEasyOffer,initData.getTakerAsSellersMaxAllowedAmount());
takeOfferAmountController.init(bisqEasyOffer, initData.getReputationBasedQuoteSideAmount());
takeOfferPaymentController.init(bisqEasyOffer);
takeOfferReviewController.init(bisqEasyOffer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@
import bisq.bisq_easy.BisqEasyTradeAmountLimits;
import bisq.bonded_roles.market_price.MarketPriceService;
import bisq.common.currency.Market;
import bisq.common.currency.MarketRepository;
import bisq.common.monetary.Fiat;
import bisq.common.monetary.Monetary;
import bisq.desktop.ServiceProvider;
import bisq.desktop.common.Browser;
import bisq.desktop.common.utils.KeyHandlerUtil;
import bisq.desktop.common.view.Controller;
import bisq.desktop.main.content.bisq_easy.components.AmountComponent;
import bisq.i18n.Res;
import bisq.offer.Direction;
import bisq.offer.amount.OfferAmountFormatter;
import bisq.offer.amount.OfferAmountUtil;
import bisq.offer.bisq_easy.BisqEasyOffer;
import bisq.offer.price.PriceUtil;
Expand All @@ -43,7 +41,6 @@
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;

import java.util.Map;
import java.util.Optional;

@Slf4j
Expand All @@ -66,7 +63,7 @@ public TakeOfferAmountController(ServiceProvider serviceProvider) {
view = new TakeOfferAmountView(model, this, amountComponent.getView().getRoot());
}

public void init(BisqEasyOffer bisqEasyOffer, Optional<Monetary> takerAsSellersMaxAllowedAmount) {
public void init(BisqEasyOffer bisqEasyOffer, Optional<Monetary> optionalReputationBasedQuoteSideAmount) {
model.setBisqEasyOffer(bisqEasyOffer);

Direction takersDirection = bisqEasyOffer.getTakersDirection();
Expand All @@ -78,42 +75,41 @@ public void init(BisqEasyOffer bisqEasyOffer, Optional<Monetary> takerAsSellersM
PriceUtil.findQuote(marketPriceService, bisqEasyOffer.getPriceSpec(), bisqEasyOffer.getMarket())
.ifPresent(amountComponent::setQuote);

Optional<Monetary> optionalQuoteSideMinOrFixedAmount = OfferAmountUtil.findQuoteSideMinOrFixedAmount(marketPriceService, bisqEasyOffer);
Optional<Monetary> optionalQuoteSideMaxOrFixedAmount = OfferAmountUtil.findQuoteSideMaxOrFixedAmount(marketPriceService, bisqEasyOffer);

if (optionalQuoteSideMinOrFixedAmount.isPresent() && takerAsSellersMaxAllowedAmount.isPresent()) {
//todo
Monetary maxAmount = takerAsSellersMaxAllowedAmount.get().round(0);
Monetary reputationBasedQuoteSideAmount = takerAsSellersMaxAllowedAmount.get().round(0);
Monetary quoteSideMinOrFixedAmount = optionalQuoteSideMinOrFixedAmount.get().round(0);
amountComponent.setReputationBasedQuoteSideAmount(reputationBasedQuoteSideAmount);
applyQuoteSideMinMaxRange(quoteSideMinOrFixedAmount, maxAmount);

long sellersScore = reputationService.getReputationScore(userIdentityService.getSelectedUserIdentity().getUserProfile()).getTotalScore();
amountComponent.setDescription(Res.get("bisqEasy.takeOffer.amount.description.limitedByTakersReputation",
sellersScore,
OfferAmountFormatter.formatQuoteSideMinAmount(marketPriceService, bisqEasyOffer, false),
AmountFormatter.formatAmountWithCode(maxAmount)));
} else if (optionalQuoteSideMinOrFixedAmount.isPresent() && optionalQuoteSideMaxOrFixedAmount.isPresent()) {
//todo
Monetary maxAmount = optionalQuoteSideMaxOrFixedAmount.get().round(0);
Monetary reputationBasedQuoteSideAmount = optionalQuoteSideMaxOrFixedAmount.get();
Monetary quoteSideMinOrFixedAmount = optionalQuoteSideMinOrFixedAmount.get().round(0);
applyQuoteSideMinMaxRange(quoteSideMinOrFixedAmount, maxAmount);

amountComponent.setDescription(Res.get("bisqEasy.takeOffer.amount.description",
OfferAmountFormatter.formatQuoteSideMinAmount(marketPriceService, bisqEasyOffer, false),
AmountFormatter.formatAmountWithCode(maxAmount)));
} else {
log.error("optionalQuoteSideMinOrFixedAmount or optionalQuoteSideMaxOrFixedAmount is not present");
Optional<Monetary> OptionalQuoteSideMinAmount = OfferAmountUtil.findQuoteSideMinAmount(marketPriceService, bisqEasyOffer);
if (OptionalQuoteSideMinAmount.isPresent()) {
Monetary quoteSideMinAmount = OptionalQuoteSideMinAmount.get().round(0);
Monetary offersQuoteSideMaxOrFixedAmount = OfferAmountUtil.findQuoteSideMaxOrFixedAmount(marketPriceService, bisqEasyOffer).orElseThrow().round(0);
String formattedMinAmount = AmountFormatter.formatAmount(quoteSideMinAmount);
Monetary maxAmount;
if (optionalReputationBasedQuoteSideAmount.isPresent()) {
// Range amounts seller case with provided reputationBasedQuoteSideAmount
Monetary reputationBasedQuoteSideAmount = optionalReputationBasedQuoteSideAmount.get();
maxAmount = reputationBasedQuoteSideAmount.isGreaterThan(offersQuoteSideMaxOrFixedAmount)
? offersQuoteSideMaxOrFixedAmount
: reputationBasedQuoteSideAmount;
amountComponent.setReputationBasedQuoteSideAmount(maxAmount);
applyQuoteSideMinMaxRange(quoteSideMinAmount, maxAmount);

long sellersScore = reputationService.getReputationScore(userIdentityService.getSelectedUserIdentity().getUserProfile()).getTotalScore();
amountComponent.setDescription(Res.get("bisqEasy.takeOffer.amount.description.limitedByTakersReputation",
sellersScore,
formattedMinAmount,
AmountFormatter.formatAmountWithCode(maxAmount)));
} else {
// Range amounts buyer case
maxAmount = offersQuoteSideMaxOrFixedAmount;
applyQuoteSideMinMaxRange(quoteSideMinAmount, maxAmount);
amountComponent.setDescription(Res.get("bisqEasy.takeOffer.amount.description",
formattedMinAmount,
AmountFormatter.formatAmountWithCode(maxAmount)));
}
String btcAmount = takersDirection.isBuy()
? Res.get("bisqEasy.component.amount.baseSide.tooltip.buyer.btcAmount")
: Res.get("bisqEasy.component.amount.baseSide.tooltip.seller.btcAmount");
Optional<String> priceQuoteOptional = PriceUtil.findQuote(marketPriceService, model.getBisqEasyOffer())
.map(priceQuote -> "\n" + Res.get("bisqEasy.component.amount.baseSide.tooltip.taker.offerPrice", PriceFormatter.formatWithCode(priceQuote)));
priceQuoteOptional.ifPresent(priceQuote -> amountComponent.setTooltip(String.format("%s%s", btcAmount, priceQuote)));
}

String btcAmount = takersDirection.isBuy()
? Res.get("bisqEasy.component.amount.baseSide.tooltip.buyer.btcAmount")
: Res.get("bisqEasy.component.amount.baseSide.tooltip.seller.btcAmount");
Optional<String> priceQuoteOptional = PriceUtil.findQuote(marketPriceService, model.getBisqEasyOffer())
.map(priceQuote -> "\n" + Res.get("bisqEasy.component.amount.baseSide.tooltip.taker.offerPrice", PriceFormatter.formatWithCode(priceQuote)));
priceQuoteOptional.ifPresent(priceQuote -> amountComponent.setTooltip(String.format("%s%s", btcAmount, priceQuote)));
}

public ReadOnlyObjectProperty<Monetary> getTakersQuoteSideAmount() {
Expand Down Expand Up @@ -141,20 +137,28 @@ public void onActivate() {
public void onDeactivate() {
baseSideAmountPin.unsubscribe();
quoteSideAmountPin.unsubscribe();
view.getRoot().setOnKeyPressed(null);
model.getIsWarningIconVisible().set(false);
model.getIsAmountLimitInfoOverlayVisible().set(false);
model.setSellersReputationBasedQuoteSideAmount(null);
}

void onSetReputationBasedAmount() {
amountComponent.setQuoteSideAmount(amountComponent.getReputationBasedQuoteSideAmount());
amountComponent.setQuoteSideAmount(amountComponent.getReputationBasedQuoteSideAmount().round(0));
}

void onShowAmountLimitInfoOverlay() {
model.getIsAmountLimitInfoOverlayVisible().set(true);
view.getRoot().setOnKeyPressed(keyEvent -> {
KeyHandlerUtil.handleEnterKeyEvent(keyEvent, () -> {
});
KeyHandlerUtil.handleEscapeKeyEvent(keyEvent, this::onCloseAmountLimitInfoOverlay);
});
}

void onCloseAmountLimitInfoOverlay() {
model.getIsAmountLimitInfoOverlayVisible().set(false);
view.getRoot().setOnKeyPressed(null);
}

void onOpenWiki(String url) {
Expand All @@ -170,12 +174,20 @@ private void applyQuoteSideMinMaxRange(Monetary minRangeValue, Monetary maxRange
model.setLinkToWikiText(Res.get("bisqEasy.takeOffer.amount.buyer.limitInfo.overlay.linkToWikiText"));
model.getAmountLimitInfoAmount().set("");

long sellersReputationScore = reputationService.getReputationScore(bisqEasyOffer.getMakersUserProfileId()).getTotalScore();
Monetary reputationBasedQuoteSideAmount = BisqEasyTradeAmountLimits.getMaxQuoteSideTradeAmount(marketPriceService, bisqEasyOffer.getMarket(), sellersReputationScore)
.orElse(Fiat.fromFaceValue(0, "USD")).round(0);
if (model.getSellersReputationBasedQuoteSideAmount() == null) {
long sellersReputationScore = reputationService.getReputationScore(bisqEasyOffer.getMakersUserProfileId()).getTotalScore();
model.setSellersReputationScore(sellersReputationScore);
Monetary reputationBasedQuoteSideAmount = BisqEasyTradeAmountLimits.getReputationBasedQuoteSideAmount(marketPriceService, bisqEasyOffer.getMarket(), sellersReputationScore)
.orElseThrow().round(0);
model.setSellersReputationBasedQuoteSideAmount(reputationBasedQuoteSideAmount);
}
long sellersReputationScore = model.getSellersReputationScore();
Monetary reputationBasedQuoteSideAmount = model.getSellersReputationBasedQuoteSideAmount();

if (reputationBasedQuoteSideAmount.isLessThan(maxRangeValue)) {
model.getIsAmountLimitInfoVisible().set(true);
amountComponent.setReputationBasedQuoteSideAmount(reputationBasedQuoteSideAmount);
amountComponent.setQuoteSideAmount(reputationBasedQuoteSideAmount);
String formattedAmount = AmountFormatter.formatAmountWithCode(reputationBasedQuoteSideAmount);
model.getAmountLimitInfoOverlayInfo().set(Res.get("bisqEasy.takeOffer.amount.buyer.limitInfo.overlay.info", sellersReputationScore, formattedAmount) + "\n\n");
if (reputationBasedQuoteSideAmount.isLessThan(minRangeValue)) {
Expand All @@ -194,10 +206,11 @@ private void applyQuoteSideMinMaxRange(Monetary minRangeValue, Monetary maxRange
model.setLinkToWikiText(Res.get("bisqEasy.tradeWizard.amount.seller.limitInfo.overlay.linkToWikiText"));
String myProfileId = userIdentityService.getSelectedUserIdentity().getUserProfile().getId();
long myReputationScore = reputationService.getReputationScore(myProfileId).getTotalScore();
BisqEasyTradeAmountLimits.getMaxQuoteSideTradeAmount(marketPriceService, bisqEasyOffer.getMarket(), myReputationScore)
.ifPresent(reputationBasedQuoteSideAmount -> {
amountComponent.setReputationBasedQuoteSideAmount(reputationBasedQuoteSideAmount);
String formattedAmount = AmountFormatter.formatAmountWithCode(reputationBasedQuoteSideAmount);
BisqEasyTradeAmountLimits.getReputationBasedQuoteSideAmount(marketPriceService, bisqEasyOffer.getMarket(), myReputationScore)
.ifPresent(myReputationBasedQuoteSideAmount -> {
model.getIsAmountHyperLinkDisabled().set(myReputationBasedQuoteSideAmount.isGreaterThan(maxRangeValue));
amountComponent.setReputationBasedQuoteSideAmount(myReputationBasedQuoteSideAmount);
String formattedAmount = AmountFormatter.formatAmountWithCode(myReputationBasedQuoteSideAmount);
model.getIsAmountLimitInfoVisible().set(true);
model.getAmountLimitInfo().set(Res.get("bisqEasy.tradeWizard.amount.seller.limitInfo", myReputationScore));
model.getAmountLimitInfoAmount().set(Res.get("bisqEasy.tradeWizard.amount.seller.limitInfoAmount", formattedAmount));
Expand All @@ -209,38 +222,13 @@ private void applyQuoteSideMinMaxRange(Monetary minRangeValue, Monetary maxRange
}

private void applyReputationBasedQuoteSideAmount() {
// Reduce by 1 USD to avoid rounding issues, but use at least 25 USD (MAX_USD_TRADE_AMOUNT_WITHOUT_REPUTATION)
long value = amountComponent.getReputationBasedQuoteSideAmount().getValue() - 10000;
value = Math.max(BisqEasyTradeAmountLimits.MAX_USD_TRADE_AMOUNT_WITHOUT_REPUTATION.getValue(), value);
amountComponent.setQuoteSideAmount(Fiat.fromValue(value, "USD"));
amountComponent.setQuoteSideAmount(amountComponent.getReputationBasedQuoteSideAmount().round(0));
}

private void maxOrFixedQuoteSideAmountChanged(Monetary value) {
if (amountComponent.getReputationBasedQuoteSideAmount() == null) {
return;
}
double reputationBasedAmountFaceValue = Monetary.valueToFaceValue(amountComponent.getReputationBasedQuoteSideAmount(), 0);
double faceValue = Monetary.valueToFaceValue(value, 0);
model.getIsWarningIconVisible().set(faceValue > reputationBasedAmountFaceValue);

long highestScore = reputationService.getScoreByUserProfileId().entrySet().stream()
.filter(e -> userIdentityService.findUserIdentity(e.getKey()).isEmpty())
.mapToLong(Map.Entry::getValue)
.max()
.orElse(0L);
/* Monetary highestPossibleUsdAmount = BisqEasyTradeAmountLimits.getUsdAmountFromReputationScore(highestScore);
// We reduce by 1 USD to avoid rounding issues
Fiat oneUsd = Fiat.fromFaceValue(1d, "USD");
highestPossibleUsdAmount = Monetary.from(highestPossibleUsdAmount, highestPossibleUsdAmount.getValue() - oneUsd.getValue());*/
Market usdBitcoinMarket = MarketRepository.getUSDBitcoinMarket();
long requiredReputationScore = BisqEasyTradeAmountLimits.findRequiredReputationScoreByFiatAmount(marketPriceService, usdBitcoinMarket, value).orElse(0L);
long numPotentialTakers = reputationService.getScoreByUserProfileId().entrySet().stream()
.filter(e -> userIdentityService.findUserIdentity(e.getKey()).isEmpty())
.filter(e -> e.getValue() >= requiredReputationScore || requiredReputationScore <= BisqEasyTradeAmountLimits.MIN_REPUTAION_SCORE)
.count();
if (model.getBisqEasyOffer().getTakersDirection().isBuy()) {
// model.getAmountLimitInfoLeft().set(Res.get("bisqEasy.tradeWizard.amount.buyer.limitInfoLeft", numPotentialTakers, AmountFormatter.formatAmountWithCode(value)));
// model.getAmountLimitInfoOverlayInfo().set(Res.get("bisqEasy.tradeWizard.amount.buyer.limitInfo.overlay.info", AmountFormatter.formatAmountWithCode(value), requiredReputationScore));
}
model.getIsWarningIconVisible().set(value.round(0).getValue() > amountComponent.getReputationBasedQuoteSideAmount().round(0).getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,14 @@ public class TakeOfferAmountModel implements Model {
private final StringProperty amountLimitInfoAmount = new SimpleStringProperty();
private final StringProperty amountLimitInfoOverlayInfo = new SimpleStringProperty();
private final BooleanProperty isWarningIconVisible = new SimpleBooleanProperty();
private final BooleanProperty isAmountHyperLinkDisabled = new SimpleBooleanProperty();
private final BooleanProperty isAmountLimitInfoOverlayVisible = new SimpleBooleanProperty();
private final BooleanProperty isAmountLimitInfoVisible = new SimpleBooleanProperty();
@Setter
private Monetary sellersReputationBasedQuoteSideAmount;
@Setter
private long sellersReputationScore;
@Setter
private String amountLimitInfoLink;
@Setter
private String linkToWikiText;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ protected void onViewAttached() {
amountLimitInfo.textProperty().bind(model.getAmountLimitInfo());
amountLimitInfoAmount.textProperty().bind(model.getAmountLimitInfoAmount());
amountLimitInfoOverlayInfo.textProperty().bind(model.getAmountLimitInfoOverlayInfo());
amountLimitInfoAmount.disableProperty().bind(model.getIsAmountHyperLinkDisabled());

amountLimitInfoAmount.managedProperty().bind(model.getIsAmountLimitInfoVisible().and(model.getAmountLimitInfoAmount().isEmpty().not()));
amountLimitInfoAmount.visibleProperty().bind(amountLimitInfoAmount.managedProperty());
Expand Down Expand Up @@ -136,6 +137,8 @@ protected void onViewDetached() {
amountLimitInfo.textProperty().unbind();
amountLimitInfoAmount.textProperty().unbind();
amountLimitInfoOverlayInfo.textProperty().unbind();
amountLimitInfoAmount.disableProperty().unbind();

amountLimitInfoAmount.managedProperty().unbind();
amountLimitInfoAmount.visibleProperty().unbind();
showOverlayHyperLink.managedProperty().unbind();
Expand Down
Loading

0 comments on commit ce17e20

Please sign in to comment.