mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 07:07:43 +01:00
Add fee check to take offer, display spinner and txt, cleanup eventhandlers, hide support tickets tab for arbitr. if not arbitr.
This commit is contained in:
parent
eefafab977
commit
2d7aecd2ed
41 changed files with 583 additions and 286 deletions
|
@ -44,6 +44,8 @@ import java.util.concurrent.*;
|
|||
public class Utilities {
|
||||
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
|
||||
private static long lastTimeStamp = System.currentTimeMillis();
|
||||
public static final String LB = System.getProperty("line.separator");
|
||||
public static final String LB2 = LB + LB;
|
||||
|
||||
public static String objectToJson(Object object) {
|
||||
Gson gson = new GsonBuilder()
|
||||
|
|
|
@ -284,27 +284,24 @@ public class WalletService {
|
|||
// Listener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AddressConfidenceListener addAddressConfidenceListener(AddressConfidenceListener listener) {
|
||||
public void addAddressConfidenceListener(AddressConfidenceListener listener) {
|
||||
addressConfidenceListeners.add(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void removeAddressConfidenceListener(AddressConfidenceListener listener) {
|
||||
addressConfidenceListeners.remove(listener);
|
||||
}
|
||||
|
||||
public TxConfidenceListener addTxConfidenceListener(TxConfidenceListener listener) {
|
||||
public void addTxConfidenceListener(TxConfidenceListener listener) {
|
||||
txConfidenceListeners.add(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void removeTxConfidenceListener(TxConfidenceListener listener) {
|
||||
txConfidenceListeners.remove(listener);
|
||||
}
|
||||
|
||||
public BalanceListener addBalanceListener(BalanceListener listener) {
|
||||
public void addBalanceListener(BalanceListener listener) {
|
||||
balanceListeners.add(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void removeBalanceListener(BalanceListener listener) {
|
||||
|
@ -667,12 +664,12 @@ public class WalletService {
|
|||
private class BitsquareWalletEventListener extends AbstractWalletEventListener {
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
notifyBalanceListeners();
|
||||
notifyBalanceListeners(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
notifyBalanceListeners();
|
||||
notifyBalanceListeners(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -691,7 +688,7 @@ public class WalletService {
|
|||
txConfidenceListener.onTransactionConfidenceChanged(tx.getConfidence()));
|
||||
}
|
||||
|
||||
private void notifyBalanceListeners() {
|
||||
private void notifyBalanceListeners(Transaction tx) {
|
||||
for (BalanceListener balanceListener : balanceListeners) {
|
||||
Coin balance;
|
||||
if (balanceListener.getAddress() != null)
|
||||
|
@ -699,7 +696,7 @@ public class WalletService {
|
|||
else
|
||||
balance = getAvailableBalance();
|
||||
|
||||
balanceListener.onBalanceChanged(balance);
|
||||
balanceListener.onBalanceChanged(balance, tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package io.bitsquare.btc.listeners;
|
|||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
public class BalanceListener {
|
||||
private Address address;
|
||||
|
@ -35,6 +36,6 @@ public class BalanceListener {
|
|||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ import io.bitsquare.trade.Trade;
|
|||
import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -51,12 +52,12 @@ public class SetupDepositBalanceListener extends TradeTask {
|
|||
|
||||
WalletService walletService = processModel.getWalletService();
|
||||
Address address = walletService.getAddressEntryByOfferId(trade.getId()).getAddress();
|
||||
balanceListener = walletService.addBalanceListener(new BalanceListener(address) {
|
||||
balanceListener = new BalanceListener(address) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
});
|
||||
};
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
|
||||
tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
|
||||
|
|
|
@ -153,7 +153,7 @@ public class BitsquareApp extends Application {
|
|||
mainView.setPersistedFilesCorrupted(corruptedDatabaseFiles);
|
||||
});*/
|
||||
|
||||
scene = new Scene(mainView.getRoot(), 1100, 740);
|
||||
scene = new Scene(mainView.getRoot(), 1060, 740);
|
||||
scene.getStylesheets().setAll(
|
||||
"/io/bitsquare/gui/bitsquare.css",
|
||||
"/io/bitsquare/gui/images.css");
|
||||
|
|
|
@ -36,7 +36,6 @@ bg color of non edit textFields: fafafa
|
|||
-bs-red-soft: derive(-bs-error-red, 60%);
|
||||
-bs-orange: #dd8f05;
|
||||
|
||||
|
||||
-bs-blue-transparent: #0f87c344;
|
||||
-bs-green-transparent: #00aa3344;
|
||||
|
||||
|
@ -678,10 +677,6 @@ textfield */
|
|||
-fx-background-color: -bs-content-bg-grey;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* TitledGroupBg */
|
||||
#titled-group-bg-label {
|
||||
-fx-font-weight: bold;
|
||||
|
@ -876,3 +871,22 @@ textfield */
|
|||
-fx-font-size: 16;
|
||||
-fx-alignment: center;
|
||||
}
|
||||
|
||||
/********************************************************************************************************************
|
||||
*
|
||||
* Popups
|
||||
*
|
||||
********************************************************************************************************************/
|
||||
|
||||
#popup-headline {
|
||||
-fx-font-size: 18;
|
||||
-fx-text-fill: #333;
|
||||
}
|
||||
|
||||
#popup-message {
|
||||
-fx-font-size: 15;
|
||||
}
|
||||
|
||||
#popup-button {
|
||||
-fx-font-size: 15;
|
||||
}
|
|
@ -28,6 +28,7 @@ import javafx.scene.layout.AnchorPane;
|
|||
import javafx.scene.paint.Color;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
public class BalanceTextField extends AnchorPane {
|
||||
|
||||
|
@ -64,7 +65,7 @@ public class BalanceTextField extends AnchorPane {
|
|||
|
||||
balanceListener = new BalanceListener(address) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
};
|
||||
|
@ -72,7 +73,7 @@ public class BalanceTextField extends AnchorPane {
|
|||
updateBalance(walletService.getBalanceForAddress(address));
|
||||
}
|
||||
|
||||
public void disarm() {
|
||||
public void cleanup() {
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,14 @@ import javafx.scene.layout.AnchorPane;
|
|||
import javafx.scene.paint.Color;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
public class BalanceWithConfirmationTextField extends AnchorPane {
|
||||
|
||||
private static WalletService walletService;
|
||||
private BalanceListener balanceListener;
|
||||
private AddressConfidenceListener confidenceListener;
|
||||
|
||||
public static void setWalletService(WalletService walletService) {
|
||||
BalanceWithConfirmationTextField.walletService = walletService;
|
||||
|
@ -77,22 +80,29 @@ public class BalanceWithConfirmationTextField extends AnchorPane {
|
|||
getChildren().addAll(textField, progressIndicator);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
walletService.removeAddressConfidenceListener(confidenceListener);
|
||||
}
|
||||
|
||||
public void setup(Address address, BSFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
walletService.addAddressConfidenceListener(new AddressConfidenceListener(address) {
|
||||
confidenceListener = new AddressConfidenceListener(address) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
updateConfidence(confidence);
|
||||
}
|
||||
});
|
||||
};
|
||||
walletService.addAddressConfidenceListener(confidenceListener);
|
||||
updateConfidence(walletService.getConfidenceForAddress(address));
|
||||
|
||||
walletService.addBalanceListener(new BalanceListener(address) {
|
||||
balanceListener = new BalanceListener(address) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
});
|
||||
};
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
updateBalance(walletService.getBalanceForAddress(address));
|
||||
}
|
||||
|
||||
|
|
|
@ -408,7 +408,7 @@ public class MainViewModel implements ViewModel {
|
|||
|
||||
walletService.addBalanceListener(new BalanceListener() {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -42,7 +42,6 @@ public class AccountView extends ActivatableView<TabPane, AccountViewModel> {
|
|||
@FXML
|
||||
Tab accountSettingsTab;
|
||||
|
||||
|
||||
private Navigation.Listener navigationListener;
|
||||
private ChangeListener<Tab> tabChangeListener;
|
||||
|
||||
|
|
|
@ -19,18 +19,10 @@ package io.bitsquare.gui.main.account;
|
|||
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.user.User;
|
||||
|
||||
class AccountViewModel implements ViewModel {
|
||||
|
||||
private final User user;
|
||||
|
||||
@Inject
|
||||
public AccountViewModel(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
boolean getNeedRegistration() {
|
||||
return user.getAccountId() == null;
|
||||
public AccountViewModel() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ public class ArbitratorRegistrationView extends ActivatableViewAndModel<VBox, Ar
|
|||
|
||||
private ChangeListener<Arbitrator> arbitratorChangeListener;
|
||||
private EnterPrivKeyPopup enterPrivKeyPopup;
|
||||
private ListChangeListener<String> listChangeListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -84,6 +85,8 @@ public class ArbitratorRegistrationView extends ActivatableViewAndModel<VBox, Ar
|
|||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
model.myArbitratorProperty.removeListener(arbitratorChangeListener);
|
||||
languagesListView.getItems().removeListener(listChangeListener);
|
||||
}
|
||||
|
||||
public void onTabSelection(boolean isSelectedTab) {
|
||||
|
@ -106,8 +109,8 @@ public class ArbitratorRegistrationView extends ActivatableViewAndModel<VBox, Ar
|
|||
private void updateLanguageList() {
|
||||
languagesListView.setItems(model.languageCodes);
|
||||
languagesListView.setPrefHeight(languagesListView.getItems().size() * Layout.LIST_ROW_HEIGHT + 2);
|
||||
languagesListView.getItems().addListener((ListChangeListener<String>)
|
||||
c -> languagesListView.setPrefHeight(languagesListView.getItems().size() * Layout.LIST_ROW_HEIGHT + 2));
|
||||
listChangeListener = c -> languagesListView.setPrefHeight(languagesListView.getItems().size() * Layout.LIST_ROW_HEIGHT + 2);
|
||||
languagesListView.getItems().addListener(listChangeListener);
|
||||
}
|
||||
|
||||
private void buildUI() {
|
||||
|
|
|
@ -26,7 +26,9 @@ import io.bitsquare.gui.popups.Popup;
|
|||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.locale.LanguageUtil;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
|
@ -52,6 +54,9 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
|||
private TableView<ArbitratorListItem> table;
|
||||
private int gridRow = 0;
|
||||
private CheckBox autoSelectAllMatchingCheckBox;
|
||||
private ListChangeListener<String> listChangeListener;
|
||||
private ListChangeListener<String> languageCodesListChangeListener;
|
||||
private ChangeListener<Boolean> isSelectedChangeListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -68,13 +73,12 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
|||
public void initialize() {
|
||||
addLanguageGroup();
|
||||
addArbitratorsGroup();
|
||||
listChangeListener = c -> languagesListView.setPrefHeight(languagesListView.getItems().size() * Layout.LIST_ROW_HEIGHT + 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
languagesListView.getItems().addListener((ListChangeListener<String>) c -> {
|
||||
languagesListView.setPrefHeight(languagesListView.getItems().size() * Layout.LIST_ROW_HEIGHT + 2);
|
||||
});
|
||||
languagesListView.getItems().addListener(listChangeListener);
|
||||
languageComboBox.setItems(model.allLanguageCodes);
|
||||
languagesListView.setItems(model.languageCodes);
|
||||
languagesListView.setPrefHeight(languagesListView.getItems().size() * Layout.LIST_ROW_HEIGHT + 2);
|
||||
|
@ -85,6 +89,8 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
|||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
languagesListView.getItems().removeListener(listChangeListener);
|
||||
model.languageCodes.removeListener(languageCodesListChangeListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -222,6 +228,7 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
|||
return new TableCell<ArbitratorListItem, ArbitratorListItem>() {
|
||||
private final CheckBox checkBox = new CheckBox();
|
||||
private TableRow tableRow;
|
||||
private BooleanProperty selectedProperty;
|
||||
|
||||
private void updateDisableState(final ArbitratorListItem item) {
|
||||
boolean selected = model.isAcceptedArbitrator(item.arbitrator);
|
||||
|
@ -261,8 +268,12 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
|||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
model.languageCodes.addListener((ListChangeListener<String>) c -> updateDisableState(item));
|
||||
item.isSelectedProperty().addListener((observable, oldValue, newValue) -> checkBox.setSelected(newValue));
|
||||
selectedProperty = item.isSelectedProperty();
|
||||
languageCodesListChangeListener = c -> updateDisableState(item);
|
||||
model.languageCodes.addListener(languageCodesListChangeListener);
|
||||
|
||||
isSelectedChangeListener = (observable, oldValue, newValue) -> checkBox.setSelected(newValue);
|
||||
selectedProperty.addListener(isSelectedChangeListener);
|
||||
|
||||
checkBox.setSelected(model.isAcceptedArbitrator(item.arbitrator));
|
||||
checkBox.setOnAction(e -> {
|
||||
|
@ -281,6 +292,10 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
|||
updateDisableState(item);
|
||||
setGraphic(checkBox);
|
||||
} else {
|
||||
model.languageCodes.removeListener(languageCodesListChangeListener);
|
||||
if (selectedProperty != null)
|
||||
selectedProperty.removeListener(isSelectedChangeListener);
|
||||
|
||||
setGraphic(null);
|
||||
|
||||
if (checkBox != null)
|
||||
|
|
|
@ -45,7 +45,6 @@ class ChangePasswordViewModel implements ViewModel {
|
|||
repeatedPasswordField.addListener((ov) -> saveButtonDisabled.set(!validate()));
|
||||
}
|
||||
|
||||
|
||||
boolean requestSavePassword() {
|
||||
if (validate()) {
|
||||
savePassword(passwordField.get());
|
||||
|
|
|
@ -30,6 +30,7 @@ import io.bitsquare.gui.popups.Popup;
|
|||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.gui.util.validation.InputValidator;
|
||||
import io.bitsquare.gui.util.validation.PasswordValidator;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ProgressIndicator;
|
||||
|
@ -54,6 +55,8 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
|
|||
private TitledGroupBg headline;
|
||||
private int gridRow = 0;
|
||||
private Label repeatedPasswordLabel;
|
||||
private ChangeListener<String> passwordFieldChangeListener;
|
||||
private ChangeListener<String> repeatedPasswordFieldChangeListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -72,17 +75,13 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
|
|||
headline = addTitledGroupBg(root, gridRow, 3, "");
|
||||
passwordField = addLabelPasswordTextField(root, gridRow, "Enter password:", Layout.FIRST_ROW_DISTANCE).second;
|
||||
passwordField.setValidator(passwordValidator);
|
||||
passwordField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
validatePasswords();
|
||||
});
|
||||
passwordFieldChangeListener = (observable, oldValue, newValue) -> validatePasswords();
|
||||
|
||||
Tuple2<Label, PasswordTextField> tuple2 = addLabelPasswordTextField(root, ++gridRow, "Repeat password:");
|
||||
repeatedPasswordLabel = tuple2.first;
|
||||
repeatedPasswordField = tuple2.second;
|
||||
repeatedPasswordField.setValidator(passwordValidator);
|
||||
repeatedPasswordField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
validatePasswords();
|
||||
});
|
||||
repeatedPasswordFieldChangeListener = (observable, oldValue, newValue) -> validatePasswords();
|
||||
|
||||
Tuple3<Button, ProgressIndicator, Label> tuple = addButtonWithStatus(root, ++gridRow, "", 0);
|
||||
pwButton = tuple.first;
|
||||
|
@ -105,7 +104,6 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
|
|||
else
|
||||
keyCrypterScrypt = ScryptUtil.getKeyCrypterScrypt();
|
||||
|
||||
|
||||
ScryptUtil.deriveKeyWithScrypt(keyCrypterScrypt, passwordField.getText(), aesKey -> {
|
||||
deriveStatusLabel.setText("");
|
||||
progressIndicator.setVisible(false);
|
||||
|
@ -169,10 +167,16 @@ public class PasswordView extends ActivatableView<GridPane, Void> {
|
|||
|
||||
@Override
|
||||
protected void activate() {
|
||||
passwordField.textProperty().addListener(passwordFieldChangeListener);
|
||||
repeatedPasswordField.textProperty().addListener(repeatedPasswordFieldChangeListener);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
passwordField.textProperty().removeListener(passwordFieldChangeListener);
|
||||
repeatedPasswordField.textProperty().removeListener(repeatedPasswordFieldChangeListener);
|
||||
|
||||
}
|
||||
|
||||
private void validatePasswords() {
|
||||
|
|
|
@ -64,6 +64,7 @@ public class PaymentAccountView extends ActivatableViewAndModel<GridPane, Paymen
|
|||
private Button addAccountButton;
|
||||
private Button saveNewAccountButton;
|
||||
private int gridRow = 0;
|
||||
private ListChangeListener<PaymentAccount> paymentAccountListChangeListener;
|
||||
|
||||
@Inject
|
||||
public PaymentAccountView(PaymentAccountViewModel model,
|
||||
|
@ -90,6 +91,7 @@ public class PaymentAccountView extends ActivatableViewAndModel<GridPane, Paymen
|
|||
@Override
|
||||
public void initialize() {
|
||||
buildForm();
|
||||
paymentAccountListChangeListener = c -> paymentAccountsComboBox.setDisable(model.getPaymentAccounts().size() == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -102,13 +104,13 @@ public class PaymentAccountView extends ActivatableViewAndModel<GridPane, Paymen
|
|||
paymentAccountsComboBox.setOnAction(paymentAccountsComboBoxHandler);
|
||||
paymentAccountsComboBox.setVisibleRowCount(20);
|
||||
|
||||
model.getPaymentAccounts().addListener(
|
||||
(ListChangeListener<PaymentAccount>) c -> paymentAccountsComboBox.setDisable(model.getPaymentAccounts().size() == 0));
|
||||
model.getPaymentAccounts().addListener(paymentAccountListChangeListener);
|
||||
paymentAccountsComboBox.setDisable(model.getPaymentAccounts().size() == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
model.getPaymentAccounts().removeListener(paymentAccountListChangeListener);
|
||||
paymentAccountsComboBox.setOnAction(null);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ import io.bitsquare.gui.popups.WalletPasswordPopup;
|
|||
import io.bitsquare.gui.util.Layout;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.DatePicker;
|
||||
import javafx.scene.control.TextArea;
|
||||
|
@ -61,6 +62,12 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||
|
||||
private int gridRow = 0;
|
||||
private DeterministicSeed keyChainSeed;
|
||||
private ChangeListener<Boolean> seedWordsValidChangeListener;
|
||||
private SimpleBooleanProperty seedWordsValid;
|
||||
private SimpleBooleanProperty dateValid;
|
||||
private ChangeListener<String> seedWordsTextAreaChangeListener;
|
||||
private ChangeListener<Boolean> datePickerChangeListener;
|
||||
private ChangeListener<LocalDate> dateChangeListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -105,6 +112,11 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
seedWordsValid.removeListener(seedWordsValidChangeListener);
|
||||
seedWordsTextArea.textProperty().removeListener(seedWordsTextAreaChangeListener);
|
||||
dateValid.removeListener(datePickerChangeListener);
|
||||
datePicker.valueProperty().removeListener(dateChangeListener);
|
||||
|
||||
seedWordsTextArea.setText("");
|
||||
datePicker.setValue(null);
|
||||
restoreButton.disableProperty().unbind();
|
||||
|
@ -134,15 +146,18 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||
restoreButton.setOnAction(e -> onRestore());
|
||||
|
||||
BooleanProperty seedWordsEdited = new SimpleBooleanProperty();
|
||||
BooleanProperty seedWordsValid = new SimpleBooleanProperty(true);
|
||||
seedWordsValid.addListener((observable, oldValue, newValue) -> {
|
||||
|
||||
seedWordsValid = new SimpleBooleanProperty(true);
|
||||
seedWordsValidChangeListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
seedWordsTextArea.getStyleClass().remove("validation_error");
|
||||
} else {
|
||||
seedWordsTextArea.getStyleClass().add("validation_error");
|
||||
}
|
||||
});
|
||||
seedWordsTextArea.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
};
|
||||
seedWordsValid.addListener(seedWordsValidChangeListener);
|
||||
|
||||
seedWordsTextAreaChangeListener = (observable, oldValue, newValue) -> {
|
||||
seedWordsEdited.set(true);
|
||||
try {
|
||||
MnemonicCode codec = new MnemonicCode();
|
||||
|
@ -154,18 +169,22 @@ public class SeedWordsView extends ActivatableView<GridPane, Void> {
|
|||
|
||||
if (creationDate.equals(datePicker.getValue()))
|
||||
datePicker.setValue(null);
|
||||
});
|
||||
};
|
||||
seedWordsTextArea.textProperty().addListener(seedWordsTextAreaChangeListener);
|
||||
|
||||
BooleanProperty dateValid = new SimpleBooleanProperty(true);
|
||||
dateValid.addListener((observable, oldValue, newValue) -> {
|
||||
dateValid = new SimpleBooleanProperty(true);
|
||||
datePickerChangeListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue)
|
||||
datePicker.getStyleClass().remove("validation_error");
|
||||
else
|
||||
datePicker.getStyleClass().add("validation_error");
|
||||
});
|
||||
datePicker.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
};
|
||||
dateValid.addListener(datePickerChangeListener);
|
||||
|
||||
dateChangeListener = (observable, oldValue, newValue) -> {
|
||||
dateValid.set(newValue != null && !newValue.isAfter(LocalDate.now()));
|
||||
});
|
||||
};
|
||||
datePicker.valueProperty().addListener(dateChangeListener);
|
||||
|
||||
restoreButton.disableProperty().bind(createBooleanBinding(() -> !seedWordsValid.get() || !dateValid.get() || !seedWordsEdited.get(),
|
||||
seedWordsValid, dateValid, seedWordsEdited));
|
||||
|
|
|
@ -29,6 +29,7 @@ import io.bitsquare.gui.main.account.content.password.PasswordView;
|
|||
import io.bitsquare.gui.main.account.content.paymentsaccount.PaymentAccountView;
|
||||
import io.bitsquare.gui.main.account.content.seedwords.SeedWordsView;
|
||||
import io.bitsquare.gui.util.Colors;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
|
@ -95,11 +96,22 @@ public class AccountSettingsView extends ActivatableViewAndModel {
|
|||
selecteedViewClass = viewPath.get(3);
|
||||
loadView(selecteedViewClass);
|
||||
}
|
||||
paymentAccount.activate();
|
||||
arbitratorSelection.activate();
|
||||
password.activate();
|
||||
seedWords.activate();
|
||||
backup.activate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
navigation.removeListener(listener);
|
||||
|
||||
paymentAccount.deactivate();
|
||||
arbitratorSelection.deactivate();
|
||||
password.deactivate();
|
||||
seedWords.deactivate();
|
||||
backup.deactivate();
|
||||
}
|
||||
|
||||
private void loadView(Class<? extends View> viewClass) {
|
||||
|
@ -133,7 +145,14 @@ public class AccountSettingsView extends ActivatableViewAndModel {
|
|||
|
||||
class MenuItem extends ToggleButton {
|
||||
|
||||
private final ChangeListener<Boolean> selectedPropertyChangeListener;
|
||||
private final ChangeListener<Boolean> disablePropertyChangeListener;
|
||||
private Navigation navigation;
|
||||
private Class<? extends View> viewClass;
|
||||
|
||||
MenuItem(Navigation navigation, ToggleGroup toggleGroup, String title, Class<? extends View> viewClass, AwesomeIcon awesomeIcon) {
|
||||
this.navigation = navigation;
|
||||
this.viewClass = viewClass;
|
||||
|
||||
setToggleGroup(toggleGroup);
|
||||
setText(title);
|
||||
|
@ -151,10 +170,7 @@ class MenuItem extends ToggleButton {
|
|||
icon.setMaxWidth(25);
|
||||
setGraphic(icon);
|
||||
|
||||
setOnAction((event) ->
|
||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, viewClass));
|
||||
|
||||
selectedProperty().addListener((ov, oldValue, newValue) -> {
|
||||
selectedPropertyChangeListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
setId("account-settings-item-background-selected");
|
||||
icon.setTextFill(Colors.BLUE);
|
||||
|
@ -162,9 +178,9 @@ class MenuItem extends ToggleButton {
|
|||
setId("account-settings-item-background-active");
|
||||
icon.setTextFill(Paint.valueOf("#333"));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
disableProperty().addListener((ov, oldValue, newValue) -> {
|
||||
disablePropertyChangeListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
setId("account-settings-item-background-disabled");
|
||||
icon.setTextFill(Paint.valueOf("#ccc"));
|
||||
|
@ -172,7 +188,19 @@ class MenuItem extends ToggleButton {
|
|||
setId("account-settings-item-background-active");
|
||||
icon.setTextFill(Paint.valueOf("#333"));
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public void activate() {
|
||||
setOnAction((event) -> navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, viewClass));
|
||||
selectedProperty().addListener(selectedPropertyChangeListener);
|
||||
disableProperty().addListener(disablePropertyChangeListener);
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
setOnAction(null);
|
||||
selectedProperty().removeListener(selectedPropertyChangeListener);
|
||||
disableProperty().removeListener(disablePropertyChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
AnchorPane.topAnchor="0.0"
|
||||
xmlns:fx="http://javafx.com/fxml">
|
||||
|
||||
<Tab fx:id="tradersDisputesTab" text="Traders support tickets" closable="false"></Tab>
|
||||
<Tab fx:id="arbitratorsDisputesTab" text="Arbitrators support tickets" closable="false"/>
|
||||
<Tab fx:id="tradersDisputesTab" text="Support tickets" closable="false"></Tab>
|
||||
|
||||
</TabPane>
|
||||
|
|
|
@ -40,7 +40,9 @@ import javax.inject.Inject;
|
|||
public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable> {
|
||||
|
||||
@FXML
|
||||
Tab tradersDisputesTab, arbitratorsDisputesTab;
|
||||
Tab tradersDisputesTab;
|
||||
|
||||
private Tab arbitratorsDisputesTab;
|
||||
|
||||
private final Navigation navigation;
|
||||
private final ArbitratorManager arbitratorManager;
|
||||
|
@ -51,6 +53,7 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
|
|||
private Tab currentTab;
|
||||
private final ViewLoader viewLoader;
|
||||
private MapChangeListener<NodeAddress, Arbitrator> arbitratorMapChangeListener;
|
||||
private boolean isArbitrator;
|
||||
|
||||
@Inject
|
||||
public DisputesView(CachingViewLoader viewLoader, Navigation navigation, ArbitratorManager arbitratorManager, KeyRing keyRing) {
|
||||
|
@ -58,6 +61,8 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
|
|||
this.navigation = navigation;
|
||||
this.arbitratorManager = arbitratorManager;
|
||||
this.keyRing = keyRing;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,12 +84,16 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
|
|||
}
|
||||
|
||||
private void updateArbitratorsDisputesTabDisableState() {
|
||||
boolean isArbitrator = arbitratorManager.getArbitratorsObservableMap().values().stream()
|
||||
isArbitrator = arbitratorManager.getArbitratorsObservableMap().values().stream()
|
||||
.filter(e -> e.getPubKeyRing() != null && e.getPubKeyRing().equals(keyRing.getPubKeyRing()))
|
||||
.findAny().isPresent();
|
||||
arbitratorsDisputesTab.setDisable(!isArbitrator);
|
||||
if (arbitratorsDisputesTab.getContent() != null)
|
||||
arbitratorsDisputesTab.getContent().setDisable(!isArbitrator);
|
||||
|
||||
if (arbitratorsDisputesTab == null && isArbitrator) {
|
||||
arbitratorsDisputesTab = new Tab("Arbitrators support tickets");
|
||||
arbitratorsDisputesTab.setClosable(false);
|
||||
root.getTabs().add(arbitratorsDisputesTab);
|
||||
tradersDisputesTab.setText("Traders support tickets");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,10 +105,10 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
|
|||
root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener);
|
||||
navigation.addListener(navigationListener);
|
||||
|
||||
if (root.getSelectionModel().getSelectedItem() == tradersDisputesTab)
|
||||
navigation.navigateTo(MainView.class, DisputesView.class, TraderDisputeView.class);
|
||||
else if (root.getSelectionModel().getSelectedItem() == arbitratorsDisputesTab)
|
||||
if (arbitratorsDisputesTab != null && root.getSelectionModel().getSelectedItem() == arbitratorsDisputesTab)
|
||||
navigation.navigateTo(MainView.class, DisputesView.class, ArbitratorDisputeView.class);
|
||||
else
|
||||
navigation.navigateTo(MainView.class, DisputesView.class, TraderDisputeView.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -117,9 +126,9 @@ public class DisputesView extends ActivatableViewAndModel<TabPane, Activatable>
|
|||
|
||||
View view = viewLoader.load(viewClass);
|
||||
|
||||
if (view instanceof ArbitratorDisputeView)
|
||||
if (arbitratorsDisputesTab != null && view instanceof ArbitratorDisputeView)
|
||||
currentTab = arbitratorsDisputesTab;
|
||||
else if (view instanceof TraderDisputeView)
|
||||
else
|
||||
currentTab = tradersDisputesTab;
|
||||
|
||||
currentTab.setContent(view.getRoot());
|
||||
|
|
|
@ -38,6 +38,7 @@ import io.bitsquare.gui.util.GUIUtil;
|
|||
import io.bitsquare.p2p.network.Connection;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ListChangeListener;
|
||||
|
@ -56,6 +57,7 @@ import javafx.util.Callback;
|
|||
import org.reactfx.util.FxTimer;
|
||||
import org.reactfx.util.Timer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
@ -92,6 +94,13 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
private VBox messagesInputBox;
|
||||
private ProgressIndicator sendMsgProgressIndicator;
|
||||
private Label sendMsgInfoLabel;
|
||||
private ChangeListener<Boolean> arrivedPropertyListener;
|
||||
private ChangeListener<Boolean> storedInMailboxPropertyListener;
|
||||
@Nullable
|
||||
private DisputeDirectMessage disputeDirectMessage;
|
||||
private ListChangeListener<DisputeDirectMessage> disputeDirectMessageListListener;
|
||||
private ChangeListener<String> inputTextAreaListener;
|
||||
private ChangeListener<Boolean> selectedDisputeClosedPropertyListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -159,6 +168,23 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
@Override
|
||||
protected void deactivate() {
|
||||
disputesTable.getSelectionModel().selectedItemProperty().removeListener(disputeChangeListener);
|
||||
|
||||
if (disputeDirectMessage != null) {
|
||||
disputeDirectMessage.arrivedProperty().removeListener(arrivedPropertyListener);
|
||||
disputeDirectMessage.storedInMailboxProperty().removeListener(storedInMailboxPropertyListener);
|
||||
}
|
||||
|
||||
if (selectedDispute != null) {
|
||||
selectedDispute.isClosedProperty().removeListener(selectedDisputeClosedPropertyListener);
|
||||
ObservableList<DisputeDirectMessage> disputeDirectMessages = selectedDispute.getDisputeDirectMessagesAsObservableList();
|
||||
if (disputeDirectMessages != null) {
|
||||
disputeDirectMessages.removeListener(disputeDirectMessageListListener);
|
||||
}
|
||||
}
|
||||
|
||||
if (inputTextArea != null)
|
||||
inputTextArea.textProperty().removeListener(inputTextAreaListener);
|
||||
|
||||
}
|
||||
|
||||
protected void setFilteredListPredicate(FilteredList<Dispute> filteredList) {
|
||||
|
@ -175,7 +201,12 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
}
|
||||
|
||||
private void onSendMessage(String inputText, Dispute dispute) {
|
||||
DisputeDirectMessage disputeDirectMessage = disputeManager.sendDisputeDirectMessage(dispute, inputText, new ArrayList<>(tempAttachments));
|
||||
if (disputeDirectMessage != null) {
|
||||
disputeDirectMessage.arrivedProperty().removeListener(arrivedPropertyListener);
|
||||
disputeDirectMessage.storedInMailboxProperty().removeListener(storedInMailboxPropertyListener);
|
||||
}
|
||||
|
||||
disputeDirectMessage = disputeManager.sendDisputeDirectMessage(dispute, inputText, new ArrayList<>(tempAttachments));
|
||||
tempAttachments.clear();
|
||||
scrollToBottom();
|
||||
|
||||
|
@ -192,19 +223,21 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
sendMsgProgressIndicator.setManaged(true);
|
||||
});
|
||||
|
||||
disputeDirectMessage.arrivedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
arrivedPropertyListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
hideSendMsgInfo(timer);
|
||||
}
|
||||
});
|
||||
disputeDirectMessage.storedInMailboxProperty().addListener((observable, oldValue, newValue) -> {
|
||||
};
|
||||
disputeDirectMessage.arrivedProperty().addListener(arrivedPropertyListener);
|
||||
storedInMailboxPropertyListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
sendMsgInfoLabel.setVisible(true);
|
||||
sendMsgInfoLabel.setManaged(true);
|
||||
sendMsgInfoLabel.setText("Receiver is not online. Message is saved to his mailbox.");
|
||||
hideSendMsgInfo(timer);
|
||||
}
|
||||
});
|
||||
};
|
||||
disputeDirectMessage.storedInMailboxProperty().addListener(storedInMailboxPropertyListener);
|
||||
}
|
||||
|
||||
private void hideSendMsgInfo(Timer timer) {
|
||||
|
@ -221,7 +254,8 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
}
|
||||
|
||||
private void onCloseDispute(Dispute dispute) {
|
||||
disputeSummaryPopup.onFinalizeDispute(() -> messagesAnchorPane.getChildren().remove(messagesInputBox)).show(dispute);
|
||||
disputeSummaryPopup.onFinalizeDispute(() -> messagesAnchorPane.getChildren().remove(messagesInputBox))
|
||||
.show(dispute);
|
||||
}
|
||||
|
||||
private void onRequestUpload() {
|
||||
|
@ -275,6 +309,14 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
}
|
||||
|
||||
private void onSelectDispute(Dispute dispute) {
|
||||
if (selectedDispute != null) {
|
||||
selectedDispute.isClosedProperty().removeListener(selectedDisputeClosedPropertyListener);
|
||||
ObservableList<DisputeDirectMessage> disputeDirectMessages = selectedDispute.getDisputeDirectMessagesAsObservableList();
|
||||
if (disputeDirectMessages != null) {
|
||||
disputeDirectMessages.removeListener(disputeDirectMessageListListener);
|
||||
}
|
||||
}
|
||||
|
||||
if (dispute == null) {
|
||||
if (root.getChildren().size() > 1)
|
||||
root.getChildren().remove(1);
|
||||
|
@ -283,7 +325,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
} else if (selectedDispute != dispute) {
|
||||
this.selectedDispute = dispute;
|
||||
|
||||
boolean isTrader = disputeManager.isTrader(dispute);
|
||||
boolean isTrader = disputeManager.isTrader(selectedDispute);
|
||||
|
||||
TableGroupHeadline tableGroupHeadline = new TableGroupHeadline();
|
||||
tableGroupHeadline.setText("Messages");
|
||||
|
@ -293,10 +335,11 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
AnchorPane.setBottomAnchor(tableGroupHeadline, 0d);
|
||||
AnchorPane.setLeftAnchor(tableGroupHeadline, 0d);
|
||||
|
||||
ObservableList<DisputeDirectMessage> list = dispute.getDisputeDirectMessagesAsObservableList();
|
||||
SortedList<DisputeDirectMessage> sortedList = new SortedList<>(list);
|
||||
ObservableList<DisputeDirectMessage> disputeDirectMessages = selectedDispute.getDisputeDirectMessagesAsObservableList();
|
||||
SortedList<DisputeDirectMessage> sortedList = new SortedList<>(disputeDirectMessages);
|
||||
sortedList.setComparator((o1, o2) -> o1.getDate().compareTo(o2.getDate()));
|
||||
list.addListener((ListChangeListener<DisputeDirectMessage>) c -> scrollToBottom());
|
||||
disputeDirectMessageListListener = c -> scrollToBottom();
|
||||
disputeDirectMessages.addListener(disputeDirectMessageListListener);
|
||||
messageListView = new ListView<>(sortedList);
|
||||
messageListView.setId("message-list-view");
|
||||
messageListView.prefWidthProperty().bind(root.widthProperty());
|
||||
|
@ -315,11 +358,13 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
|
||||
Button sendButton = new Button("Send");
|
||||
sendButton.setDefaultButton(true);
|
||||
sendButton.setOnAction(e -> onSendMessage(inputTextArea.getText(), dispute));
|
||||
sendButton.setOnAction(e -> onSendMessage(inputTextArea.getText(), selectedDispute));
|
||||
sendButton.setDisable(true);
|
||||
inputTextArea.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
sendButton.setDisable(newValue.length() == 0 && tempAttachments.size() == 0 && dispute.disputeResultProperty().get() == null);
|
||||
});
|
||||
inputTextAreaListener = (observable, oldValue, newValue) ->
|
||||
sendButton.setDisable(newValue.length() == 0
|
||||
&& tempAttachments.size() == 0 &&
|
||||
selectedDispute.disputeResultProperty().get() == null);
|
||||
inputTextArea.textProperty().addListener(inputTextAreaListener);
|
||||
|
||||
Button uploadButton = new Button("Add attachments");
|
||||
uploadButton.setOnAction(e -> onRequestUpload());
|
||||
|
@ -335,19 +380,20 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
sendMsgProgressIndicator.setVisible(false);
|
||||
sendMsgProgressIndicator.setManaged(false);
|
||||
|
||||
dispute.isClosedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
selectedDisputeClosedPropertyListener = (observable, oldValue, newValue) -> {
|
||||
messagesInputBox.setVisible(!newValue);
|
||||
messagesInputBox.setManaged(!newValue);
|
||||
AnchorPane.setBottomAnchor(messageListView, newValue ? 0d : 120d);
|
||||
});
|
||||
if (!dispute.isClosed()) {
|
||||
};
|
||||
selectedDispute.isClosedProperty().addListener(selectedDisputeClosedPropertyListener);
|
||||
if (!selectedDispute.isClosed()) {
|
||||
HBox buttonBox = new HBox();
|
||||
buttonBox.setSpacing(10);
|
||||
buttonBox.getChildren().addAll(sendButton, uploadButton, sendMsgProgressIndicator, sendMsgInfoLabel);
|
||||
|
||||
if (!isTrader) {
|
||||
Button closeDisputeButton = new Button("Close ticket");
|
||||
closeDisputeButton.setOnAction(e -> onCloseDispute(dispute));
|
||||
closeDisputeButton.setOnAction(e -> onCloseDispute(selectedDispute));
|
||||
closeDisputeButton.setDefaultButton(true);
|
||||
Pane spacer = new Pane();
|
||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||
|
@ -375,6 +421,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
@Override
|
||||
public ListCell<DisputeDirectMessage> call(ListView<DisputeDirectMessage> list) {
|
||||
return new ListCell<DisputeDirectMessage>() {
|
||||
public ChangeListener<Number> sendMsgProgressIndicatorListener;
|
||||
final Pane bg = new Pane();
|
||||
final ImageView arrow = new ImageView();
|
||||
final Label headerLabel = new Label();
|
||||
|
@ -434,14 +481,15 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
else
|
||||
arrow.setId("bubble_arrow_blue_right");
|
||||
|
||||
sendMsgProgressIndicator.progressProperty().addListener((observable, oldValue, newValue) -> {
|
||||
sendMsgProgressIndicatorListener = (observable, oldValue, newValue) -> {
|
||||
if ((double) oldValue == -1 && (double) newValue == 0) {
|
||||
if (item.arrivedProperty().get())
|
||||
showArrivedIcon();
|
||||
else if (item.storedInMailboxProperty().get())
|
||||
showMailboxIcon();
|
||||
}
|
||||
});
|
||||
};
|
||||
sendMsgProgressIndicator.progressProperty().addListener(sendMsgProgressIndicatorListener);
|
||||
|
||||
if (item.arrivedProperty().get())
|
||||
showArrivedIcon();
|
||||
|
@ -527,6 +575,9 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
// TODO There are still some cell rendering issues on updates
|
||||
setGraphic(messageAnchorPane);
|
||||
} else {
|
||||
if (sendMsgProgressIndicator != null)
|
||||
sendMsgProgressIndicator.progressProperty().removeListener(sendMsgProgressIndicatorListener);
|
||||
|
||||
messageAnchorPane.prefWidthProperty().unbind();
|
||||
|
||||
AnchorPane.clearConstraints(bg);
|
||||
|
@ -726,18 +777,26 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
|||
return new TableCell<Dispute, Dispute>() {
|
||||
|
||||
|
||||
public ReadOnlyBooleanProperty closedProperty;
|
||||
public ChangeListener<Boolean> listener;
|
||||
|
||||
@Override
|
||||
public void updateItem(final Dispute item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty) {
|
||||
item.isClosedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
listener = (observable, oldValue, newValue) -> {
|
||||
setText(newValue ? "Closed" : "Open");
|
||||
getTableRow().setOpacity(newValue ? 0.4 : 1);
|
||||
});
|
||||
};
|
||||
closedProperty = item.isClosedProperty();
|
||||
closedProperty.addListener(listener);
|
||||
boolean isClosed = item.isClosed();
|
||||
setText(isClosed ? "Closed" : "Open");
|
||||
getTableRow().setOpacity(isClosed ? 0.4 : 1);
|
||||
} else {
|
||||
if (closedProperty != null)
|
||||
closedProperty.removeListener(listener);
|
||||
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import javafx.beans.property.StringProperty;
|
|||
import javafx.scene.control.Label;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -58,13 +59,13 @@ public class ReservedListItem {
|
|||
|
||||
// balance
|
||||
balanceLabel = new Label();
|
||||
balanceListener = walletService.addBalanceListener(new BalanceListener(getAddress()) {
|
||||
balanceListener = new BalanceListener(getAddress()) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
updateBalance(walletService.getBalanceForAddress(getAddress()));
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ import javafx.scene.control.*;
|
|||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
@ -95,7 +96,7 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
|||
table.getSortOrder().add(dateColumn);
|
||||
balanceListener = new BalanceListener() {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateList();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -154,13 +154,13 @@ public class TransactionsListItem {
|
|||
Tooltip.install(progressIndicator, tooltip);
|
||||
|
||||
if (address != null) {
|
||||
txConfidenceListener = walletService.addTxConfidenceListener(new TxConfidenceListener(txId) {
|
||||
txConfidenceListener = new TxConfidenceListener(txId) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
updateConfidence(confidence);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
walletService.addTxConfidenceListener(txConfidenceListener);
|
||||
updateConfidence(transaction.getConfidence());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import io.bitsquare.gui.util.BSFormatter;
|
|||
import javafx.scene.control.Label;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
public class WithdrawalListItem {
|
||||
private final BalanceListener balanceListener;
|
||||
|
@ -42,12 +43,13 @@ public class WithdrawalListItem {
|
|||
|
||||
// balance
|
||||
balanceLabel = new Label();
|
||||
balanceListener = walletService.addBalanceListener(new BalanceListener(getAddress()) {
|
||||
balanceListener = new BalanceListener(getAddress()) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
});
|
||||
};
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
|
||||
updateBalance(walletService.getBalanceForAddress(getAddress()));
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||
|
||||
balanceListener = new BalanceListener() {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateList();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -32,6 +32,7 @@ import io.bitsquare.gui.popups.Popup;
|
|||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.scene.control.Tab;
|
||||
import javafx.scene.control.TabPane;
|
||||
|
@ -46,7 +47,7 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
private TakeOfferView takeOfferView;
|
||||
private AnchorPane createOfferPane;
|
||||
private AnchorPane takeOfferPane;
|
||||
private Navigation.Listener listener;
|
||||
private Navigation.Listener navigationListener;
|
||||
private Offer offer;
|
||||
|
||||
private final ViewLoader viewLoader;
|
||||
|
@ -56,6 +57,8 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
private Tab takeOfferTab, createOfferTab, offerBookTab;
|
||||
private TradeCurrency tradeCurrency;
|
||||
private boolean createOfferViewOpen, takeOfferViewOpen;
|
||||
private ChangeListener<Tab> tabChangeListener;
|
||||
private ListChangeListener<Tab> tabListChangeListener;
|
||||
|
||||
protected OfferView(ViewLoader viewLoader, Navigation navigation, MarketPriceFeed marketPriceFeed) {
|
||||
this.viewLoader = viewLoader;
|
||||
|
@ -66,43 +69,32 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
|
||||
@Override
|
||||
protected void initialize() {
|
||||
listener = viewPath -> {
|
||||
navigationListener = viewPath -> {
|
||||
if (viewPath.size() == 3 && viewPath.indexOf(this.getClass()) == 1)
|
||||
loadView(viewPath.tip());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
// We need to remove open validation error popups
|
||||
// UserThread.execute needed as focus-out event is called after selectedIndexProperty changed
|
||||
// TODO Find a way to do that in the InputTextField directly, but a tab change does not trigger any event...
|
||||
TabPane tabPane = root;
|
||||
tabPane.getSelectionModel().selectedItemProperty()
|
||||
.addListener((observableValue, oldValue, newValue) -> {
|
||||
UserThread.execute(InputTextField::hideErrorMessageDisplay);
|
||||
if (newValue != null) {
|
||||
if (newValue.equals(createOfferTab) && createOfferView != null) {
|
||||
createOfferView.onTabSelected(true);
|
||||
} else if (newValue.equals(takeOfferTab) && takeOfferView != null) {
|
||||
takeOfferView.onTabSelected(true);
|
||||
} else if (newValue.equals(offerBookTab) && offerBookView != null) {
|
||||
offerBookView.onTabSelected(true);
|
||||
}
|
||||
}
|
||||
if (oldValue != null) {
|
||||
if (oldValue.equals(createOfferTab) && createOfferView != null) {
|
||||
createOfferView.onTabSelected(false);
|
||||
} else if (oldValue.equals(takeOfferTab) && takeOfferView != null) {
|
||||
takeOfferView.onTabSelected(false);
|
||||
} else if (oldValue.equals(offerBookTab) && offerBookView != null) {
|
||||
offerBookView.onTabSelected(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// We want to get informed when a tab get closed
|
||||
tabPane.getTabs().addListener((ListChangeListener<Tab>) change -> {
|
||||
tabChangeListener = (observableValue, oldValue, newValue) -> {
|
||||
UserThread.execute(InputTextField::hideErrorMessageDisplay);
|
||||
if (newValue != null) {
|
||||
if (newValue.equals(createOfferTab) && createOfferView != null) {
|
||||
createOfferView.onTabSelected(true);
|
||||
} else if (newValue.equals(takeOfferTab) && takeOfferView != null) {
|
||||
takeOfferView.onTabSelected(true);
|
||||
} else if (newValue.equals(offerBookTab) && offerBookView != null) {
|
||||
offerBookView.onTabSelected(true);
|
||||
}
|
||||
}
|
||||
if (oldValue != null) {
|
||||
if (oldValue.equals(createOfferTab) && createOfferView != null) {
|
||||
createOfferView.onTabSelected(false);
|
||||
} else if (oldValue.equals(takeOfferTab) && takeOfferView != null) {
|
||||
takeOfferView.onTabSelected(false);
|
||||
} else if (oldValue.equals(offerBookTab) && offerBookView != null) {
|
||||
offerBookView.onTabSelected(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
tabListChangeListener = (ListChangeListener<Tab>) change -> {
|
||||
change.next();
|
||||
List<? extends Tab> removedTabs = change.getRemoved();
|
||||
if (removedTabs.size() == 1) {
|
||||
|
@ -111,17 +103,23 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
else if (removedTabs.get(0).getContent().equals(takeOfferPane))
|
||||
onTakeOfferViewRemoved();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
tradeCurrency = CurrencyUtil.getDefaultTradeCurrency();
|
||||
|
||||
navigation.addListener(listener);
|
||||
root.getSelectionModel().selectedItemProperty().addListener(tabChangeListener);
|
||||
root.getTabs().addListener(tabListChangeListener);
|
||||
navigation.addListener(navigationListener);
|
||||
navigation.navigateTo(MainView.class, this.getClass(), OfferBookView.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
navigation.removeListener(listener);
|
||||
navigation.removeListener(navigationListener);
|
||||
root.getSelectionModel().selectedItemProperty().removeListener(tabChangeListener);
|
||||
root.getTabs().removeListener(tabListChangeListener);
|
||||
}
|
||||
|
||||
private void loadView(Class<? extends View> viewClass) {
|
||||
|
@ -140,7 +138,7 @@ public abstract class OfferView extends ActivatableView<TabPane, Void> {
|
|||
tabPane.getTabs().add(offerBookTab);
|
||||
offerBookView = (OfferBookView) view;
|
||||
offerBookView.onTabSelected(true);
|
||||
|
||||
|
||||
OfferActionHandler offerActionHandler = new OfferActionHandler() {
|
||||
@Override
|
||||
public void onCreateOffer(TradeCurrency tradeCurrency) {
|
||||
|
|
|
@ -46,8 +46,8 @@ import javafx.beans.property.*;
|
|||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.SetChangeListener;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.utils.ExchangeRate;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -102,7 +102,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
final ObservableList<PaymentAccount> paymentAccounts = FXCollections.observableArrayList();
|
||||
|
||||
private PaymentAccount paymentAccount;
|
||||
private WalletEventListener walletEventListener;
|
||||
private boolean isTabSelected;
|
||||
|
||||
|
||||
|
@ -134,42 +133,36 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
|
||||
balanceListener = new BalanceListener(getAddressEntry().getAddress()) {
|
||||
@Override
|
||||
public void onBalanceChanged(@NotNull Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
|
||||
if (preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET) {
|
||||
SettableFuture<Coin> future = blockchainService.requestFee(tx.getHashAsString());
|
||||
Futures.addCallback(future, new FutureCallback<Coin>() {
|
||||
public void onSuccess(Coin fee) {
|
||||
UserThread.execute(() -> feeFromFundingTxProperty.set(fee));
|
||||
}
|
||||
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
UserThread.execute(() -> new Popup()
|
||||
.warning("We did not get a response for the request of the mining fee used " +
|
||||
"in the funding transaction.\n\n" +
|
||||
"Are you sure you used a sufficiently high fee of at least " +
|
||||
formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + "?")
|
||||
.actionButtonText("Yes, I used a sufficiently high fee.")
|
||||
.onAction(() -> feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx()))
|
||||
.closeButtonText("No. Let's cancel that payment.")
|
||||
.onClose(() -> feeFromFundingTxProperty.set(Coin.ZERO))
|
||||
.show());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
paymentAccountsChangeListener = change -> paymentAccounts.setAll(user.getPaymentAccounts());
|
||||
walletEventListener = new WalletEventListener() {
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
requestFeeFromBlockchain(tx.getHashAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReorganize(Wallet wallet) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWalletChanged(Wallet wallet) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScriptsChanged(Wallet wallet, List<Script> scripts, boolean isAddingScripts) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onKeysAdded(List<ECKey> keys) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -203,14 +196,12 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
|
||||
private void addListeners() {
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
walletService.getWallet().addEventListener(walletEventListener);
|
||||
user.getPaymentAccountsAsObservable().addListener(paymentAccountsChangeListener);
|
||||
}
|
||||
|
||||
|
||||
private void removeListeners() {
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
walletService.getWallet().removeEventListener(walletEventListener);
|
||||
user.getPaymentAccountsAsObservable().removeListener(paymentAccountsChangeListener);
|
||||
}
|
||||
|
||||
|
@ -281,9 +272,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
}
|
||||
|
||||
private void doPlaceOffer(Offer offer, TransactionResultHandler resultHandler) {
|
||||
openOfferManager.onPlaceOffer(offer,
|
||||
resultHandler
|
||||
);
|
||||
openOfferManager.onPlaceOffer(offer, resultHandler);
|
||||
}
|
||||
|
||||
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
||||
|
@ -349,25 +338,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
// Utils
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void requestFeeFromBlockchain(String transactionId) {
|
||||
if (preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET) {
|
||||
SettableFuture<Coin> future = blockchainService.requestFee(transactionId);
|
||||
Futures.addCallback(future, new FutureCallback<Coin>() {
|
||||
public void onSuccess(Coin fee) {
|
||||
UserThread.execute(() -> feeFromFundingTxProperty.set(fee));
|
||||
}
|
||||
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
UserThread.execute(() -> new Popup()
|
||||
.warning("We did not get a response for the request of the mining fee used in the funding transaction.")
|
||||
.show());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
|
||||
}
|
||||
}
|
||||
|
||||
void calculateVolume() {
|
||||
if (priceAsFiat.get() != null &&
|
||||
amountAsCoin.get() != null &&
|
||||
|
@ -400,6 +370,9 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
|||
|
||||
private void updateBalance(Coin balance) {
|
||||
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
||||
|
||||
if (isWalletFunded.get())
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
public Coin getOfferFeeAsCoin() {
|
||||
|
|
|
@ -451,8 +451,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
if (!model.dataModel.isFeeFromFundingTxSufficient()) {
|
||||
new Popup().warning("The mining fee from your funding transaction is not sufficiently high.\n\n" +
|
||||
"You need to use at least a mining fee of " +
|
||||
model.formatCoin(FeePolicy.getMinRequiredFeeForFundingTx()) + ".\n\n" +
|
||||
"The fee used in your funding transaction was only " + model.formatCoin(newValue) + ".\n\n" +
|
||||
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + ".\n\n" +
|
||||
"The fee used in your funding transaction was only " +
|
||||
model.formatter.formatCoinWithCode(newValue) + ".\n\n" +
|
||||
"The trade transactions might take too much time to be included in " +
|
||||
"a block if the fee is too low.\n" +
|
||||
"Please check at your external wallet that you set the required fee and " +
|
||||
|
@ -680,8 +681,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||
placeOfferButton.setVisible(false);
|
||||
placeOfferButton.setOnAction(e -> onPlaceOffer());
|
||||
placeOfferSpinner = placeOfferTuple.second;
|
||||
placeOfferSpinner.setPrefSize(18, 18);
|
||||
placeOfferSpinnerInfoLabel = placeOfferTuple.third;
|
||||
placeOfferSpinnerInfoLabel.setText(BSResources.get("createOffer.fundsBox.placeOfferSpinnerInfo"));
|
||||
placeOfferSpinnerInfoLabel.textProperty().bind(model.placeOfferSpinnerInfoText);
|
||||
placeOfferSpinnerInfoLabel.setVisible(false);
|
||||
|
||||
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel"));
|
||||
|
|
|
@ -64,6 +64,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
final StringProperty errorMessage = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
final StringProperty tradeCurrencyCode = new SimpleStringProperty();
|
||||
final StringProperty placeOfferSpinnerInfoText = new SimpleStringProperty();
|
||||
|
||||
final BooleanProperty isPlaceOfferButtonVisible = new SimpleBooleanProperty(false);
|
||||
final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(true);
|
||||
|
@ -105,7 +106,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
|
||||
@Inject
|
||||
public CreateOfferViewModel(CreateOfferDataModel dataModel, FiatValidator fiatValidator, BtcValidator btcValidator,
|
||||
P2PService p2PService,
|
||||
P2PService p2PService,
|
||||
BSFormatter formatter) {
|
||||
super(dataModel);
|
||||
|
||||
|
@ -231,15 +232,30 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
priceAsFiatListener = (ov, oldValue, newValue) -> price.set(formatter.formatFiat(newValue));
|
||||
volumeAsFiatListener = (ov, oldValue, newValue) -> volume.set(formatter.formatFiat(newValue));
|
||||
|
||||
isWalletFundedListener = (ov, oldValue, newValue) -> updateButtonDisableState();
|
||||
feeFromFundingTxListener = (ov, oldValue, newValue) -> updateButtonDisableState();
|
||||
isWalletFundedListener = (ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
isPlaceOfferSpinnerVisible.set(true);
|
||||
placeOfferSpinnerInfoText.set("Checking funding tx miner fee...");
|
||||
};
|
||||
feeFromFundingTxListener = (ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
if (newValue.isPositive()) {
|
||||
isPlaceOfferSpinnerVisible.set(false);
|
||||
placeOfferSpinnerInfoText.set("");
|
||||
}
|
||||
};
|
||||
requestPlaceOfferSuccessListener = (ov, oldValue, newValue) -> {
|
||||
isPlaceOfferButtonVisible.set(!newValue);
|
||||
isPlaceOfferSpinnerVisible.set(false);
|
||||
if (newValue) {
|
||||
isPlaceOfferButtonVisible.set(!newValue);
|
||||
isPlaceOfferSpinnerVisible.set(false);
|
||||
placeOfferSpinnerInfoText.set("");
|
||||
}
|
||||
};
|
||||
requestPlaceOfferErrorMessageListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue != null)
|
||||
if (newValue != null) {
|
||||
isPlaceOfferSpinnerVisible.set(false);
|
||||
placeOfferSpinnerInfoText.set("");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -256,6 +272,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
dataModel.minAmountAsCoin.addListener(minAmountAsCoinListener);
|
||||
dataModel.priceAsFiat.addListener(priceAsFiatListener);
|
||||
dataModel.volumeAsFiat.addListener(volumeAsFiatListener);
|
||||
|
||||
dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
|
||||
dataModel.isWalletFunded.addListener(isWalletFundedListener);
|
||||
requestPlaceOfferSuccess.addListener(requestPlaceOfferSuccessListener);
|
||||
|
@ -275,7 +292,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
dataModel.priceAsFiat.removeListener(priceAsFiatListener);
|
||||
dataModel.volumeAsFiat.removeListener(volumeAsFiatListener);
|
||||
|
||||
dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
|
||||
dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
|
||||
dataModel.isWalletFunded.removeListener(isWalletFundedListener);
|
||||
requestPlaceOfferSuccess.removeListener(requestPlaceOfferSuccessListener);
|
||||
errorMessage.removeListener(requestPlaceOfferErrorMessageListener);
|
||||
|
@ -302,6 +319,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
errorMessage.set(null);
|
||||
isPlaceOfferSpinnerVisible.set(true);
|
||||
requestPlaceOfferSuccess.set(false);
|
||||
placeOfferSpinnerInfoText.set(BSResources.get("createOffer.fundsBox.placeOfferSpinnerInfo"));
|
||||
|
||||
errorMessageListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
|
@ -315,7 +333,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||
}
|
||||
};
|
||||
offer.errorMessageProperty().addListener(errorMessageListener);
|
||||
dataModel.onPlaceOffer(offer, (transaction) -> requestPlaceOfferSuccess.set(true));
|
||||
dataModel.onPlaceOffer(offer, transaction -> requestPlaceOfferSuccess.set(true));
|
||||
}
|
||||
|
||||
void onShowPayFundsScreen() {
|
||||
|
|
|
@ -110,6 +110,7 @@ class OfferBookViewModel extends ActivatableViewModel {
|
|||
@Override
|
||||
protected void deactivate() {
|
||||
btcCode.unbind();
|
||||
offerBookListItems.removeListener(listChangeListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -168,7 +169,6 @@ class OfferBookViewModel extends ActivatableViewModel {
|
|||
|
||||
public ObservableList<TradeCurrency> getTradeCurrencies() {
|
||||
ObservableList<TradeCurrency> list = preferences.getTradeCurrenciesAsObservable();
|
||||
/* list.add(0, new AllTradeCurrenciesEntry());*/
|
||||
return list;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,18 +17,22 @@
|
|||
|
||||
package io.bitsquare.gui.main.offer.takeoffer;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import com.google.inject.Inject;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.arbitration.Arbitrator;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.TradeWalletService;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.*;
|
||||
import io.bitsquare.btc.blockchain.BlockchainService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.btc.pricefeed.MarketPriceFeed;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.handlers.ResultHandler;
|
||||
import io.bitsquare.gui.common.model.ActivatableDataModel;
|
||||
import io.bitsquare.gui.popups.Popup;
|
||||
import io.bitsquare.gui.popups.WalletPasswordPopup;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.locale.TradeCurrency;
|
||||
import io.bitsquare.payment.PaymentAccount;
|
||||
import io.bitsquare.payment.PaymentMethod;
|
||||
|
@ -65,6 +69,8 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
private final WalletPasswordPopup walletPasswordPopup;
|
||||
private final Preferences preferences;
|
||||
private MarketPriceFeed marketPriceFeed;
|
||||
private BlockchainService blockchainService;
|
||||
private BSFormatter formatter;
|
||||
|
||||
private final Coin offerFeeAsCoin;
|
||||
private final Coin networkFeeAsCoin;
|
||||
|
@ -79,6 +85,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> feeFromFundingTxProperty = new SimpleObjectProperty(Coin.NEGATIVE_SATOSHI);
|
||||
|
||||
private BalanceListener balanceListener;
|
||||
private PaymentAccount paymentAccount;
|
||||
|
@ -93,7 +100,8 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
@Inject
|
||||
TakeOfferDataModel(TradeManager tradeManager, TradeWalletService tradeWalletService,
|
||||
WalletService walletService, User user, WalletPasswordPopup walletPasswordPopup,
|
||||
Preferences preferences, MarketPriceFeed marketPriceFeed) {
|
||||
Preferences preferences, MarketPriceFeed marketPriceFeed, BlockchainService blockchainService,
|
||||
BSFormatter formatter) {
|
||||
this.tradeManager = tradeManager;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.walletService = walletService;
|
||||
|
@ -101,6 +109,8 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
this.walletPasswordPopup = walletPasswordPopup;
|
||||
this.preferences = preferences;
|
||||
this.marketPriceFeed = marketPriceFeed;
|
||||
this.blockchainService = blockchainService;
|
||||
this.formatter = formatter;
|
||||
|
||||
offerFeeAsCoin = FeePolicy.getCreateOfferFee();
|
||||
networkFeeAsCoin = FeePolicy.getFixedTxFeeForTrades();
|
||||
|
@ -116,6 +126,12 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
addListeners();
|
||||
updateBalance(walletService.getBalanceForAddress(addressEntry.getAddress()));
|
||||
|
||||
// TODO In case that we have funded but restarted, or canceled but took again the offer we would need to
|
||||
// store locally the result when we received the funding tx(s).
|
||||
// For now we just ignore that rare case and bypass the check by setting a sufficient value
|
||||
if (isWalletFunded.get())
|
||||
feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
|
||||
|
||||
if (isTabSelected)
|
||||
marketPriceFeed.setCurrencyCode(offer.getCurrencyCode());
|
||||
}
|
||||
|
@ -153,8 +169,32 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
|
||||
balanceListener = new BalanceListener(addressEntry.getAddress()) {
|
||||
@Override
|
||||
public void onBalanceChanged(@NotNull Coin balance) {
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
|
||||
if (preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET) {
|
||||
SettableFuture<Coin> future = blockchainService.requestFee(tx.getHashAsString());
|
||||
Futures.addCallback(future, new FutureCallback<Coin>() {
|
||||
public void onSuccess(Coin fee) {
|
||||
UserThread.execute(() -> feeFromFundingTxProperty.set(fee));
|
||||
}
|
||||
|
||||
public void onFailure(@NotNull Throwable throwable) {
|
||||
UserThread.execute(() -> new Popup()
|
||||
.warning("We did not get a response for the request of the mining fee used " +
|
||||
"in the funding transaction.\n\n" +
|
||||
"Are you sure you used a sufficiently high fee of at least " +
|
||||
formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + "?")
|
||||
.actionButtonText("Yes, I used a sufficiently high fee.")
|
||||
.onAction(() -> feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx()))
|
||||
.closeButtonText("No. Let's cancel that payment.")
|
||||
.onClose(() -> feeFromFundingTxProperty.set(Coin.ZERO))
|
||||
.show());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -233,6 +273,10 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
return user.getAcceptedArbitrators().size() > 0;
|
||||
}
|
||||
|
||||
boolean isFeeFromFundingTxSufficient() {
|
||||
return feeFromFundingTxProperty.get().compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Bindings, listeners
|
||||
|
@ -279,6 +323,9 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
|||
|
||||
private void updateBalance(@NotNull Coin balance) {
|
||||
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
||||
|
||||
if (isWalletFunded.get())
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
boolean isMinAmountLessOrEqualAmount() {
|
||||
|
|
|
@ -32,6 +32,8 @@ import io.bitsquare.gui.main.MainView;
|
|||
import io.bitsquare.gui.main.account.AccountView;
|
||||
import io.bitsquare.gui.main.account.content.arbitratorselection.ArbitratorSelectionView;
|
||||
import io.bitsquare.gui.main.account.settings.AccountSettingsView;
|
||||
import io.bitsquare.gui.main.funds.FundsView;
|
||||
import io.bitsquare.gui.main.funds.withdrawal.WithdrawalView;
|
||||
import io.bitsquare.gui.main.offer.OfferView;
|
||||
import io.bitsquare.gui.main.portfolio.PortfolioView;
|
||||
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView;
|
||||
|
@ -51,6 +53,7 @@ import javafx.scene.layout.*;
|
|||
import javafx.scene.text.Font;
|
||||
import javafx.stage.Window;
|
||||
import javafx.util.StringConverter;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.controlsfx.control.PopOver;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
|
@ -99,6 +102,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
private Subscription showCheckAvailabilityPopupSubscription;
|
||||
private SimpleBooleanProperty errorPopupDisplayed;
|
||||
private Popup isOfferAvailablePopup;
|
||||
private ChangeListener<Coin> feeFromFundingTxListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -233,6 +237,30 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
paymentAccountsComboBox.setItems(model.getPossiblePaymentAccounts());
|
||||
paymentAccountsComboBox.getSelectionModel().select(0);
|
||||
}
|
||||
|
||||
feeFromFundingTxListener = (observable, oldValue, newValue) -> {
|
||||
log.debug("feeFromFundingTxListener " + newValue);
|
||||
if (!model.dataModel.isFeeFromFundingTxSufficient()) {
|
||||
new Popup().warning("The mining fee from your funding transaction is not sufficiently high.\n\n" +
|
||||
"You need to use at least a mining fee of " +
|
||||
model.formatter.formatCoinWithCode(FeePolicy.getMinRequiredFeeForFundingTx()) + ".\n\n" +
|
||||
"The fee used in your funding transaction was only " +
|
||||
model.formatter.formatCoinWithCode(newValue) + ".\n\n" +
|
||||
"The trade transactions might take too much time to be included in " +
|
||||
"a block if the fee is too low.\n" +
|
||||
"Please check at your external wallet that you set the required fee and " +
|
||||
"do a funding again with the correct fee.\n\n" +
|
||||
"In the \"Funds/Open for withdrawal\" section you can withdraw those funds.")
|
||||
.closeButtonText("Close")
|
||||
.onClose(() -> {
|
||||
close();
|
||||
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
|
||||
})
|
||||
.show();
|
||||
}
|
||||
};
|
||||
model.dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -261,6 +289,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
showWarningInvalidBtcDecimalPlacesSubscription.unsubscribe();
|
||||
showTransactionPublishedScreenSubscription.unsubscribe();
|
||||
showCheckAvailabilityPopupSubscription.unsubscribe();
|
||||
|
||||
model.dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -313,8 +343,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
public void onTabSelected(boolean isSelected) {
|
||||
model.dataModel.onTabSelected(isSelected);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// UI actions
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -530,8 +560,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||
takeOfferButton.setVisible(false);
|
||||
takeOfferButton.setOnAction(e -> onTakeOffer());
|
||||
takeOfferSpinner = takeOfferTuple.second;
|
||||
takeOfferSpinner.setPrefSize(18, 18);
|
||||
takeOfferSpinnerInfoLabel = takeOfferTuple.third;
|
||||
takeOfferSpinnerInfoLabel.setText(BSResources.get("takeOffer.fundsBox.takeOfferSpinnerInfo"));
|
||||
takeOfferSpinnerInfoLabel.textProperty().bind(model.takeOfferSpinnerInfoText);
|
||||
takeOfferSpinnerInfoLabel.setVisible(false);
|
||||
|
||||
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel"));
|
||||
|
|
|
@ -68,6 +68,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
final StringProperty errorMessage = new SimpleStringProperty();
|
||||
final StringProperty offerWarning = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
final StringProperty takeOfferSpinnerInfoText = new SimpleStringProperty();
|
||||
|
||||
final BooleanProperty isOfferAvailable = new SimpleBooleanProperty();
|
||||
final BooleanProperty isTakeOfferButtonDisabled = new SimpleBooleanProperty(true);
|
||||
|
@ -89,6 +90,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
private ChangeListener<Offer.State> offerStateListener;
|
||||
private ChangeListener<String> offerErrorListener;
|
||||
private ConnectionListener connectionListener;
|
||||
private ChangeListener<Coin> feeFromFundingTxListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -177,9 +179,9 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
|
||||
void onTakeOffer() {
|
||||
takeOfferRequested = true;
|
||||
applyOnTakeOfferResult(false);
|
||||
|
||||
showTransactionPublishedScreen.set(false);
|
||||
isTakeOfferSpinnerVisible.set(true);
|
||||
takeOfferSpinnerInfoText.set(BSResources.get("takeOffer.fundsBox.takeOfferSpinnerInfo"));
|
||||
dataModel.onTakeOffer(trade -> {
|
||||
this.trade = trade;
|
||||
trade.stateProperty().addListener(tradeStateListener);
|
||||
|
@ -328,26 +330,23 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
|| trade.getState() == Trade.State.DEPOSIT_PUBLISHED_MSG_SENT
|
||||
|| trade.getState() == Trade.State.DEPOSIT_PUBLISHED_MSG_RECEIVED) {
|
||||
if (trade.getDepositTx() != null)
|
||||
applyOnTakeOfferResult(true);
|
||||
showTransactionPublishedScreen.set(true);
|
||||
else
|
||||
log.error("trade.getDepositTx() == null. That must not happen");
|
||||
}
|
||||
|
||||
if (errorMessage.get() != null)
|
||||
takeOfferSpinnerInfoText.set("");
|
||||
if (errorMessage.get() == null)
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
}
|
||||
|
||||
private void applyOnTakeOfferResult(boolean success) {
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
showTransactionPublishedScreen.set(success);
|
||||
}
|
||||
|
||||
private void updateButtonDisableState() {
|
||||
isTakeOfferButtonDisabled.set(!(isBtcInputValid(amount.get()).isValid
|
||||
&& dataModel.isMinAmountLessOrEqualAmount()
|
||||
&& !dataModel.isAmountLargerThanOfferAmount()
|
||||
&& dataModel.isWalletFunded.get()
|
||||
&& !takeOfferRequested)
|
||||
&& !takeOfferRequested
|
||||
&& dataModel.isFeeFromFundingTxSufficient())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -389,7 +388,11 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
updateButtonDisableState();
|
||||
};
|
||||
amountAsCoinListener = (ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue));
|
||||
isWalletFundedListener = (ov, oldValue, newValue) -> updateButtonDisableState();
|
||||
isWalletFundedListener = (ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
isTakeOfferSpinnerVisible.set(true);
|
||||
takeOfferSpinnerInfoText.set("Checking funding tx miner fee...");
|
||||
};
|
||||
tradeStateListener = (ov, oldValue, newValue) -> applyTradeState(newValue);
|
||||
tradeErrorListener = (ov, oldValue, newValue) -> applyTradeErrorMessage(newValue);
|
||||
offerStateListener = (ov, oldValue, newValue) -> applyOfferState(newValue);
|
||||
|
@ -411,6 +414,13 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
public void onError(Throwable throwable) {
|
||||
}
|
||||
};
|
||||
feeFromFundingTxListener = (ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
if (newValue.isPositive()) {
|
||||
isTakeOfferSpinnerVisible.set(false);
|
||||
takeOfferSpinnerInfoText.set("");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void addListeners() {
|
||||
|
@ -423,6 +433,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
|
||||
dataModel.isWalletFunded.addListener(isWalletFundedListener);
|
||||
p2PService.getNetworkNode().addConnectionListener(connectionListener);
|
||||
dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
|
@ -434,7 +445,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
dataModel.isWalletFunded.removeListener(isWalletFundedListener);
|
||||
if (offer != null) {
|
||||
offer.stateProperty().removeListener(offerStateListener);
|
||||
offer.errorMessageProperty().addListener(offerErrorListener);
|
||||
offer.errorMessageProperty().removeListener(offerErrorListener);
|
||||
}
|
||||
|
||||
if (trade != null) {
|
||||
|
@ -442,6 +453,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
trade.errorMessageProperty().removeListener(tradeErrorListener);
|
||||
}
|
||||
p2PService.getNetworkNode().removeConnectionListener(connectionListener);
|
||||
dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -148,20 +148,6 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
|||
updateSelectedItem();
|
||||
}
|
||||
|
||||
private void updateSelectedItem() {
|
||||
PendingTradesListItem selectedItem = model.getSelectedItem();
|
||||
if (selectedItem != null) {
|
||||
// Select and focus selectedItem from model
|
||||
int index = table.getItems().indexOf(selectedItem);
|
||||
UserThread.execute(() -> {
|
||||
//TODO app wide focus
|
||||
table.getSelectionModel().select(index);
|
||||
//table.requestFocus();
|
||||
//UserThread.execute(() -> table.getFocusModel().focus(index));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
table.getSelectionModel().selectedItemProperty().removeListener(selectedItemChangeListener);
|
||||
|
@ -183,7 +169,21 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
|||
scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
|
||||
}
|
||||
|
||||
|
||||
private void updateSelectedItem() {
|
||||
PendingTradesListItem selectedItem = model.getSelectedItem();
|
||||
if (selectedItem != null) {
|
||||
// Select and focus selectedItem from model
|
||||
int index = table.getItems().indexOf(selectedItem);
|
||||
UserThread.execute(() -> {
|
||||
//TODO app wide focus
|
||||
table.getSelectionModel().select(index);
|
||||
//table.requestFocus();
|
||||
//UserThread.execute(() -> table.getFocusModel().focus(index));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Subviews
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -105,13 +105,14 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
}
|
||||
});
|
||||
|
||||
timer = FxTimer.runPeriodically(Duration.ofSeconds(1), this::updateTimeLeft);
|
||||
|
||||
tradePeriodStateSubscription = EasyBind.subscribe(trade.getTradePeriodStateProperty(), newValue -> {
|
||||
if (newValue != null) {
|
||||
updateTradePeriodState(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
timer = FxTimer.runPeriodically(Duration.ofSeconds(1), this::updateTimeLeft);
|
||||
|
||||
}
|
||||
|
||||
public void doDeactivate() {
|
||||
|
@ -130,11 +131,11 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
if (tradePeriodStateSubscription != null)
|
||||
tradePeriodStateSubscription.unsubscribe();
|
||||
|
||||
if (notificationGroup != null)
|
||||
notificationGroup.button.setOnAction(null);
|
||||
|
||||
if (timer != null)
|
||||
timer.stop();
|
||||
|
||||
if (notificationGroup != null)
|
||||
notificationGroup.button.setOnAction(null);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -32,6 +32,7 @@ import io.bitsquare.gui.util.Transitions;
|
|||
import io.bitsquare.trade.Contract;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.ObjectBinding;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.control.*;
|
||||
|
@ -66,6 +67,10 @@ public class DisputeSummaryPopup extends Popup {
|
|||
private ToggleGroup feeToggleGroup;
|
||||
private String role;
|
||||
private TextArea summaryNotesTextArea;
|
||||
private ObjectBinding<Tuple2<DisputeResult.FeePaymentPolicy, Toggle>> feePaymentPolicyChanged;
|
||||
private ChangeListener<Tuple2<DisputeResult.FeePaymentPolicy, Toggle>> feePaymentPolicyListener;
|
||||
private ChangeListener<Boolean> shareRadioButtonSelectedListener;
|
||||
private ChangeListener<Toggle> feeToggleSelectionListener;
|
||||
// keep a reference to not get GCed
|
||||
|
||||
|
||||
|
@ -106,6 +111,18 @@ public class DisputeSummaryPopup extends Popup {
|
|||
// Protected
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
if (feePaymentPolicyChanged != null)
|
||||
feePaymentPolicyChanged.removeListener(feePaymentPolicyListener);
|
||||
|
||||
if (shareRadioButton != null)
|
||||
shareRadioButton.selectedProperty().removeListener(shareRadioButtonSelectedListener);
|
||||
|
||||
if (feeToggleGroup != null)
|
||||
feeToggleGroup.selectedToggleProperty().removeListener(feeToggleSelectionListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createGridPane() {
|
||||
super.createGridPane();
|
||||
|
@ -164,13 +181,14 @@ public class DisputeSummaryPopup extends Popup {
|
|||
applyTradeAmountRadioButtonStates();
|
||||
} else {
|
||||
applyPayoutAmounts(disputeResult.feePaymentPolicyProperty().get(), tradeAmountToggleGroup.selectedToggleProperty().get());
|
||||
ObjectBinding<Tuple2<DisputeResult.FeePaymentPolicy, Toggle>> changed = Bindings.createObjectBinding(
|
||||
feePaymentPolicyChanged = Bindings.createObjectBinding(
|
||||
() -> new Tuple2(disputeResult.feePaymentPolicyProperty().get(), tradeAmountToggleGroup.selectedToggleProperty().get()),
|
||||
disputeResult.feePaymentPolicyProperty(),
|
||||
tradeAmountToggleGroup.selectedToggleProperty());
|
||||
changed.addListener((observable, oldValue, newValue) -> {
|
||||
feePaymentPolicyListener = (observable, oldValue, newValue) -> {
|
||||
applyPayoutAmounts(newValue.first, newValue.second);
|
||||
});
|
||||
};
|
||||
feePaymentPolicyChanged.addListener(feePaymentPolicyListener);
|
||||
}
|
||||
|
||||
setFeeRadioButtonState();
|
||||
|
@ -242,7 +260,7 @@ public class DisputeSummaryPopup extends Popup {
|
|||
sellerIsWinnerRadioButton.setToggleGroup(tradeAmountToggleGroup);
|
||||
shareRadioButton.setToggleGroup(tradeAmountToggleGroup);
|
||||
|
||||
shareRadioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
shareRadioButtonSelectedListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
loserPaysFeeRadioButton.setSelected(false);
|
||||
|
||||
|
@ -254,7 +272,8 @@ public class DisputeSummaryPopup extends Popup {
|
|||
}
|
||||
|
||||
loserPaysFeeRadioButton.setDisable(newValue);
|
||||
});
|
||||
};
|
||||
shareRadioButton.selectedProperty().addListener(shareRadioButtonSelectedListener);
|
||||
}
|
||||
|
||||
private void addFeeControls() {
|
||||
|
@ -277,16 +296,15 @@ public class DisputeSummaryPopup extends Popup {
|
|||
splitFeeRadioButton.setToggleGroup(feeToggleGroup);
|
||||
waiveFeeRadioButton.setToggleGroup(feeToggleGroup);
|
||||
|
||||
//setFeeRadioButtonState();
|
||||
|
||||
feeToggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
|
||||
feeToggleSelectionListener = (observable, oldValue, newValue) -> {
|
||||
if (newValue == loserPaysFeeRadioButton)
|
||||
disputeResult.setFeePaymentPolicy(DisputeResult.FeePaymentPolicy.LOSER);
|
||||
else if (newValue == splitFeeRadioButton)
|
||||
disputeResult.setFeePaymentPolicy(DisputeResult.FeePaymentPolicy.SPLIT);
|
||||
else if (newValue == waiveFeeRadioButton)
|
||||
disputeResult.setFeePaymentPolicy(DisputeResult.FeePaymentPolicy.WAIVE);
|
||||
});
|
||||
};
|
||||
feeToggleGroup.selectedToggleProperty().addListener(feeToggleSelectionListener);
|
||||
|
||||
if (dispute.isSupportTicket())
|
||||
feeToggleGroup.selectToggle(waiveFeeRadioButton);
|
||||
|
|
|
@ -19,6 +19,7 @@ package io.bitsquare.gui.popups;
|
|||
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
|
@ -31,6 +32,7 @@ public class EnterPrivKeyPopup extends Popup {
|
|||
private Button unlockButton;
|
||||
private InputTextField keyInputTextField;
|
||||
private PrivKeyHandler privKeyHandler;
|
||||
private ChangeListener<String> changeListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -47,6 +49,8 @@ public class EnterPrivKeyPopup extends Popup {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public EnterPrivKeyPopup() {
|
||||
if (keyInputTextField != null)
|
||||
keyInputTextField.textProperty().addListener(changeListener);
|
||||
}
|
||||
|
||||
public void show() {
|
||||
|
@ -80,6 +84,10 @@ public class EnterPrivKeyPopup extends Popup {
|
|||
// Protected
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
}
|
||||
|
||||
private void addInputFields() {
|
||||
Label label = new Label("Enter private key:");
|
||||
label.setWrapText(true);
|
||||
|
@ -92,9 +100,10 @@ public class EnterPrivKeyPopup extends Popup {
|
|||
GridPane.setMargin(keyInputTextField, new Insets(3, 0, 0, 0));
|
||||
GridPane.setRowIndex(keyInputTextField, rowIndex);
|
||||
GridPane.setColumnIndex(keyInputTextField, 1);
|
||||
keyInputTextField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
changeListener = (observable, oldValue, newValue) -> {
|
||||
unlockButton.setDisable(newValue.length() == 0);
|
||||
});
|
||||
};
|
||||
keyInputTextField.textProperty().addListener(changeListener);
|
||||
gridPane.getChildren().addAll(label, keyInputTextField);
|
||||
}
|
||||
|
||||
|
|
|
@ -100,9 +100,15 @@ public class Popup {
|
|||
stage.hide();
|
||||
else
|
||||
log.warn("Stage is null");
|
||||
|
||||
cleanup();
|
||||
PopupManager.isHidden(this);
|
||||
}
|
||||
|
||||
protected void cleanup() {
|
||||
|
||||
}
|
||||
|
||||
public Popup onClose(Runnable closeHandler) {
|
||||
this.closeHandlerOptional = Optional.of(closeHandler);
|
||||
return this;
|
||||
|
@ -249,7 +255,7 @@ public class Popup {
|
|||
if (headLine != null) {
|
||||
headLineLabel = new Label(BSResources.get(headLine));
|
||||
headLineLabel.setMouseTransparent(true);
|
||||
headLineLabel.setStyle("-fx-font-size: 16; -fx-text-fill: #333;");
|
||||
headLineLabel.setId("popup-headline");
|
||||
GridPane.setHalignment(headLineLabel, HPos.LEFT);
|
||||
GridPane.setRowIndex(headLineLabel, ++rowIndex);
|
||||
GridPane.setColumnSpan(headLineLabel, 2);
|
||||
|
@ -271,6 +277,7 @@ public class Popup {
|
|||
messageLabel = new Label(truncatedMessage);
|
||||
messageLabel.setMouseTransparent(true);
|
||||
messageLabel.setWrapText(true);
|
||||
messageLabel.setId("popup-message");
|
||||
GridPane.setHalignment(messageLabel, HPos.LEFT);
|
||||
GridPane.setHgrow(messageLabel, Priority.ALWAYS);
|
||||
GridPane.setMargin(messageLabel, new Insets(3, 0, 0, 0));
|
||||
|
@ -288,6 +295,7 @@ public class Popup {
|
|||
"It will make debugging easier if you can attach the bitsquare.log file which you can find in the application directory.");
|
||||
|
||||
Button githubButton = new Button("Report to Github issue tracker");
|
||||
githubButton.setId("popup-button");
|
||||
GridPane.setMargin(githubButton, new Insets(20, 0, 0, 0));
|
||||
GridPane.setHalignment(githubButton, HPos.RIGHT);
|
||||
GridPane.setRowIndex(githubButton, ++rowIndex);
|
||||
|
@ -300,6 +308,7 @@ public class Popup {
|
|||
});
|
||||
|
||||
Button mailButton = new Button("Report by email");
|
||||
mailButton.setId("popup-button");
|
||||
GridPane.setHalignment(mailButton, HPos.RIGHT);
|
||||
GridPane.setRowIndex(mailButton, ++rowIndex);
|
||||
GridPane.setColumnIndex(mailButton, 1);
|
||||
|
@ -337,6 +346,7 @@ public class Popup {
|
|||
|
||||
protected void addCloseButton() {
|
||||
closeButton = new Button(closeButtonText == null ? "Close" : closeButtonText);
|
||||
closeButton.setId("popup-button");
|
||||
closeButton.setOnAction(event -> {
|
||||
hide();
|
||||
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
|
||||
|
@ -344,6 +354,7 @@ public class Popup {
|
|||
|
||||
if (actionHandlerOptional.isPresent() || actionButtonText != null) {
|
||||
actionButton = new Button(actionButtonText == null ? "Ok" : actionButtonText);
|
||||
actionButton.setId("popup-button");
|
||||
actionButton.setDefaultButton(true);
|
||||
//TODO app wide focus
|
||||
//actionButton.requestFocus();
|
||||
|
|
|
@ -27,6 +27,7 @@ import io.bitsquare.trade.Trade;
|
|||
import io.bitsquare.trade.offer.Offer;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.TextArea;
|
||||
|
@ -46,6 +47,8 @@ public class TradeDetailsPopup extends Popup {
|
|||
private final BSFormatter formatter;
|
||||
private DisputeManager disputeManager;
|
||||
private Trade trade;
|
||||
private ChangeListener<Number> changeListener;
|
||||
private TextArea textArea;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -78,6 +81,12 @@ public class TradeDetailsPopup extends Popup {
|
|||
// Protected
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
if (textArea != null)
|
||||
textArea.scrollTopProperty().addListener(changeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createGridPane() {
|
||||
super.createGridPane();
|
||||
|
@ -173,17 +182,18 @@ public class TradeDetailsPopup extends Popup {
|
|||
}
|
||||
|
||||
if (trade.errorMessageProperty().get() != null) {
|
||||
TextArea textArea = addLabelTextArea(gridPane, ++rowIndex, "Error message:", "").second;
|
||||
textArea = addLabelTextArea(gridPane, ++rowIndex, "Error message:", "").second;
|
||||
textArea.setText(trade.errorMessageProperty().get());
|
||||
textArea.setEditable(false);
|
||||
|
||||
IntegerProperty count = new SimpleIntegerProperty(20);
|
||||
int rowHeight = 10;
|
||||
textArea.prefHeightProperty().bindBidirectional(count);
|
||||
textArea.scrollTopProperty().addListener((ov, old, newVal) -> {
|
||||
changeListener = (ov, old, newVal) -> {
|
||||
if (newVal.intValue() > rowHeight)
|
||||
count.setValue(count.get() + newVal.intValue() + 10);
|
||||
});
|
||||
};
|
||||
textArea.scrollTopProperty().addListener(changeListener);
|
||||
textArea.setScrollTop(30);
|
||||
|
||||
TextField state = addLabelTextField(gridPane, ++rowIndex, "Trade state:").second;
|
||||
|
|
|
@ -22,6 +22,7 @@ import io.bitsquare.crypto.ScryptUtil;
|
|||
import io.bitsquare.gui.components.PasswordTextField;
|
||||
import io.bitsquare.gui.util.Transitions;
|
||||
import io.bitsquare.gui.util.validation.PasswordValidator;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
|
@ -44,6 +45,7 @@ public class WalletPasswordPopup extends Popup {
|
|||
private Button unlockButton;
|
||||
private AesKeyHandler aesKeyHandler;
|
||||
private PasswordTextField passwordTextField;
|
||||
private ChangeListener<String> changeListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -59,6 +61,12 @@ public class WalletPasswordPopup extends Popup {
|
|||
// Public API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
if (passwordTextField != null)
|
||||
passwordTextField.textProperty().addListener(changeListener);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public WalletPasswordPopup(WalletService walletService) {
|
||||
this.walletService = walletService;
|
||||
|
@ -106,9 +114,10 @@ public class WalletPasswordPopup extends Popup {
|
|||
GridPane.setRowIndex(passwordTextField, rowIndex);
|
||||
GridPane.setColumnIndex(passwordTextField, 1);
|
||||
PasswordValidator passwordValidator = new PasswordValidator();
|
||||
passwordTextField.textProperty().addListener((observable, oldValue, newValue) -> {
|
||||
changeListener = (observable, oldValue, newValue) -> {
|
||||
unlockButton.setDisable(!passwordValidator.validate(newValue).isValid);
|
||||
});
|
||||
};
|
||||
passwordTextField.textProperty().addListener(changeListener);
|
||||
gridPane.getChildren().addAll(label, passwordTextField);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue