Add support for referrer IDs at offers and trades

See: https://github.com/bisq-network/proposals/issues/28
This commit is contained in:
Manfred Karrer 2018-06-26 21:35:30 +02:00
parent 28a5d82fd0
commit 808915ec82
No known key found for this signature in database
GPG Key ID: 401250966A6B2C46
6 changed files with 138 additions and 16 deletions

View File

@ -248,7 +248,6 @@ public class BisqApp extends Application implements UncaughtExceptionHandler {
private void addSceneKeyEventHandler(Scene scene, Injector injector) {
scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEvent -> {
Utilities.isAltOrCtrlPressed(KeyCode.W, keyEvent);
if (Utilities.isCtrlPressed(KeyCode.W, keyEvent) ||
Utilities.isCtrlPressed(KeyCode.Q, keyEvent)) {
stop();

View File

@ -28,31 +28,62 @@ import bisq.desktop.main.MainView;
import bisq.desktop.main.market.offerbook.OfferBookChartView;
import bisq.desktop.main.market.spread.SpreadView;
import bisq.desktop.main.market.trades.TradesChartsView;
import bisq.desktop.main.offer.offerbook.OfferBook;
import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.BSFormatter;
import bisq.core.locale.Res;
import bisq.core.offer.OfferPayload;
import bisq.core.trade.statistics.TradeStatistics2;
import bisq.network.p2p.P2PService;
import bisq.common.util.Utilities;
import javax.inject.Inject;
import com.google.common.base.Joiner;
import org.apache.commons.lang3.StringUtils;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.beans.value.ChangeListener;
import javafx.event.EventHandler;
import java.util.List;
import java.util.stream.Collectors;
@FxmlView
public class MarketView extends ActivatableViewAndModel<TabPane, Activatable> {
@FXML
Tab offerBookTab, tradesTab, spreadTab;
private final ViewLoader viewLoader;
private final P2PService p2PService;
private final OfferBook offerBook;
private final BSFormatter formatter;
private final Navigation navigation;
private Navigation.Listener navigationListener;
private ChangeListener<Tab> tabChangeListener;
private EventHandler<KeyEvent> keyEventEventHandler;
private Scene scene;
@Inject
public MarketView(CachingViewLoader viewLoader, Navigation navigation) {
public MarketView(CachingViewLoader viewLoader, P2PService p2PService, OfferBook offerBook, BSFormatter formatter,
Navigation navigation) {
this.viewLoader = viewLoader;
this.p2PService = p2PService;
this.offerBook = offerBook;
this.formatter = formatter;
this.navigation = navigation;
}
@ -78,6 +109,22 @@ public class MarketView extends ActivatableViewAndModel<TabPane, Activatable> {
//noinspection unchecked
navigation.navigateTo(MainView.class, MarketView.class, SpreadView.class);
};
keyEventEventHandler = keyEvent -> {
if (Utilities.isCtrlPressed(KeyCode.T, keyEvent)) {
String allTradesWithReferralId = getAllTradesWithReferralId();
new Popup<>().message(StringUtils.abbreviate(allTradesWithReferralId, 600))
.actionButtonText(Res.get("shared.copyToClipboard"))
.onAction(() -> Utilities.copyToClipboard(allTradesWithReferralId))
.show();
} else if (Utilities.isCtrlPressed(KeyCode.O, keyEvent)) {
String allOffersWithReferralId = getAllOffersWithReferralId();
new Popup<>().message(StringUtils.abbreviate(allOffersWithReferralId, 600))
.actionButtonText(Res.get("shared.copyToClipboard"))
.onAction(() -> Utilities.copyToClipboard(allOffersWithReferralId))
.show();
}
};
}
@Override
@ -94,12 +141,21 @@ public class MarketView extends ActivatableViewAndModel<TabPane, Activatable> {
else
//noinspection unchecked
navigation.navigateTo(MainView.class, MarketView.class, SpreadView.class);
if (root.getScene() != null) {
scene = root.getScene();
scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
}
}
@Override
protected void deactivate() {
root.getSelectionModel().selectedItemProperty().removeListener(tabChangeListener);
navigation.removeListener(navigationListener);
// root.getScene() is null already so we used a field property
if (scene != null)
scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
}
private void loadView(Class<? extends View> viewClass) {
@ -119,4 +175,49 @@ public class MarketView extends ActivatableViewAndModel<TabPane, Activatable> {
root.getSelectionModel().select(tab);
}
private String getAllTradesWithReferralId() {
// We don't use the list from the tradeStatisticsManager as that has filtered the duplicates but we want to get
// all items of both traders in case the referral ID was only set by one trader.
// If both traders had set it the tradeStatistics is only delivered once.
// If both traders used a differnet refferral ID then we would get 2 objects.
List<String> list = p2PService.getP2PDataStorage().getPersistableNetworkPayloadList().getMap().values().stream()
.filter(e -> e instanceof TradeStatistics2)
.map(e -> (TradeStatistics2) e)
.filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap() != null)
.filter(tradeStatistics2 -> tradeStatistics2.getExtraDataMap().get(OfferPayload.REFERRAL_ID) != null)
.map(trade -> {
StringBuilder sb = new StringBuilder();
sb.append("Trade ID: ").append(trade.getOfferId()).append("\n")
.append("Date: ").append(formatter.formatDateTime(trade.getTradeDate())).append("\n")
.append("Market: ").append(formatter.getCurrencyPair(trade.getCurrencyCode())).append("\n")
.append("Price: ").append(formatter.formatPrice(trade.getTradePrice())).append("\n")
.append("Amount: ").append(formatter.formatCoin(trade.getTradeAmount())).append("\n")
.append("Volume: ").append(formatter.formatVolume(trade.getTradeVolume())).append("\n")
.append("Payment method: ").append(Res.get(trade.getOfferPaymentMethod())).append("\n")
.append("ReferralID: ").append(trade.getExtraDataMap().get(OfferPayload.REFERRAL_ID));
return sb.toString();
})
.collect(Collectors.toList());
return Joiner.on("\n\n").join(list);
}
private String getAllOffersWithReferralId() {
List<String> list = offerBook.getOfferBookListItems().stream()
.map(offerBookListItem -> offerBookListItem.getOffer())
.filter(offer -> offer.getOfferPayload().getExtraDataMap() != null)
.filter(offer -> offer.getOfferPayload().getExtraDataMap().get(OfferPayload.REFERRAL_ID) != null)
.map(offer -> {
StringBuilder sb = new StringBuilder();
sb.append("Offer ID: ").append(offer.getId()).append("\n")
.append("Type: ").append(offer.getDirection().name()).append("\n")
.append("Market: ").append(formatter.getCurrencyPair(offer.getCurrencyCode())).append("\n")
.append("Price: ").append(formatter.formatPrice(offer.getPrice())).append("\n")
.append("Amount: ").append(formatter.formatAmount(offer)).append(" BTC\n")
.append("Payment method: ").append(Res.get(offer.getPaymentMethod().getId())).append("\n")
.append("ReferralID: ").append(offer.getOfferPayload().getExtraDataMap().get(OfferPayload.REFERRAL_ID));
return sb.toString();
})
.collect(Collectors.toList());
return Joiner.on("\n\n").join(list);
}
}

View File

@ -49,6 +49,7 @@ import bisq.core.payment.SpecificBanksAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.handlers.TransactionResultHandler;
import bisq.core.trade.statistics.ReferralIdService;
import bisq.core.user.Preferences;
import bisq.core.user.User;
@ -86,6 +87,7 @@ import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@ -107,6 +109,7 @@ public abstract class EditableOfferDataModel extends OfferDataModel implements B
private final AccountAgeWitnessService accountAgeWitnessService;
private final TradeWalletService tradeWalletService;
private final FeeService feeService;
private final ReferralIdService referralIdService;
private final BSFormatter formatter;
private final String offerId;
private final BalanceListener btcBalanceListener;
@ -147,7 +150,7 @@ public abstract class EditableOfferDataModel extends OfferDataModel implements B
Preferences preferences, User user, KeyRing keyRing, P2PService p2PService,
PriceFeedService priceFeedService, FilterManager filterManager,
AccountAgeWitnessService accountAgeWitnessService, TradeWalletService tradeWalletService,
FeeService feeService, BSFormatter formatter) {
FeeService feeService, ReferralIdService referralIdService, BSFormatter formatter) {
super(btcWalletService);
this.openOfferManager = openOfferManager;
@ -161,6 +164,7 @@ public abstract class EditableOfferDataModel extends OfferDataModel implements B
this.accountAgeWitnessService = accountAgeWitnessService;
this.tradeWalletService = tradeWalletService;
this.feeService = feeService;
this.referralIdService = referralIdService;
this.formatter = formatter;
offerId = Utilities.getRandomPrefix(5, 8) + "-" +
@ -357,13 +361,19 @@ public abstract class EditableOfferDataModel extends OfferDataModel implements B
long lowerClosePrice = 0;
long upperClosePrice = 0;
String hashOfChallenge = null;
HashMap<String, String> extraDataMap = null;
Map<String, String> extraDataMap = null;
if (CurrencyUtil.isFiatCurrency(currencyCode)) {
extraDataMap = new HashMap<>();
final String myWitnessHashAsHex = accountAgeWitnessService.getMyWitnessHashAsHex(paymentAccount.getPaymentAccountPayload());
extraDataMap.put(OfferPayload.ACCOUNT_AGE_WITNESS_HASH, myWitnessHashAsHex);
}
if (referralIdService.getOptionalReferralId().isPresent()) {
if (extraDataMap == null)
extraDataMap = new HashMap<>();
extraDataMap.put(OfferPayload.REFERRAL_ID, referralIdService.getOptionalReferralId().get());
}
Coin buyerSecurityDepositAsCoin = buyerSecurityDeposit.get();
checkArgument(buyerSecurityDepositAsCoin.compareTo(Restrictions.getMaxBuyerSecurityDeposit()) <= 0,
"securityDeposit must be not exceed " +

View File

@ -28,6 +28,7 @@ import bisq.core.offer.OpenOfferManager;
import bisq.core.payment.AccountAgeWitnessService;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.ReferralIdService;
import bisq.core.user.Preferences;
import bisq.core.user.User;
@ -45,7 +46,7 @@ import com.google.inject.Inject;
class CreateOfferDataModel extends EditableOfferDataModel {
@Inject
public CreateOfferDataModel(OpenOfferManager openOfferManager, BtcWalletService btcWalletService, BsqWalletService bsqWalletService, Preferences preferences, User user, KeyRing keyRing, P2PService p2PService, PriceFeedService priceFeedService, FilterManager filterManager, AccountAgeWitnessService accountAgeWitnessService, TradeWalletService tradeWalletService, FeeService feeService, BSFormatter formatter) {
super(openOfferManager, btcWalletService, bsqWalletService, preferences, user, keyRing, p2PService, priceFeedService, filterManager, accountAgeWitnessService, tradeWalletService, feeService, formatter);
public CreateOfferDataModel(OpenOfferManager openOfferManager, BtcWalletService btcWalletService, BsqWalletService bsqWalletService, Preferences preferences, User user, KeyRing keyRing, P2PService p2PService, PriceFeedService priceFeedService, FilterManager filterManager, AccountAgeWitnessService accountAgeWitnessService, TradeWalletService tradeWalletService, FeeService feeService, ReferralIdService referralIdService, BSFormatter formatter) {
super(openOfferManager, btcWalletService, bsqWalletService, preferences, user, keyRing, p2PService, priceFeedService, filterManager, accountAgeWitnessService, tradeWalletService, feeService, referralIdService, formatter);
}
}

View File

@ -33,6 +33,7 @@ import bisq.core.payment.AccountAgeWitnessService;
import bisq.core.payment.PaymentAccount;
import bisq.core.provider.fee.FeeService;
import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.ReferralIdService;
import bisq.core.user.Preferences;
import bisq.core.user.User;
@ -52,8 +53,8 @@ class EditOpenOfferDataModel extends EditableOfferDataModel {
private OpenOffer.State initialState;
@Inject
EditOpenOfferDataModel(OpenOfferManager openOfferManager, BtcWalletService btcWalletService, BsqWalletService bsqWalletService, Preferences preferences, User user, KeyRing keyRing, P2PService p2PService, PriceFeedService priceFeedService, FilterManager filterManager, AccountAgeWitnessService accountAgeWitnessService, TradeWalletService tradeWalletService, FeeService feeService, BSFormatter formatter) {
super(openOfferManager, btcWalletService, bsqWalletService, preferences, user, keyRing, p2PService, priceFeedService, filterManager, accountAgeWitnessService, tradeWalletService, feeService, formatter);
EditOpenOfferDataModel(OpenOfferManager openOfferManager, BtcWalletService btcWalletService, BsqWalletService bsqWalletService, Preferences preferences, User user, KeyRing keyRing, P2PService p2PService, PriceFeedService priceFeedService, FilterManager filterManager, AccountAgeWitnessService accountAgeWitnessService, TradeWalletService tradeWalletService, FeeService feeService, ReferralIdService referralIdService, BSFormatter formatter) {
super(openOfferManager, btcWalletService, bsqWalletService, preferences, user, keyRing, p2PService, priceFeedService, filterManager, accountAgeWitnessService, tradeWalletService, feeService, referralIdService, formatter);
}
public void initWithData(OpenOffer openOffer) {

View File

@ -40,6 +40,7 @@ import bisq.core.locale.LanguageUtil;
import bisq.core.locale.Res;
import bisq.core.locale.TradeCurrency;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.statistics.ReferralIdService;
import bisq.core.user.BlockChainExplorer;
import bisq.core.user.Preferences;
@ -95,11 +96,11 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
private CheckBox useAnimationsCheckBox, autoSelectArbitratorsCheckBox, showOwnOffersInOfferBook, sortMarketCurrenciesNumericallyCheckBox, useCustomFeeCheckbox;
private int gridRow = 0;
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField;
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, referralIdInputTextField;
private ChangeListener<Boolean> transactionFeeFocusedListener;
private final Preferences preferences;
private final FeeService feeService;
private final BisqEnvironment bisqEnvironment;
private final ReferralIdService referralIdService;
private final BSFormatter formatter;
private ListView<FiatCurrency> fiatCurrenciesListView;
@ -117,7 +118,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
private ObservableList<CryptoCurrency> allCryptoCurrencies;
private ObservableList<TradeCurrency> tradeCurrencies;
private InputTextField deviationInputTextField;
private ChangeListener<String> deviationListener, ignoreTradersListListener;
private ChangeListener<String> deviationListener, ignoreTradersListListener, referralIdListener;
private ChangeListener<Boolean> deviationFocusedListener;
private ChangeListener<Boolean> useCustomFeeCheckboxListener;
private ChangeListener<Number> transactionFeeChangeListener;
@ -127,12 +128,12 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public PreferencesView(Preferences preferences, FeeService feeService,
BisqEnvironment bisqEnvironment, BSFormatter formatter) {
public PreferencesView(Preferences preferences, FeeService feeService, ReferralIdService referralIdService,
BSFormatter formatter) {
super();
this.preferences = preferences;
this.feeService = feeService;
this.bisqEnvironment = bisqEnvironment;
this.referralIdService = referralIdService;
this.formatter = formatter;
}
@ -176,7 +177,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
///////////////////////////////////////////////////////////////////////////////////////////
private void initializeGeneralOptions() {
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 7, Res.get("setting.preferences.general"));
TitledGroupBg titledGroupBg = addTitledGroupBg(root, gridRow, 8, Res.get("setting.preferences.general"));
GridPane.setColumnSpan(titledGroupBg, 4);
// selectBaseCurrencyNetwork
@ -298,6 +299,13 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
preferences.setIgnoreTradersList(Arrays.asList(StringUtils.deleteWhitespace(newValue)
.replace(":9999", "").replace(".onion", "")
.split(",")));
// referralId
referralIdInputTextField = addLabelInputTextField(root, ++gridRow, Res.get("setting.preferences.refererId")).second;
referralIdListener = (observable, oldValue, newValue) -> {
if (!newValue.equals(oldValue))
referralIdService.setReferralId(newValue);
};
}
private void initializeDisplayCurrencies() {
@ -496,7 +504,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
transactionFeeInputTextField.setText(String.valueOf(getTxFeeForWithdrawalPerByte()));
ignoreTradersListInputTextField.setText(preferences.getIgnoreTradersList().stream().collect(Collectors.joining(", ")));
referralIdService.getOptionalReferralId().ifPresent(referralId -> referralIdInputTextField.setText(referralId));
userLanguageComboBox.setItems(languageCodes);
userLanguageComboBox.getSelectionModel().select(preferences.getUserLanguage());
userLanguageComboBox.setConverter(new StringConverter<String>() {
@ -574,6 +582,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener);
ignoreTradersListInputTextField.textProperty().addListener(ignoreTradersListListener);
useCustomFeeCheckbox.selectedProperty().addListener(useCustomFeeCheckboxListener);
referralIdInputTextField.textProperty().addListener(referralIdListener);
}
private Coin getTxFeeForWithdrawalPerByte() {
@ -678,6 +687,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Activatab
feeService.feeUpdateCounterProperty().removeListener(transactionFeeChangeListener);
ignoreTradersListInputTextField.textProperty().removeListener(ignoreTradersListListener);
useCustomFeeCheckbox.selectedProperty().removeListener(useCustomFeeCheckboxListener);
referralIdInputTextField.textProperty().removeListener(referralIdListener);
}
private void deactivateDisplayCurrencies() {