mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Savings wallet (WIP)
This commit is contained in:
parent
9ac0740e33
commit
37b31a5d0a
@ -60,7 +60,7 @@ public class Log {
|
||||
appender.start();
|
||||
|
||||
logbackLogger = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
|
||||
logbackLogger.setLevel(useDetailedLogging ? Level.TRACE : Level.WARN);
|
||||
logbackLogger.setLevel(useDetailedLogging ? Level.TRACE : Level.TRACE);
|
||||
logbackLogger.addAppender(appender);
|
||||
|
||||
// log errors in separate file
|
||||
|
@ -62,7 +62,7 @@ public final class PubKeyRing implements Payload {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,8 +52,7 @@ public final class SealedAndSigned implements Payload {
|
||||
in.defaultReadObject();
|
||||
sigPublicKey = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(sigPublicKeyBytes));
|
||||
} catch (Throwable t) {
|
||||
log.error("Exception at readObject: " + t.getMessage());
|
||||
t.printStackTrace();
|
||||
log.warn("Exception at readObject: " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,8 +53,7 @@ public final class Alert implements StoragePayload {
|
||||
in.defaultReadObject();
|
||||
storagePublicKey = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(storagePublicKeyBytes));
|
||||
} catch (Throwable t) {
|
||||
log.error("Exception at readObject: " + t.getMessage());
|
||||
t.printStackTrace();
|
||||
log.warn("Exception at readObject: " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ public final class Dispute implements Payload {
|
||||
disputeResultProperty = new SimpleObjectProperty<>(disputeResult);
|
||||
isClosedProperty = new SimpleBooleanProperty(isClosed);
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ public final class DisputeList<DisputeCase> extends ArrayList<DisputeCase> imple
|
||||
try {
|
||||
in.defaultReadObject();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ public final class DisputeResult implements Payload {
|
||||
in.defaultReadObject();
|
||||
init();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ public final class DisputeCommunicationMessage extends DisputeMessage {
|
||||
arrivedProperty = new SimpleBooleanProperty(arrived);
|
||||
storedInMailboxProperty = new SimpleBooleanProperty(storedInMailbox);
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,11 @@ class AddressBasedCoinSelector implements CoinSelector {
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AddressBasedCoinSelector(NetworkParameters params, AddressEntry addressEntry) {
|
||||
public AddressBasedCoinSelector(NetworkParameters params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public AddressBasedCoinSelector(NetworkParameters params, @Nullable AddressEntry addressEntry) {
|
||||
this.params = params;
|
||||
this.addressEntry = addressEntry;
|
||||
}
|
||||
@ -119,6 +123,9 @@ class AddressBasedCoinSelector implements CoinSelector {
|
||||
|
||||
log.trace("No match found at matchesRequiredAddress addressOutput / addressEntries " + addressOutput.toString
|
||||
() + " / " + addressEntries.toString());
|
||||
} else {
|
||||
// use savings wallet
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -101,7 +101,7 @@ public final class AddressEntry implements Persistable {
|
||||
params = RegTestParams.get();
|
||||
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* The List supporting our persistence solution.
|
||||
@ -82,6 +83,17 @@ public final class AddressEntryList extends ArrayList<AddressEntry> implements P
|
||||
}
|
||||
|
||||
|
||||
public void swapTradeToSavings(String offerId) {
|
||||
Optional<AddressEntry> addressEntryOptional = this.stream().filter(addressEntry -> offerId.equals(addressEntry.getOfferId())).findAny();
|
||||
if (addressEntryOptional.isPresent()) {
|
||||
AddressEntry addressEntry = addressEntryOptional.get();
|
||||
add(new AddressEntry(addressEntry.getKeyPair(), wallet.getParams(), AddressEntry.Context.SAVINGS));
|
||||
remove(addressEntry);
|
||||
storage.queueUpForSave();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public AddressEntry getArbitratorAddressEntry() {
|
||||
if (size() > 0)
|
||||
return get(0);
|
||||
|
@ -134,19 +134,22 @@ public class TradeWalletService {
|
||||
|
||||
/**
|
||||
* @param addressEntry From where we want to spend the transaction fee. Used also as change address.
|
||||
* @param useSavingsWallet
|
||||
* @param tradingFee The amount of the trading fee.
|
||||
* @param feeReceiverAddresses The address of the receiver of the trading fee (arbitrator).
|
||||
* @return The broadcasted transaction
|
||||
* @param feeReceiverAddresses The address of the receiver of the trading fee (arbitrator). @return The broadcasted transaction
|
||||
* @throws InsufficientMoneyException
|
||||
* @throws AddressFormatException
|
||||
*/
|
||||
public Transaction createTradingFeeTx(AddressEntry addressEntry, Coin tradingFee, String feeReceiverAddresses)
|
||||
public Transaction createTradingFeeTx(AddressEntry addressEntry, Address changeAddress, Coin reservedFundsForOffer,
|
||||
boolean useSavingsWallet, Coin tradingFee, String feeReceiverAddresses)
|
||||
throws InsufficientMoneyException, AddressFormatException {
|
||||
Transaction tradingFeeTx = new Transaction(params);
|
||||
Preconditions.checkArgument(Restrictions.isAboveFixedTxFeeAndDust(tradingFee),
|
||||
"You cannot send an amount which are smaller than the fee + dust output.");
|
||||
Coin outPutAmount = tradingFee.subtract(FeePolicy.getFixedTxFeeForTrades());
|
||||
tradingFeeTx.addOutput(outPutAmount, new Address(params, feeReceiverAddresses));
|
||||
// the reserved amount we need for the trade we send to our trade address
|
||||
tradingFeeTx.addOutput(reservedFundsForOffer, addressEntry.getAddress());
|
||||
|
||||
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to
|
||||
// wait for 1 confirmation)
|
||||
@ -154,13 +157,16 @@ public class TradeWalletService {
|
||||
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tradingFeeTx);
|
||||
sendRequest.shuffleOutputs = false;
|
||||
sendRequest.aesKey = aesKey;
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry);
|
||||
if (useSavingsWallet)
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params);
|
||||
else
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry);
|
||||
// We use a fixed fee
|
||||
sendRequest.feePerKb = Coin.ZERO;
|
||||
sendRequest.fee = FeePolicy.getFixedTxFeeForTrades();
|
||||
// We use always the same address for all transactions in a trade to keep things simple.
|
||||
// To be discussed if that introduce any privacy issues.
|
||||
sendRequest.changeAddress = addressEntry.getAddress();
|
||||
|
||||
// Change is optional in case of overpay or use of funds from savings wallet
|
||||
sendRequest.changeAddress = changeAddress;
|
||||
|
||||
checkNotNull(wallet, "Wallet must not be null");
|
||||
wallet.completeTx(sendRequest);
|
||||
@ -181,20 +187,20 @@ public class TradeWalletService {
|
||||
|
||||
|
||||
/**
|
||||
* The taker creates a dummy transaction to get the input(s) and optional change output for the amount and the addressEntry for that trade.
|
||||
* The taker creates a dummy transaction to get the input(s) and optional change output for the amount and the takersAddressEntry for that trade.
|
||||
* That will be used to send to the offerer for creating the deposit transaction.
|
||||
*
|
||||
* @param inputAmount Amount of takers input
|
||||
* @param addressEntry Address entry of taker
|
||||
* @param inputAmount Amount of takers input
|
||||
* @param takersAddressEntry Address entry of taker
|
||||
* @return A data container holding the inputs, the output value and address
|
||||
* @throws TransactionVerificationException
|
||||
* @throws WalletException
|
||||
*/
|
||||
public InputsAndChangeOutput takerCreatesDepositsTxInputs(Coin inputAmount, AddressEntry addressEntry) throws
|
||||
TransactionVerificationException, WalletException {
|
||||
public InputsAndChangeOutput takerCreatesDepositsTxInputs(Coin inputAmount, AddressEntry takersAddressEntry, Address takersChangeAddress) throws
|
||||
TransactionVerificationException, WalletException, AddressFormatException {
|
||||
log.trace("createTakerDepositTxInputs called");
|
||||
log.trace("inputAmount " + inputAmount.toFriendlyString());
|
||||
log.trace("addressEntry " + addressEntry.toString());
|
||||
log.trace("takersAddressEntry " + takersAddressEntry.toString());
|
||||
|
||||
// We add the mining fee 2 times to the deposit tx:
|
||||
// 1. Will be spent when publishing the deposit tx (paid by buyer)
|
||||
@ -224,7 +230,7 @@ public class TradeWalletService {
|
||||
// Find the needed inputs to pay the output, optionally add 1 change output.
|
||||
// Normally only 1 input and no change output is used, but we support multiple inputs and 1 change output.
|
||||
// Our spending transaction output is from the create offer fee payment.
|
||||
addAvailableInputsAndChangeOutputs(dummyTX, addressEntry);
|
||||
addAvailableInputsAndChangeOutputs(dummyTX, takersAddressEntry, takersChangeAddress);
|
||||
|
||||
// The completeTx() call signs the input, but we don't want to pass over signed tx inputs so we remove the signature
|
||||
removeSignatures(dummyTX);
|
||||
@ -261,17 +267,17 @@ public class TradeWalletService {
|
||||
/**
|
||||
* The offerer creates the deposit transaction using the takers input(s) and optional output and signs his input(s).
|
||||
*
|
||||
* @param offererIsBuyer The flag indicating if we are in the offerer as buyer role or the opposite.
|
||||
* @param contractHash The hash of the contract to be added to the OP_RETURN output.
|
||||
* @param offererInputAmount The input amount of the offerer.
|
||||
* @param msOutputAmount The output amount to our MS output.
|
||||
* @param takerRawTransactionInputs Raw data for the connected outputs for all inputs of the taker (normally 1 input)
|
||||
* @param takerChangeOutputValue Optional taker change output value
|
||||
* @param takerChangeAddressString Optional taker change address
|
||||
* @param offererAddressInfo The offerers address entry.
|
||||
* @param buyerPubKey The public key of the buyer.
|
||||
* @param sellerPubKey The public key of the seller.
|
||||
* @param arbitratorPubKey The public key of the arbitrator.
|
||||
* @param offererIsBuyer The flag indicating if we are in the offerer as buyer role or the opposite.
|
||||
* @param contractHash The hash of the contract to be added to the OP_RETURN output.
|
||||
* @param offererInputAmount The input amount of the offerer.
|
||||
* @param msOutputAmount The output amount to our MS output.
|
||||
* @param takerRawTransactionInputs Raw data for the connected outputs for all inputs of the taker (normally 1 input)
|
||||
* @param takerChangeOutputValue Optional taker change output value
|
||||
* @param takerChangeAddressString Optional taker change address
|
||||
* @param offererAddressEntry The offerers address entry.
|
||||
* @param buyerPubKey The public key of the buyer.
|
||||
* @param sellerPubKey The public key of the seller.
|
||||
* @param arbitratorPubKey The public key of the arbitrator.
|
||||
* @return A data container holding the serialized transaction and the offerer raw inputs
|
||||
* @throws SigningException
|
||||
* @throws TransactionVerificationException
|
||||
@ -284,7 +290,8 @@ public class TradeWalletService {
|
||||
List<RawTransactionInput> takerRawTransactionInputs,
|
||||
long takerChangeOutputValue,
|
||||
@Nullable String takerChangeAddressString,
|
||||
AddressEntry offererAddressInfo,
|
||||
AddressEntry offererAddressEntry,
|
||||
Address offererChangeAddress,
|
||||
byte[] buyerPubKey,
|
||||
byte[] sellerPubKey,
|
||||
byte[] arbitratorPubKey)
|
||||
@ -308,7 +315,7 @@ public class TradeWalletService {
|
||||
Coin dummyOutputAmount = offererInputAmount.subtract(FeePolicy.getFixedTxFeeForTrades());
|
||||
TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, dummyOutputAmount, new ECKey().toAddress(params));
|
||||
dummyTx.addOutput(dummyOutput);
|
||||
addAvailableInputsAndChangeOutputs(dummyTx, offererAddressInfo);
|
||||
addAvailableInputsAndChangeOutputs(dummyTx, offererAddressEntry, offererChangeAddress);
|
||||
// Normally we have only 1 input but we support multiple inputs if the user has paid in with several transactions.
|
||||
List<TransactionInput> offererInputs = dummyTx.getInputs();
|
||||
TransactionOutput offererOutput = null;
|
||||
@ -1035,7 +1042,7 @@ public class TradeWalletService {
|
||||
}
|
||||
}
|
||||
|
||||
private void addAvailableInputsAndChangeOutputs(Transaction transaction, AddressEntry addressEntry) throws WalletException {
|
||||
private void addAvailableInputsAndChangeOutputs(Transaction transaction, AddressEntry addressEntry, Address changeAddress) throws WalletException {
|
||||
try {
|
||||
// Lets let the framework do the work to find the right inputs
|
||||
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction);
|
||||
@ -1047,7 +1054,7 @@ public class TradeWalletService {
|
||||
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation)
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry);
|
||||
// We use always the same address in a trade for all transactions
|
||||
sendRequest.changeAddress = addressEntry.getAddress();
|
||||
sendRequest.changeAddress = changeAddress;
|
||||
// With the usage of completeTx() we get all the work done with fee calculation, validation and coin selection.
|
||||
// We don't commit that tx to the wallet as it will be changed later and it's not signed yet.
|
||||
// So it will not change the wallet balance.
|
||||
|
@ -305,19 +305,13 @@ public class WalletService {
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AddressInfo
|
||||
// Trade AddressEntry
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public List<AddressEntry> getAddressEntryList() {
|
||||
return ImmutableList.copyOf(addressEntryList);
|
||||
}
|
||||
|
||||
public List<AddressEntry> getSavingsAddressEntryList() {
|
||||
return getAddressEntryList().stream()
|
||||
.filter(e -> e.getContext().equals(AddressEntry.Context.SAVINGS))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public AddressEntry getArbitratorAddressEntry() {
|
||||
return arbitratorAddressEntry;
|
||||
}
|
||||
@ -332,10 +326,6 @@ public class WalletService {
|
||||
return addressEntryList.getNewTradeAddressEntry(offerId);
|
||||
}
|
||||
|
||||
public AddressEntry getNewSavingsAddressEntry() {
|
||||
return addressEntryList.getNewSavingsAddressEntry();
|
||||
}
|
||||
|
||||
private Optional<AddressEntry> getAddressEntryByAddress(String address) {
|
||||
return getAddressEntryList().stream()
|
||||
.filter(e -> e.getAddressString() != null && e.getAddressString().equals(address))
|
||||
@ -343,6 +333,72 @@ public class WalletService {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// SavingsAddressEntry
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public AddressEntry getNewSavingsAddressEntry() {
|
||||
return addressEntryList.getNewSavingsAddressEntry();
|
||||
}
|
||||
|
||||
public List<AddressEntry> getSavingsAddressEntryList() {
|
||||
return getAddressEntryList().stream()
|
||||
.filter(e -> e.getContext().equals(AddressEntry.Context.SAVINGS))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public AddressEntry getUnusedSavingsAddressEntry() {
|
||||
List<AddressEntry> unusedSavingsAddressEntries = getUnusedSavingsAddressEntries();
|
||||
if (!unusedSavingsAddressEntries.isEmpty())
|
||||
return unusedSavingsAddressEntries.get(0);
|
||||
else
|
||||
return getNewSavingsAddressEntry();
|
||||
}
|
||||
|
||||
public List<AddressEntry> getUnusedSavingsAddressEntries() {
|
||||
return getSavingsAddressEntryList().stream()
|
||||
.filter(addressEntry -> getNumTxOutputsForAddress(addressEntry.getAddress()) == 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<AddressEntry> getUsedSavingsAddressEntries() {
|
||||
return getSavingsAddressEntryList().stream()
|
||||
.filter(addressEntry -> getNumTxOutputsForAddress(addressEntry.getAddress()) > 0)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Address> getUsedSavingsAddresses() {
|
||||
return getSavingsAddressEntryList().stream()
|
||||
.filter(addressEntry -> getNumTxOutputsForAddress(addressEntry.getAddress()) > 0)
|
||||
.map(addressEntry -> addressEntry.getAddress())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Transaction> getUsedSavingWalletTransactions() {
|
||||
List<Transaction> transactions = new ArrayList<>();
|
||||
List<TransactionOutput> transactionOutputs = new ArrayList<>();
|
||||
List<Address> usedSavingsAddresses = getUsedSavingsAddresses();
|
||||
log.debug("usedSavingsAddresses = " + usedSavingsAddresses);
|
||||
wallet.getTransactions(true).stream().forEach(transaction -> transactionOutputs.addAll(transaction.getOutputs()));
|
||||
for (TransactionOutput transactionOutput : transactionOutputs) {
|
||||
if (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash()) {
|
||||
Address addressOutput = transactionOutput.getScriptPubKey().getToAddress(params);
|
||||
|
||||
if (usedSavingsAddresses.contains(addressOutput) && transactionOutput.getParentTransaction() != null) {
|
||||
log.debug("transactionOutput.getParentTransaction() = " + transactionOutput.getParentTransaction().getHashAsString());
|
||||
transactions.add(transactionOutput.getParentTransaction());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return transactions;
|
||||
}
|
||||
|
||||
public void swapTradeToSavings(String offerId) {
|
||||
addressEntryList.swapTradeToSavings(offerId);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TransactionConfidence
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -448,6 +504,28 @@ public class WalletService {
|
||||
return balance;
|
||||
}
|
||||
|
||||
public Coin getSavingWalletBalance() {
|
||||
Coin balance = Coin.ZERO;
|
||||
for (AddressEntry addressEntry : getSavingsAddressEntryList()) {
|
||||
balance = balance.add(getBalanceForAddress(addressEntry.getAddress()));
|
||||
}
|
||||
return balance;
|
||||
}
|
||||
|
||||
public int getNumTxOutputsForAddress(Address address) {
|
||||
List<TransactionOutput> transactionOutputs = new ArrayList<>();
|
||||
wallet.getTransactions(true).stream().forEach(t -> transactionOutputs.addAll(t.getOutputs()));
|
||||
int outputs = 0;
|
||||
for (TransactionOutput transactionOutput : transactionOutputs) {
|
||||
if (transactionOutput.getScriptPubKey().isSentToAddress() || transactionOutput.getScriptPubKey().isPayToScriptHash()) {
|
||||
Address addressOutput = transactionOutput.getScriptPubKey().getToAddress(params);
|
||||
if (addressOutput.equals(address))
|
||||
outputs++;
|
||||
}
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Withdrawal
|
||||
|
@ -115,7 +115,7 @@ public final class PaymentMethod implements Persistable, Comparable {
|
||||
this.maxTradePeriod = paymentMethod.getMaxTradePeriod();
|
||||
this.maxTradeLimitInBitcoin = paymentMethod.getMaxTradeLimitInBitcoin();
|
||||
} catch (Throwable t) {
|
||||
log.error("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ public final class BuyerAsOffererTrade extends BuyerTrade implements OffererTrad
|
||||
initStateProperties();
|
||||
initAmountProperty();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,7 @@ public final class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade {
|
||||
initStateProperties();
|
||||
initAmountProperty();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ public final class SellerAsOffererTrade extends SellerTrade implements OffererTr
|
||||
initStateProperties();
|
||||
initAmountProperty();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ public final class SellerAsTakerTrade extends SellerTrade implements TakerTrade
|
||||
initStateProperties();
|
||||
initAmountProperty();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public final class TradableList<T extends Tradable> extends ArrayList<T> impleme
|
||||
try {
|
||||
in.defaultReadObject();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ public abstract class Trade implements Tradable, Model {
|
||||
initAmountProperty();
|
||||
errorMessageProperty = new SimpleStringProperty(errorMessage);
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +220,8 @@ public abstract class Trade implements Tradable, Model {
|
||||
TradeManager tradeManager,
|
||||
OpenOfferManager openOfferManager,
|
||||
User user,
|
||||
KeyRing keyRing) {
|
||||
KeyRing keyRing,
|
||||
Coin fundsNeededForTrade) {
|
||||
Log.traceCall();
|
||||
processModel.onAllServicesInitialized(offer,
|
||||
tradeManager,
|
||||
@ -230,7 +231,8 @@ public abstract class Trade implements Tradable, Model {
|
||||
tradeWalletService,
|
||||
arbitratorManager,
|
||||
user,
|
||||
keyRing);
|
||||
keyRing,
|
||||
fundsNeededForTrade);
|
||||
|
||||
createProtocol();
|
||||
|
||||
|
@ -178,7 +178,7 @@ public class TradeManager {
|
||||
else {*/
|
||||
trade.setStorage(tradableListStorage);
|
||||
trade.updateDepositTxFromWallet(tradeWalletService);
|
||||
initTrade(trade);
|
||||
initTrade(trade, trade.getProcessModel().getFundsNeededForTrade());
|
||||
|
||||
|
||||
// }
|
||||
@ -209,7 +209,7 @@ public class TradeManager {
|
||||
trade = new SellerAsOffererTrade(offer, tradableListStorage);
|
||||
|
||||
trade.setStorage(tradableListStorage);
|
||||
initTrade(trade);
|
||||
initTrade(trade, trade.getProcessModel().getFundsNeededForTrade());
|
||||
trades.add(trade);
|
||||
((OffererTrade) trade).handleTakeOfferRequest(message, peerNodeAddress);
|
||||
} else {
|
||||
@ -220,7 +220,7 @@ public class TradeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void initTrade(Trade trade) {
|
||||
private void initTrade(Trade trade, Coin fundsNeededForTrade) {
|
||||
trade.init(p2PService,
|
||||
walletService,
|
||||
tradeWalletService,
|
||||
@ -228,7 +228,8 @@ public class TradeManager {
|
||||
this,
|
||||
openOfferManager,
|
||||
user,
|
||||
keyRing);
|
||||
keyRing,
|
||||
fundsNeededForTrade);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -256,6 +257,7 @@ public class TradeManager {
|
||||
|
||||
// First we check if offer is still available then we create the trade with the protocol
|
||||
public void onTakeOffer(Coin amount,
|
||||
Coin fundsNeededForTrade,
|
||||
Offer offer,
|
||||
String paymentAccountId,
|
||||
TradeResultHandler tradeResultHandler) {
|
||||
@ -263,11 +265,12 @@ public class TradeManager {
|
||||
offer.checkOfferAvailability(model,
|
||||
() -> {
|
||||
if (offer.getState() == Offer.State.AVAILABLE)
|
||||
createTrade(amount, offer, paymentAccountId, model, tradeResultHandler);
|
||||
createTrade(amount, fundsNeededForTrade, offer, paymentAccountId, model, tradeResultHandler);
|
||||
});
|
||||
}
|
||||
|
||||
private void createTrade(Coin amount,
|
||||
Coin fundsNeededForTrade,
|
||||
Offer offer,
|
||||
String paymentAccountId,
|
||||
OfferAvailabilityModel model,
|
||||
@ -282,7 +285,7 @@ public class TradeManager {
|
||||
trade.setTakeOfferDateAsBlockHeight(tradeWalletService.getBestChainHeight());
|
||||
trade.setTakerPaymentAccountId(paymentAccountId);
|
||||
|
||||
initTrade(trade);
|
||||
initTrade(trade, fundsNeededForTrade);
|
||||
|
||||
trades.add(trade);
|
||||
((TakerTrade) trade).takeAvailableOffer();
|
||||
|
@ -178,7 +178,7 @@ public final class Offer implements StoragePayload, RequiresOwnerIsOnlinePayload
|
||||
// we don't need to fill it as the error message is only relevant locally, so we don't store it in the transmitted object
|
||||
errorMessageProperty = new SimpleStringProperty();
|
||||
} catch (Throwable t) {
|
||||
log.error("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ public final class OpenOffer implements Tradable {
|
||||
setState(State.AVAILABLE);
|
||||
|
||||
} catch (Throwable t) {
|
||||
log.error("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
public Date getDate() {
|
||||
|
@ -44,6 +44,7 @@ import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel;
|
||||
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
|
||||
import io.bitsquare.user.User;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -227,9 +228,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void placeOffer(Offer offer,
|
||||
TransactionResultHandler resultHandler) {
|
||||
PlaceOfferModel model = new PlaceOfferModel(offer, walletService, tradeWalletService, offerBookService, user);
|
||||
public void placeOffer(Offer offer, Coin reservedFundsForOffer, boolean useSavingsWallet, TransactionResultHandler resultHandler) {
|
||||
PlaceOfferModel model = new PlaceOfferModel(offer, reservedFundsForOffer, useSavingsWallet, walletService, tradeWalletService, offerBookService, user);
|
||||
PlaceOfferProtocol placeOfferProtocol = new PlaceOfferProtocol(
|
||||
model,
|
||||
transaction -> {
|
||||
@ -272,6 +272,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||
openOffer.setState(OpenOffer.State.CANCELED);
|
||||
openOffers.remove(openOffer);
|
||||
closedTradableManager.add(openOffer);
|
||||
walletService.swapTradeToSavings(offer.getId());
|
||||
resultHandler.handleResult();
|
||||
},
|
||||
errorMessageHandler);
|
||||
|
@ -23,6 +23,7 @@ import io.bitsquare.common.taskrunner.Model;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.trade.offer.OfferBookService;
|
||||
import io.bitsquare.user.User;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -31,6 +32,8 @@ public class PlaceOfferModel implements Model {
|
||||
private static final Logger log = LoggerFactory.getLogger(PlaceOfferModel.class);
|
||||
|
||||
public final Offer offer;
|
||||
public final Coin reservedFundsForOffer;
|
||||
public final boolean useSavingsWallet;
|
||||
public final WalletService walletService;
|
||||
public final TradeWalletService tradeWalletService;
|
||||
public final OfferBookService offerBookService;
|
||||
@ -39,11 +42,15 @@ public class PlaceOfferModel implements Model {
|
||||
private Transaction transaction;
|
||||
|
||||
public PlaceOfferModel(Offer offer,
|
||||
Coin reservedFundsForOffer,
|
||||
boolean useSavingsWallet,
|
||||
WalletService walletService,
|
||||
TradeWalletService tradeWalletService,
|
||||
OfferBookService offerBookService,
|
||||
User user) {
|
||||
this.offer = offer;
|
||||
this.reservedFundsForOffer = reservedFundsForOffer;
|
||||
this.useSavingsWallet = useSavingsWallet;
|
||||
this.walletService = walletService;
|
||||
this.tradeWalletService = tradeWalletService;
|
||||
this.offerBookService = offerBookService;
|
||||
|
@ -18,13 +18,10 @@
|
||||
package io.bitsquare.trade.protocol.placeoffer.tasks;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.common.taskrunner.Task;
|
||||
import io.bitsquare.common.taskrunner.TaskRunner;
|
||||
import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferModel;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
@ -44,67 +41,59 @@ public class BroadcastCreateOfferFeeTx extends Task<PlaceOfferModel> {
|
||||
protected void run() {
|
||||
try {
|
||||
runInterceptHook();
|
||||
Coin totalsNeeded = FeePolicy.getSecurityDeposit().add(FeePolicy.getCreateOfferFee()).add(FeePolicy.getFixedTxFeeForTrades());
|
||||
AddressEntry addressEntry = model.walletService.getTradeAddressEntry(model.offer.getId());
|
||||
Coin balance = model.walletService.getBalanceForAddress(addressEntry.getAddress());
|
||||
if (balance.compareTo(totalsNeeded) >= 0) {
|
||||
model.tradeWalletService.broadcastTx(model.getTransaction(), new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction) {
|
||||
log.info("Broadcast of offer fee payment succeeded: transaction = " + transaction.toString());
|
||||
model.tradeWalletService.broadcastTx(model.getTransaction(), new FutureCallback<Transaction>() {
|
||||
@Override
|
||||
public void onSuccess(Transaction transaction) {
|
||||
log.info("Broadcast of offer fee payment succeeded: transaction = " + transaction.toString());
|
||||
|
||||
if (model.getTransaction().getHashAsString().equals(transaction.getHashAsString())) {
|
||||
model.offer.setState(Offer.State.OFFER_FEE_PAID);
|
||||
// No tx malleability happened after broadcast (still not in blockchain)
|
||||
complete();
|
||||
} else {
|
||||
log.warn("Tx malleability happened after broadcast. We publish the changed offer to the P2P network again.");
|
||||
// Tx malleability happened after broadcast. We first remove the malleable offer.
|
||||
// Then we publish the changed offer to the P2P network again after setting the new TxId.
|
||||
// Normally we use a delay for broadcasting to the peers, but at shut down we want to get it fast out
|
||||
model.offerBookService.removeOffer(model.offer,
|
||||
() -> {
|
||||
log.info("We store now the changed txID to the offer and add that again.");
|
||||
// We store now the changed txID to the offer and add that again.
|
||||
model.offer.setOfferFeePaymentTxID(transaction.getHashAsString());
|
||||
model.setTransaction(transaction);
|
||||
model.offerBookService.addOffer(model.offer,
|
||||
() -> complete(),
|
||||
errorMessage -> {
|
||||
log.error("addOffer failed");
|
||||
addOfferFailed = true;
|
||||
updateStateOnFault();
|
||||
model.offer.setErrorMessage("An error occurred when adding the offer to the P2P network.\n" +
|
||||
"Error message:\n"
|
||||
+ errorMessage);
|
||||
failed(errorMessage);
|
||||
});
|
||||
},
|
||||
errorMessage -> {
|
||||
log.error("removeOffer failed");
|
||||
removeOfferFailed = true;
|
||||
updateStateOnFault();
|
||||
model.offer.setErrorMessage("An error occurred when removing the offer from the P2P network.\n" +
|
||||
"Error message:\n"
|
||||
+ errorMessage);
|
||||
failed(errorMessage);
|
||||
});
|
||||
}
|
||||
if (model.getTransaction().getHashAsString().equals(transaction.getHashAsString())) {
|
||||
model.offer.setState(Offer.State.OFFER_FEE_PAID);
|
||||
// No tx malleability happened after broadcast (still not in blockchain)
|
||||
complete();
|
||||
} else {
|
||||
log.warn("Tx malleability happened after broadcast. We publish the changed offer to the P2P network again.");
|
||||
// Tx malleability happened after broadcast. We first remove the malleable offer.
|
||||
// Then we publish the changed offer to the P2P network again after setting the new TxId.
|
||||
// Normally we use a delay for broadcasting to the peers, but at shut down we want to get it fast out
|
||||
model.offerBookService.removeOffer(model.offer,
|
||||
() -> {
|
||||
log.info("We store now the changed txID to the offer and add that again.");
|
||||
// We store now the changed txID to the offer and add that again.
|
||||
model.offer.setOfferFeePaymentTxID(transaction.getHashAsString());
|
||||
model.setTransaction(transaction);
|
||||
model.offerBookService.addOffer(model.offer,
|
||||
() -> complete(),
|
||||
errorMessage -> {
|
||||
log.error("addOffer failed");
|
||||
addOfferFailed = true;
|
||||
updateStateOnFault();
|
||||
model.offer.setErrorMessage("An error occurred when adding the offer to the P2P network.\n" +
|
||||
"Error message:\n"
|
||||
+ errorMessage);
|
||||
failed(errorMessage);
|
||||
});
|
||||
},
|
||||
errorMessage -> {
|
||||
log.error("removeOffer failed");
|
||||
removeOfferFailed = true;
|
||||
updateStateOnFault();
|
||||
model.offer.setErrorMessage("An error occurred when removing the offer from the P2P network.\n" +
|
||||
"Error message:\n"
|
||||
+ errorMessage);
|
||||
failed(errorMessage);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable t) {
|
||||
updateStateOnFault();
|
||||
model.offer.setErrorMessage("An error occurred.\n" +
|
||||
"Error message:\n"
|
||||
+ t.getMessage());
|
||||
failed(t);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateStateOnFault();
|
||||
model.offer.setErrorMessage("You don't have enough balance in your wallet for placing the offer.");
|
||||
}
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable t) {
|
||||
updateStateOnFault();
|
||||
model.offer.setErrorMessage("An error occurred.\n" +
|
||||
"Error message:\n"
|
||||
+ t.getMessage());
|
||||
failed(t);
|
||||
}
|
||||
});
|
||||
} catch (Throwable t) {
|
||||
model.offer.setErrorMessage("An error occurred.\n" +
|
||||
"Error message:\n"
|
||||
|
@ -28,6 +28,8 @@ import org.bitcoinj.core.Transaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class CreateOfferFeeTx extends Task<PlaceOfferModel> {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateOfferFeeTx.class);
|
||||
|
||||
@ -43,8 +45,12 @@ public class CreateOfferFeeTx extends Task<PlaceOfferModel> {
|
||||
NodeAddress selectedArbitratorNodeAddress = ArbitrationSelectionRule.select(model.user.getAcceptedArbitratorAddresses(), model.offer);
|
||||
log.debug("selectedArbitratorAddress " + selectedArbitratorNodeAddress);
|
||||
Arbitrator selectedArbitrator = model.user.getAcceptedArbitratorByAddress(selectedArbitratorNodeAddress);
|
||||
checkNotNull(selectedArbitrator, "selectedArbitrator must not be null at CreateOfferFeeTx");
|
||||
Transaction transaction = model.tradeWalletService.createTradingFeeTx(
|
||||
model.walletService.getTradeAddressEntry(model.offer.getId()),
|
||||
model.walletService.getUnusedSavingsAddressEntry().getAddress(),
|
||||
model.reservedFundsForOffer,
|
||||
model.useSavingsWallet,
|
||||
FeePolicy.getCreateOfferFee(),
|
||||
selectedArbitrator.getBtcAddress());
|
||||
|
||||
|
@ -110,7 +110,7 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
|
||||
VerifyTakerAccount.class,
|
||||
LoadTakeOfferFeeTx.class,
|
||||
CreateAndSignContract.class,
|
||||
CreateAndSignDepositTxAsBuyer.class,
|
||||
OffererCreatesAndSignsDepositTxAsBuyer.class,
|
||||
InitWaitPeriodForOpenDispute.class,
|
||||
SetupDepositBalanceListener.class,
|
||||
SendPublishDepositTxRequest.class
|
||||
|
@ -95,7 +95,7 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
|
||||
LoadCreateOfferFeeTx.class,
|
||||
CreateTakeOfferFeeTx.class,
|
||||
BroadcastTakeOfferFeeTx.class,
|
||||
CreateDepositTxInputsAsBuyer.class,
|
||||
TakerCreatesDepositTxInputsAsBuyer.class,
|
||||
SendPayDepositRequest.class
|
||||
);
|
||||
startTimeout();
|
||||
|
@ -36,6 +36,8 @@ import io.bitsquare.trade.offer.Offer;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
||||
import io.bitsquare.user.User;
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -64,6 +66,7 @@ public class ProcessModel implements Model, Serializable {
|
||||
transient private KeyRing keyRing;
|
||||
transient private P2PService p2PService;
|
||||
|
||||
|
||||
// Mutable
|
||||
public final TradingPeer tradingPeer;
|
||||
transient private TradeMessage tradeMessage;
|
||||
@ -80,6 +83,8 @@ public class ProcessModel implements Model, Serializable {
|
||||
@Nullable
|
||||
private String changeOutputAddress;
|
||||
private Transaction takeOfferFeeTx;
|
||||
public boolean useSavingsWallet;
|
||||
private Coin fundsNeededForTrade;
|
||||
|
||||
public ProcessModel() {
|
||||
tradingPeer = new TradingPeer();
|
||||
@ -89,7 +94,7 @@ public class ProcessModel implements Model, Serializable {
|
||||
try {
|
||||
in.defaultReadObject();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +106,8 @@ public class ProcessModel implements Model, Serializable {
|
||||
TradeWalletService tradeWalletService,
|
||||
ArbitratorManager arbitratorManager,
|
||||
User user,
|
||||
KeyRing keyRing) {
|
||||
KeyRing keyRing,
|
||||
Coin fundsNeededForTrade) {
|
||||
this.offer = offer;
|
||||
this.tradeManager = tradeManager;
|
||||
this.openOfferManager = openOfferManager;
|
||||
@ -111,6 +117,7 @@ public class ProcessModel implements Model, Serializable {
|
||||
this.user = user;
|
||||
this.keyRing = keyRing;
|
||||
this.p2PService = p2PService;
|
||||
this.fundsNeededForTrade = fundsNeededForTrade;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -154,6 +161,10 @@ public class ProcessModel implements Model, Serializable {
|
||||
return p2PService.getAddress();
|
||||
}
|
||||
|
||||
public Coin getFundsNeededForTrade() {
|
||||
return fundsNeededForTrade;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter/Setter for Mutable objects
|
||||
@ -183,6 +194,10 @@ public class ProcessModel implements Model, Serializable {
|
||||
return walletService.getTradeAddressEntry(offer.getId());
|
||||
}
|
||||
|
||||
public Address getUnusedSavingsAddress() {
|
||||
return walletService.getUnusedSavingsAddressEntry().getAddress();
|
||||
}
|
||||
|
||||
public byte[] getTradeWalletPubKey() {
|
||||
return getAddressEntry().getPubKey();
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
|
||||
LoadTakeOfferFeeTx.class,
|
||||
InitWaitPeriodForOpenDispute.class,
|
||||
CreateAndSignContract.class,
|
||||
CreateAndSignDepositTxAsSeller.class,
|
||||
OffererCreatesAndSignsDepositTxAsSeller.class,
|
||||
SetupDepositBalanceListener.class,
|
||||
SendPublishDepositTxRequest.class
|
||||
);
|
||||
|
@ -103,7 +103,7 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
|
||||
LoadCreateOfferFeeTx.class,
|
||||
CreateTakeOfferFeeTx.class,
|
||||
BroadcastTakeOfferFeeTx.class,
|
||||
CreateDepositTxInputsAsSeller.class,
|
||||
TakerCreatesDepositTxInputsAsSeller.class,
|
||||
SendPayDepositRequest.class
|
||||
);
|
||||
startTimeout();
|
||||
|
@ -64,7 +64,7 @@ public final class TradingPeer implements Persistable {
|
||||
try {
|
||||
in.defaultReadObject();
|
||||
} catch (Throwable t) {
|
||||
log.trace("Cannot be deserialized." + t.getMessage());
|
||||
log.warn("Cannot be deserialized." + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,10 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class CreateAndSignDepositTxAsBuyer extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateAndSignDepositTxAsBuyer.class);
|
||||
public class OffererCreatesAndSignsDepositTxAsBuyer extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(OffererCreatesAndSignsDepositTxAsBuyer.class);
|
||||
|
||||
public CreateAndSignDepositTxAsBuyer(TaskRunner taskHandler, Trade trade) {
|
||||
public OffererCreatesAndSignsDepositTxAsBuyer(TaskRunner taskHandler, Trade trade) {
|
||||
super(taskHandler, trade);
|
||||
}
|
||||
|
||||
@ -60,6 +60,7 @@ public class CreateAndSignDepositTxAsBuyer extends TradeTask {
|
||||
processModel.tradingPeer.getChangeOutputValue(),
|
||||
processModel.tradingPeer.getChangeOutputAddress(),
|
||||
processModel.getAddressEntry(),
|
||||
processModel.getUnusedSavingsAddress(),
|
||||
processModel.getTradeWalletPubKey(),
|
||||
processModel.tradingPeer.getTradeWalletPubKey(),
|
||||
processModel.getArbitratorPubKey(trade.getArbitratorNodeAddress()));
|
@ -26,10 +26,10 @@ import org.bitcoinj.core.Coin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CreateDepositTxInputsAsBuyer extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateDepositTxInputsAsBuyer.class);
|
||||
public class TakerCreatesDepositTxInputsAsBuyer extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(TakerCreatesDepositTxInputsAsBuyer.class);
|
||||
|
||||
public CreateDepositTxInputsAsBuyer(TaskRunner taskHandler, Trade trade) {
|
||||
public TakerCreatesDepositTxInputsAsBuyer(TaskRunner taskHandler, Trade trade) {
|
||||
super(taskHandler, trade);
|
||||
}
|
||||
|
||||
@ -38,7 +38,9 @@ public class CreateDepositTxInputsAsBuyer extends TradeTask {
|
||||
try {
|
||||
runInterceptHook();
|
||||
Coin takerInputAmount = FeePolicy.getSecurityDeposit().add(FeePolicy.getFixedTxFeeForTrades());
|
||||
InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs(takerInputAmount, processModel.getAddressEntry());
|
||||
InputsAndChangeOutput result = processModel.getTradeWalletService()
|
||||
.takerCreatesDepositsTxInputs(takerInputAmount, processModel.getAddressEntry(),
|
||||
processModel.getUnusedSavingsAddress());
|
||||
processModel.setRawTransactionInputs(result.rawTransactionInputs);
|
||||
processModel.setChangeOutputValue(result.changeOutputValue);
|
||||
processModel.setChangeOutputAddress(result.changeOutputAddress);
|
@ -29,10 +29,10 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class CreateAndSignDepositTxAsSeller extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateAndSignDepositTxAsSeller.class);
|
||||
public class OffererCreatesAndSignsDepositTxAsSeller extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(OffererCreatesAndSignsDepositTxAsSeller.class);
|
||||
|
||||
public CreateAndSignDepositTxAsSeller(TaskRunner taskHandler, Trade trade) {
|
||||
public OffererCreatesAndSignsDepositTxAsSeller(TaskRunner taskHandler, Trade trade) {
|
||||
super(taskHandler, trade);
|
||||
}
|
||||
|
||||
@ -60,6 +60,7 @@ public class CreateAndSignDepositTxAsSeller extends TradeTask {
|
||||
processModel.tradingPeer.getChangeOutputValue(),
|
||||
processModel.tradingPeer.getChangeOutputAddress(),
|
||||
processModel.getAddressEntry(),
|
||||
processModel.getUnusedSavingsAddress(),
|
||||
processModel.tradingPeer.getTradeWalletPubKey(),
|
||||
processModel.getTradeWalletPubKey(),
|
||||
processModel.getArbitratorPubKey(trade.getArbitratorNodeAddress()));
|
@ -26,10 +26,10 @@ import org.bitcoinj.core.Coin;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CreateDepositTxInputsAsSeller extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateDepositTxInputsAsSeller.class);
|
||||
public class TakerCreatesDepositTxInputsAsSeller extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(TakerCreatesDepositTxInputsAsSeller.class);
|
||||
|
||||
public CreateDepositTxInputsAsSeller(TaskRunner taskHandler, Trade trade) {
|
||||
public TakerCreatesDepositTxInputsAsSeller(TaskRunner taskHandler, Trade trade) {
|
||||
super(taskHandler, trade);
|
||||
}
|
||||
|
||||
@ -40,8 +40,10 @@ public class CreateDepositTxInputsAsSeller extends TradeTask {
|
||||
if (trade.getTradeAmount() != null) {
|
||||
Coin takerInputAmount = FeePolicy.getSecurityDeposit().add(FeePolicy.getFixedTxFeeForTrades()).add(trade.getTradeAmount());
|
||||
|
||||
InputsAndChangeOutput result = processModel.getTradeWalletService().takerCreatesDepositsTxInputs(takerInputAmount, processModel
|
||||
.getAddressEntry());
|
||||
InputsAndChangeOutput result = processModel.getTradeWalletService()
|
||||
.takerCreatesDepositsTxInputs(takerInputAmount,
|
||||
processModel.getAddressEntry(),
|
||||
processModel.getUnusedSavingsAddress());
|
||||
processModel.setRawTransactionInputs(result.rawTransactionInputs);
|
||||
processModel.setChangeOutputValue(result.changeOutputValue);
|
||||
processModel.setChangeOutputAddress(result.changeOutputAddress);
|
@ -29,6 +29,8 @@ import org.bitcoinj.core.Transaction;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
public class CreateTakeOfferFeeTx extends TradeTask {
|
||||
private static final Logger log = LoggerFactory.getLogger(CreateTakeOfferFeeTx.class);
|
||||
|
||||
@ -45,8 +47,12 @@ public class CreateTakeOfferFeeTx extends TradeTask {
|
||||
NodeAddress selectedArbitratorNodeAddress = ArbitrationSelectionRule.select(user.getAcceptedArbitratorAddresses(), processModel.getOffer());
|
||||
log.debug("selectedArbitratorAddress " + selectedArbitratorNodeAddress);
|
||||
Arbitrator selectedArbitrator = user.getAcceptedArbitratorByAddress(selectedArbitratorNodeAddress);
|
||||
checkNotNull(selectedArbitrator, "selectedArbitrator must not be null at CreateTakeOfferFeeTx");
|
||||
Transaction createTakeOfferFeeTx = processModel.getTradeWalletService().createTradingFeeTx(
|
||||
processModel.getAddressEntry(),
|
||||
processModel.getUnusedSavingsAddress(),
|
||||
processModel.getFundsNeededForTrade(),
|
||||
processModel.useSavingsWallet,
|
||||
FeePolicy.getTakeOfferFee(),
|
||||
selectedArbitrator.getBtcAddress());
|
||||
|
||||
|
@ -75,7 +75,7 @@ import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY;
|
||||
public class BitsquareApp extends Application {
|
||||
private static final Logger log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(BitsquareApp.class);
|
||||
|
||||
public static final boolean DEV_MODE = false;
|
||||
public static final boolean DEV_MODE = true;
|
||||
public static final boolean IS_RELEASE_VERSION = !DEV_MODE && true;
|
||||
|
||||
private static Environment env;
|
||||
|
@ -201,6 +201,12 @@ bg color of non edit textFields: fafafa
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
.internal-funds-icon {
|
||||
-fx-text-fill: #999;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Tooltip *
|
||||
@ -1000,3 +1006,7 @@ textfield */
|
||||
-fx-text-fill: white;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
|
||||
#popup-qr-code-info {
|
||||
-fx-font-size: 11;
|
||||
}
|
@ -21,11 +21,16 @@ public class AddressWithIconAndDirection extends AnchorPane {
|
||||
private final Label directionIcon;
|
||||
private final Label label;
|
||||
|
||||
public AddressWithIconAndDirection(String text, String address, AwesomeIcon awesomeIcon, boolean received) {
|
||||
public AddressWithIconAndDirection(String text, String address, AwesomeIcon awesomeIcon, boolean received, boolean isInternal) {
|
||||
directionIcon = new Label();
|
||||
directionIcon.setLayoutY(3);
|
||||
directionIcon.getStyleClass().add(received ? "received-funds-icon" : "sent-funds-icon");
|
||||
AwesomeDude.setIcon(directionIcon, received ? AwesomeIcon.SIGNIN : AwesomeIcon.SIGNOUT);
|
||||
if (isInternal) {
|
||||
directionIcon.getStyleClass().add("internal-funds-icon");
|
||||
AwesomeDude.setIcon(directionIcon, AwesomeIcon.REPEAT);
|
||||
} else {
|
||||
directionIcon.getStyleClass().add(received ? "received-funds-icon" : "sent-funds-icon");
|
||||
AwesomeDude.setIcon(directionIcon, received ? AwesomeIcon.SIGNIN : AwesomeIcon.SIGNOUT);
|
||||
}
|
||||
directionIcon.setMouseTransparent(true);
|
||||
|
||||
HBox hBox = new HBox();
|
||||
@ -52,7 +57,7 @@ public class AddressWithIconAndDirection extends AnchorPane {
|
||||
|
||||
AnchorPane.setLeftAnchor(directionIcon, 3.0);
|
||||
AnchorPane.setTopAnchor(directionIcon, 2.0);
|
||||
AnchorPane.setLeftAnchor(hBox, 20.0);
|
||||
AnchorPane.setLeftAnchor(hBox, 22.0);
|
||||
AnchorPane.setRightAnchor(hBox, 15.0);
|
||||
AnchorPane.setRightAnchor(openLinkIcon, 4.0);
|
||||
AnchorPane.setTopAnchor(openLinkIcon, 3.0);
|
||||
|
@ -60,9 +60,11 @@ public class BalanceTextField extends AnchorPane {
|
||||
getChildren().addAll(textField);
|
||||
}
|
||||
|
||||
public void setup(Address address, BSFormatter formatter) {
|
||||
public void setFormatter(BSFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
}
|
||||
|
||||
public void setupBalanceListener(Address address) {
|
||||
balanceListener = new BalanceListener(address) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
@ -74,16 +76,21 @@ public class BalanceTextField extends AnchorPane {
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
if (balanceListener != null)
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
public void setBalance(Coin balance) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void updateBalance(Coin balance) {
|
||||
textField.setText(formatter.formatCoinWithCode(balance));
|
||||
if (formatter != null)
|
||||
textField.setText(formatter.formatCoinWithCode(balance));
|
||||
if (balance.isPositive())
|
||||
textField.setEffect(fundedEffect);
|
||||
else
|
||||
|
@ -473,7 +473,7 @@ public class MainViewModel implements ViewModel {
|
||||
if (tuple.payload instanceof Ping &&
|
||||
((Ping) tuple.payload).nonce == payload.nonce &&
|
||||
((Ping) tuple.payload).lastRoundTripTime == payload.lastRoundTripTime)
|
||||
log.trace("Crypto test succeeded");
|
||||
log.debug("Crypto test succeeded");
|
||||
else
|
||||
throw new CryptoException("Payload not correct after decryption");
|
||||
} catch (CryptoException e) {
|
||||
@ -494,9 +494,7 @@ public class MainViewModel implements ViewModel {
|
||||
|
||||
}
|
||||
};
|
||||
// Delay a bit the test, there was one bug report (Key length not 128//192/256 bits) where the crypto test failed.
|
||||
// TODO investigate
|
||||
UserThread.runAfter(() -> checkCryptoThread.start(), 3);
|
||||
checkCryptoThread.start();
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,7 +51,7 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
||||
|
||||
private ListView<String> languagesListView;
|
||||
private ComboBox<String> languageComboBox;
|
||||
private TableView<ArbitratorListItem> table;
|
||||
private TableView<ArbitratorListItem> tableView;
|
||||
private int gridRow = 0;
|
||||
private CheckBox autoSelectAllMatchingCheckBox;
|
||||
private ListChangeListener<String> listChangeListener;
|
||||
@ -83,7 +83,7 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
||||
languagesListView.setItems(model.languageCodes);
|
||||
languagesListView.setPrefHeight(languagesListView.getItems().size() * Layout.LIST_ROW_HEIGHT + 2);
|
||||
|
||||
table.setItems(model.arbitratorListItems);
|
||||
tableView.setItems(model.arbitratorListItems);
|
||||
autoSelectAllMatchingCheckBox.setSelected(model.getAutoSelectArbitrators());
|
||||
}
|
||||
|
||||
@ -188,11 +188,11 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
||||
GridPane.setMargin(tableGroupHeadline, new Insets(40, -10, -10, -10));
|
||||
root.getChildren().add(tableGroupHeadline);
|
||||
|
||||
table = new TableView<>();
|
||||
GridPane.setRowIndex(table, gridRow);
|
||||
GridPane.setColumnSpan(table, 2);
|
||||
GridPane.setMargin(table, new Insets(60, -10, 5, -10));
|
||||
root.getChildren().add(table);
|
||||
tableView = new TableView<>();
|
||||
GridPane.setRowIndex(tableView, gridRow);
|
||||
GridPane.setColumnSpan(tableView, 2);
|
||||
GridPane.setMargin(tableView, new Insets(60, -10, 5, -10));
|
||||
root.getChildren().add(tableView);
|
||||
|
||||
autoSelectAllMatchingCheckBox = addCheckBox(root, ++gridRow, "Auto select all arbitrators with matching language");
|
||||
GridPane.setColumnSpan(autoSelectAllMatchingCheckBox, 2);
|
||||
@ -202,15 +202,18 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
||||
autoSelectAllMatchingCheckBox.setOnAction(event -> model.setAutoSelectArbitrators(autoSelectAllMatchingCheckBox.isSelected()));
|
||||
|
||||
TableColumn<ArbitratorListItem, String> dateColumn = new TableColumn("Registration date");
|
||||
dateColumn.setSortable(false);
|
||||
dateColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper(param.getValue().getRegistrationDate()));
|
||||
dateColumn.setMinWidth(130);
|
||||
dateColumn.setMaxWidth(130);
|
||||
|
||||
TableColumn<ArbitratorListItem, String> nameColumn = new TableColumn("Onion address");
|
||||
nameColumn.setSortable(false);
|
||||
nameColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper(param.getValue().getAddressString()));
|
||||
nameColumn.setMinWidth(90);
|
||||
|
||||
TableColumn<ArbitratorListItem, String> languagesColumn = new TableColumn("Languages");
|
||||
languagesColumn.setSortable(false);
|
||||
languagesColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper(param.getValue().getLanguageCodes()));
|
||||
languagesColumn.setMinWidth(130);
|
||||
|
||||
@ -309,8 +312,8 @@ public class ArbitratorSelectionView extends ActivatableViewAndModel<GridPane, A
|
||||
}
|
||||
});
|
||||
|
||||
table.getColumns().addAll(dateColumn, nameColumn, languagesColumn, selectionColumn);
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.getColumns().addAll(dateColumn, nameColumn, languagesColumn, selectionColumn);
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ public class DebugView extends InitializableView {
|
||||
VerifyArbitrationSelection.class,
|
||||
VerifyTakerAccount.class,
|
||||
CreateAndSignContract.class,
|
||||
CreateAndSignDepositTxAsBuyer.class,
|
||||
OffererCreatesAndSignsDepositTxAsBuyer.class,
|
||||
LoadTakeOfferFeeTx.class,
|
||||
InitWaitPeriodForOpenDispute.class,
|
||||
SetupDepositBalanceListener.class,
|
||||
@ -106,7 +106,7 @@ public class DebugView extends InitializableView {
|
||||
SelectArbitrator.class,
|
||||
CreateTakeOfferFeeTx.class,
|
||||
BroadcastTakeOfferFeeTx.class,
|
||||
CreateDepositTxInputsAsSeller.class,
|
||||
TakerCreatesDepositTxInputsAsSeller.class,
|
||||
SendPayDepositRequest.class,
|
||||
|
||||
ProcessPublishDepositTxRequest.class,
|
||||
@ -132,7 +132,7 @@ public class DebugView extends InitializableView {
|
||||
SelectArbitrator.class,
|
||||
CreateTakeOfferFeeTx.class,
|
||||
BroadcastTakeOfferFeeTx.class,
|
||||
CreateDepositTxInputsAsSeller.class,
|
||||
TakerCreatesDepositTxInputsAsSeller.class,
|
||||
SendPayDepositRequest.class,
|
||||
|
||||
ProcessPublishDepositTxRequest.class,
|
||||
@ -159,7 +159,7 @@ public class DebugView extends InitializableView {
|
||||
VerifyTakerAccount.class,
|
||||
InitWaitPeriodForOpenDispute.class,
|
||||
CreateAndSignContract.class,
|
||||
CreateAndSignDepositTxAsBuyer.class,
|
||||
OffererCreatesAndSignsDepositTxAsBuyer.class,
|
||||
SetupDepositBalanceListener.class,
|
||||
SendPublishDepositTxRequest.class,
|
||||
|
||||
|
@ -90,7 +90,9 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
||||
|
||||
private final List<Attachment> tempAttachments = new ArrayList<>();
|
||||
|
||||
private TableView<Dispute> disputesTable;
|
||||
private TableView<Dispute> tableView;
|
||||
private SortedList<Dispute> sortedList;
|
||||
|
||||
private Dispute selectedDispute;
|
||||
private ListView<DisputeCommunicationMessage> messageListView;
|
||||
private TextArea inputTextArea;
|
||||
@ -133,28 +135,32 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
disputesTable = new TableView<>();
|
||||
VBox.setVgrow(disputesTable, Priority.SOMETIMES);
|
||||
disputesTable.setMinHeight(150);
|
||||
root.getChildren().add(disputesTable);
|
||||
|
||||
TableColumn<Dispute, Dispute> tradeIdColumn = getTradeIdColumn();
|
||||
disputesTable.getColumns().add(tradeIdColumn);
|
||||
TableColumn<Dispute, Dispute> roleColumn = getRoleColumn();
|
||||
disputesTable.getColumns().add(roleColumn);
|
||||
TableColumn<Dispute, Dispute> dateColumn = getDateColumn();
|
||||
disputesTable.getColumns().add(dateColumn);
|
||||
TableColumn<Dispute, Dispute> contractColumn = getContractColumn();
|
||||
disputesTable.getColumns().add(contractColumn);
|
||||
TableColumn<Dispute, Dispute> stateColumn = getStateColumn();
|
||||
disputesTable.getColumns().add(stateColumn);
|
||||
|
||||
disputesTable.getSortOrder().add(dateColumn);
|
||||
disputesTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView = new TableView<>();
|
||||
VBox.setVgrow(tableView, Priority.SOMETIMES);
|
||||
tableView.setMinHeight(150);
|
||||
root.getChildren().add(tableView);
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
Label placeholder = new Label("There are no open tickets");
|
||||
placeholder.setWrapText(true);
|
||||
disputesTable.setPlaceholder(placeholder);
|
||||
disputesTable.getSelectionModel().clearSelection();
|
||||
tableView.setPlaceholder(placeholder);
|
||||
tableView.getSelectionModel().clearSelection();
|
||||
|
||||
TableColumn<Dispute, Dispute> tradeIdColumn = getTradeIdColumn();
|
||||
tableView.getColumns().add(tradeIdColumn);
|
||||
TableColumn<Dispute, Dispute> roleColumn = getRoleColumn();
|
||||
tableView.getColumns().add(roleColumn);
|
||||
TableColumn<Dispute, Dispute> dateColumn = getDateColumn();
|
||||
tableView.getColumns().add(dateColumn);
|
||||
TableColumn<Dispute, Dispute> contractColumn = getContractColumn();
|
||||
tableView.getColumns().add(contractColumn);
|
||||
TableColumn<Dispute, Dispute> stateColumn = getStateColumn();
|
||||
tableView.getColumns().add(stateColumn);
|
||||
|
||||
tradeIdColumn.setComparator((o1, o2) -> o1.getTradeId().compareTo(o2.getTradeId()));
|
||||
dateColumn.setComparator((o1, o2) -> o1.getOpeningDate().compareTo(o2.getOpeningDate()));
|
||||
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
|
||||
/*inputTextAreaListener = (observable, oldValue, newValue) ->
|
||||
sendButton.setDisable(newValue.length() == 0
|
||||
@ -183,24 +189,27 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
|
||||
FilteredList<Dispute> filteredList = new FilteredList<>(disputeManager.getDisputesAsObservableList());
|
||||
setFilteredListPredicate(filteredList);
|
||||
SortedList<Dispute> sortedList = new SortedList<>(filteredList);
|
||||
// sortedList.setComparator((o1, o2) -> o2.getOpeningDate().compareTo(o1.getOpeningDate()));
|
||||
sortedList.comparatorProperty().bind(disputesTable.comparatorProperty());
|
||||
disputesTable.setItems(sortedList);
|
||||
disputesTable.sort();
|
||||
selectedDisputeSubscription = EasyBind.subscribe(disputesTable.getSelectionModel().selectedItemProperty(), this::onSelectDispute);
|
||||
|
||||
Dispute selectedItem = disputesTable.getSelectionModel().getSelectedItem();
|
||||
sortedList = new SortedList<>(filteredList);
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
|
||||
// sortedList.setComparator((o1, o2) -> o2.getOpeningDate().compareTo(o1.getOpeningDate()));
|
||||
selectedDisputeSubscription = EasyBind.subscribe(tableView.getSelectionModel().selectedItemProperty(), this::onSelectDispute);
|
||||
|
||||
Dispute selectedItem = tableView.getSelectionModel().getSelectedItem();
|
||||
if (selectedItem != null)
|
||||
disputesTable.getSelectionModel().select(selectedItem);
|
||||
tableView.getSelectionModel().select(selectedItem);
|
||||
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
sortedList.comparatorProperty().unbind();
|
||||
selectedDisputeSubscription.unsubscribe();
|
||||
removeListenersOnSelectDispute();
|
||||
}
|
||||
@ -752,6 +761,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
||||
setMinWidth(130);
|
||||
}
|
||||
};
|
||||
column.setSortable(false);
|
||||
column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<Dispute, Dispute>, TableCell<Dispute, Dispute>>() {
|
||||
@ -809,6 +819,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
||||
setSortable(false);
|
||||
}
|
||||
};
|
||||
column.setSortable(false);
|
||||
column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<Dispute, Dispute>, TableCell<Dispute, Dispute>>() {
|
||||
@ -846,6 +857,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
|
||||
setMinWidth(50);
|
||||
}
|
||||
};
|
||||
column.setSortable(false);
|
||||
column.setCellValueFactory((dispute) -> new ReadOnlyObjectWrapper<>(dispute.getValue()));
|
||||
column.setCellFactory(
|
||||
new Callback<TableColumn<Dispute, Dispute>, TableCell<Dispute, Dispute>>() {
|
||||
|
@ -37,6 +37,8 @@ public class DepositListItem {
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
private final StringProperty balance = new SimpleStringProperty();
|
||||
private final WalletService walletService;
|
||||
|
||||
private Coin balanceAsCoin;
|
||||
private BSFormatter formatter;
|
||||
private final ConfidenceProgressIndicator progressIndicator;
|
||||
private final Tooltip tooltip;
|
||||
@ -44,8 +46,9 @@ public class DepositListItem {
|
||||
|
||||
private String balanceString;
|
||||
private String addressString;
|
||||
private String status = "Unused";
|
||||
private String usage = "-";
|
||||
private TxConfidenceListener txConfidenceListener;
|
||||
private int numTxOutputs = 0;
|
||||
|
||||
// public DepositListItem(AddressEntry addressEntry, Transaction transaction, WalletService walletService, Optional<Tradable> tradableOptional, BSFormatter formatter) {
|
||||
public DepositListItem(AddressEntry addressEntry, WalletService walletService, BSFormatter formatter) {
|
||||
@ -67,17 +70,17 @@ public class DepositListItem {
|
||||
walletService.addBalanceListener(new BalanceListener(address) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balanceAsCoin, Transaction tx) {
|
||||
DepositListItem.this.balanceAsCoin = balanceAsCoin;
|
||||
DepositListItem.this.balance.set(formatter.formatCoin(balanceAsCoin));
|
||||
updateConfidence(walletService.getConfidenceForTxId(tx.getHashAsString()));
|
||||
if (balanceAsCoin.isPositive())
|
||||
status = "Funded";
|
||||
updateUsage(address);
|
||||
}
|
||||
});
|
||||
|
||||
Coin balanceAsCoin = walletService.getBalanceForAddress(address);
|
||||
balanceAsCoin = walletService.getBalanceForAddress(address);
|
||||
balance.set(formatter.formatCoin(balanceAsCoin));
|
||||
if (balanceAsCoin.isPositive())
|
||||
status = "Funded";
|
||||
|
||||
updateUsage(address);
|
||||
|
||||
TransactionConfidence transactionConfidence = walletService.getConfidenceForAddress(address);
|
||||
if (transactionConfidence != null) {
|
||||
@ -93,8 +96,9 @@ public class DepositListItem {
|
||||
}
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
private void updateUsage(Address address) {
|
||||
numTxOutputs = walletService.getNumTxOutputsForAddress(address);
|
||||
usage = numTxOutputs == 0 ? "Unused" : "Used in " + numTxOutputs + " transactions";
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
@ -134,8 +138,8 @@ public class DepositListItem {
|
||||
return addressString;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
public String getUsage() {
|
||||
return usage;
|
||||
}
|
||||
|
||||
public final StringProperty balanceProperty() {
|
||||
@ -146,4 +150,11 @@ public class DepositListItem {
|
||||
return balance.get();
|
||||
}
|
||||
|
||||
public Coin getBalanceAsCoin() {
|
||||
return balanceAsCoin;
|
||||
}
|
||||
|
||||
public int getNumTxOutputs() {
|
||||
return numTxOutputs;
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.funds.deposit.DepositView"
|
||||
@ -27,18 +26,13 @@
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Select" fx:id="selectColumn" minWidth="110" maxWidth="110" sortable="false"/>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="260"/>
|
||||
<TableColumn text="Balance (BTC)" fx:id="balanceColumn" minWidth="150" maxWidth="150">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="balance"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
|
||||
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="150" maxWidth="150"/>
|
||||
<TableColumn text="Status" fx:id="statusColumn" minWidth="150" maxWidth="150"/>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="320"/>
|
||||
<TableColumn text="Balance (BTC)" fx:id="balanceColumn" minWidth="150"/>
|
||||
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="150"/>
|
||||
<TableColumn text="Usage" fx:id="usageColumn" minWidth="200"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
|
@ -18,7 +18,9 @@
|
||||
package io.bitsquare.gui.main.funds.deposit;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.Restrictions;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.common.util.Tuple2;
|
||||
@ -27,26 +29,20 @@ import io.bitsquare.gui.common.view.ActivatableView;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.AddressTextField;
|
||||
import io.bitsquare.gui.components.HyperlinkWithIcon;
|
||||
import io.bitsquare.gui.components.InputTextField;
|
||||
import io.bitsquare.gui.components.TitledGroupBg;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow;
|
||||
import io.bitsquare.gui.main.overlays.windows.QRCodeWindow;
|
||||
import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
|
||||
import io.bitsquare.gui.main.overlays.windows.WalletPasswordWindow;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.Layout;
|
||||
import io.bitsquare.gui.util.validation.BtcAddressValidator;
|
||||
import io.bitsquare.trade.TradeManager;
|
||||
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||
import io.bitsquare.trade.failed.FailedTradesManager;
|
||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
@ -58,6 +54,8 @@ import net.glxn.qrgen.image.ImageType;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.uri.BitcoinURI;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@ -72,30 +70,27 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
GridPane gridPane;
|
||||
|
||||
@FXML
|
||||
TableView<DepositListItem> table;
|
||||
TableView<DepositListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<DepositListItem, DepositListItem> selectColumn, addressColumn, balanceColumn, confidenceColumn, statusColumn;
|
||||
TableColumn<DepositListItem, DepositListItem> selectColumn, addressColumn, balanceColumn, confidenceColumn, usageColumn;
|
||||
private ImageView qrCodeImageView;
|
||||
private int gridRow = 0;
|
||||
private AddressTextField addressTextField;
|
||||
Button generateNewAddressButton;
|
||||
|
||||
private final WalletService walletService;
|
||||
private final TradeManager tradeManager;
|
||||
private final ClosedTradableManager closedTradableManager;
|
||||
private final FailedTradesManager failedTradesManager;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final BSFormatter formatter;
|
||||
private final Preferences preferences;
|
||||
private final BtcAddressValidator btcAddressValidator;
|
||||
private final WalletPasswordWindow walletPasswordWindow;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private final ObservableList<DepositListItem> depositAddresses = FXCollections.observableArrayList();
|
||||
private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList();
|
||||
private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList);
|
||||
|
||||
private BalanceListener balanceListener;
|
||||
private TitledGroupBg titledGroupBg;
|
||||
private Label addressLabel;
|
||||
private Label addressLabel, amountLabel;
|
||||
private Label qrCodeLabel;
|
||||
private InputTextField amountTextField;
|
||||
private Subscription amountTextFieldSubscription;
|
||||
private String paymentLabel;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -103,36 +98,34 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private DepositView(WalletService walletService, TradeManager tradeManager,
|
||||
ClosedTradableManager closedTradableManager,
|
||||
FailedTradesManager failedTradesManager, OpenOfferManager openOfferManager,
|
||||
BSFormatter formatter, Preferences preferences,
|
||||
BtcAddressValidator btcAddressValidator, WalletPasswordWindow walletPasswordWindow,
|
||||
OfferDetailsWindow offerDetailsWindow, TradeDetailsWindow tradeDetailsWindow) {
|
||||
private DepositView(WalletService walletService,
|
||||
BSFormatter formatter,
|
||||
Preferences preferences) {
|
||||
this.walletService = walletService;
|
||||
this.tradeManager = tradeManager;
|
||||
this.closedTradableManager = closedTradableManager;
|
||||
this.failedTradesManager = failedTradesManager;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.formatter = formatter;
|
||||
this.preferences = preferences;
|
||||
this.btcAddressValidator = btcAddressValidator;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.offerDetailsWindow = offerDetailsWindow;
|
||||
this.tradeDetailsWindow = tradeDetailsWindow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
table.setPlaceholder(new Label("No deposit addresses are generated yet"));
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No deposit addresses are generated yet"));
|
||||
|
||||
setSelectColumnCellFactory();
|
||||
setAddressColumnCellFactory();
|
||||
setStatusColumnCellFactory();
|
||||
setBalanceColumnCellFactory();
|
||||
setUsageColumnCellFactory();
|
||||
setConfidenceColumnCellFactory();
|
||||
|
||||
titledGroupBg = addTitledGroupBg(gridPane, gridRow, 2, "Fund your wallet");
|
||||
addressColumn.setComparator((o1, o2) -> o1.getAddressString().compareTo(o2.getAddressString()));
|
||||
balanceColumn.setComparator((o1, o2) -> o1.getBalanceAsCoin().compareTo(o2.getBalanceAsCoin()));
|
||||
confidenceColumn.setComparator((o1, o2) -> Double.valueOf(o1.getProgressIndicator().getProgress())
|
||||
.compareTo(o2.getProgressIndicator().getProgress()));
|
||||
usageColumn.setComparator((a, b) -> (a.getNumTxOutputs() < b.getNumTxOutputs()) ? -1 : ((a.getNumTxOutputs() == b.getNumTxOutputs()) ? 0 : 1));
|
||||
tableView.getSortOrder().add(usageColumn);
|
||||
|
||||
|
||||
titledGroupBg = addTitledGroupBg(gridPane, gridRow, 3, "Fund your wallet");
|
||||
|
||||
qrCodeLabel = addLabel(gridPane, gridRow, "QR-Code:", 0);
|
||||
//GridPane.setMargin(qrCodeLabel, new Insets(Layout.FIRST_ROW_DISTANCE - 9, 0, 0, 5));
|
||||
@ -148,9 +141,18 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
|
||||
Tuple2<Label, AddressTextField> addressTuple = addLabelAddressTextField(gridPane, ++gridRow, "Address:");
|
||||
addressLabel = addressTuple.first;
|
||||
GridPane.setValignment(addressLabel, VPos.TOP);
|
||||
GridPane.setMargin(addressLabel, new Insets(3, 0, 0, 0));
|
||||
//GridPane.setValignment(addressLabel, VPos.TOP);
|
||||
//GridPane.setMargin(addressLabel, new Insets(3, 0, 0, 0));
|
||||
addressTextField = addressTuple.second;
|
||||
paymentLabel = "Fund Bitsquare wallet";
|
||||
addressTextField.setPaymentLabel(paymentLabel);
|
||||
|
||||
|
||||
Tuple2<Label, InputTextField> amountTuple = addLabelInputTextField(gridPane, ++gridRow, "Amount in BTC (optional):");
|
||||
amountLabel = amountTuple.first;
|
||||
amountTextField = amountTuple.second;
|
||||
if (BitsquareApp.DEV_MODE)
|
||||
amountTextField.setText("1");
|
||||
|
||||
titledGroupBg.setVisible(false);
|
||||
titledGroupBg.setManaged(false);
|
||||
@ -162,16 +164,18 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
addressLabel.setManaged(false);
|
||||
addressTextField.setVisible(false);
|
||||
addressTextField.setManaged(false);
|
||||
amountLabel.setVisible(false);
|
||||
amountTextField.setManaged(false);
|
||||
|
||||
generateNewAddressButton = addButton(gridPane, ++gridRow, "Generate new address", -20);
|
||||
GridPane.setColumnIndex(generateNewAddressButton, 0);
|
||||
GridPane.setHalignment(generateNewAddressButton, HPos.LEFT);
|
||||
|
||||
generateNewAddressButton.setOnAction(event -> {
|
||||
boolean hasUnUsedAddress = walletService.getSavingsAddressEntryList().stream()
|
||||
.filter(addressEntry -> walletService.getBalanceForAddress(addressEntry.getAddress()).isZero())
|
||||
.findAny().isPresent();
|
||||
boolean hasUnUsedAddress = observableList.stream().filter(e -> e.getNumTxOutputs() == 0).findAny().isPresent();
|
||||
if (hasUnUsedAddress) {
|
||||
new Popup().warning("You have already addresses generated which are still not used.\n" +
|
||||
"Please select in the address table an unused address.").show();
|
||||
new Popup().warning("You have addresses which are not used in any transaction.\n" +
|
||||
"Please select in the address table any unused address.").show();
|
||||
} else {
|
||||
AddressEntry newSavingsAddressEntry = walletService.getNewSavingsAddressEntry();
|
||||
fillForm(newSavingsAddressEntry.getAddressString());
|
||||
@ -187,25 +191,44 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
};
|
||||
}
|
||||
|
||||
private Coin getAmountAsCoin() {
|
||||
Coin senderAmount = formatter.parseToCoin(amountTextField.getText());
|
||||
if (!Restrictions.isAboveFixedTxFeeAndDust(senderAmount)) {
|
||||
senderAmount = Coin.ZERO;
|
||||
/* new Popup()
|
||||
.warning("The amount is lower than the transaction fee and the min. possible tx value (dust).")
|
||||
.show();*/
|
||||
}
|
||||
return senderAmount;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getBitcoinURI() {
|
||||
return BitcoinURI.convertToBitcoinURI(addressTextField.getAddress(),
|
||||
null,
|
||||
null,
|
||||
getAmountAsCoin(),
|
||||
paymentLabel,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
updateList();
|
||||
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
|
||||
addressTextField.setAmountAsCoin(formatter.parseToCoin(t));
|
||||
updateQRCode();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
depositAddresses.forEach(DepositListItem::cleanup);
|
||||
sortedList.comparatorProperty().unbind();
|
||||
observableList.forEach(DepositListItem::cleanup);
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
amountTextFieldSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
|
||||
@ -225,22 +248,30 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
addressLabel.setManaged(true);
|
||||
addressTextField.setVisible(true);
|
||||
addressTextField.setManaged(true);
|
||||
amountLabel.setVisible(true);
|
||||
amountTextField.setManaged(true);
|
||||
|
||||
GridPane.setMargin(generateNewAddressButton, new Insets(15, 0, 0, 0));
|
||||
|
||||
addressTextField.setAddress(address);
|
||||
|
||||
final byte[] imageBytes = QRCode
|
||||
.from(getBitcoinURI())
|
||||
.withSize(150, 150) // code has 41 elements 8 px is border with 150 we get 3x scale and min. border
|
||||
.to(ImageType.PNG)
|
||||
.stream()
|
||||
.toByteArray();
|
||||
Image qrImage = new Image(new ByteArrayInputStream(imageBytes));
|
||||
qrCodeImageView.setImage(qrImage);
|
||||
updateQRCode();
|
||||
|
||||
}
|
||||
|
||||
private void updateQRCode() {
|
||||
if (addressTextField.getAddress() != null && !addressTextField.getAddress().isEmpty()) {
|
||||
final byte[] imageBytes = QRCode
|
||||
.from(getBitcoinURI())
|
||||
.withSize(150, 150) // code has 41 elements 8 px is border with 150 we get 3x scale and min. border
|
||||
.to(ImageType.PNG)
|
||||
.stream()
|
||||
.toByteArray();
|
||||
Image qrImage = new Image(new ByteArrayInputStream(imageBytes));
|
||||
qrCodeImageView.setImage(qrImage);
|
||||
}
|
||||
}
|
||||
|
||||
private void openBlockExplorer(DepositListItem item) {
|
||||
if (item.getAddressString() != null) {
|
||||
try {
|
||||
@ -258,10 +289,9 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void updateList() {
|
||||
depositAddresses.clear();
|
||||
observableList.clear();
|
||||
walletService.getSavingsAddressEntryList().stream()
|
||||
.forEach(e -> depositAddresses.add(new DepositListItem(e, walletService, formatter)));
|
||||
table.setItems(depositAddresses);
|
||||
.forEach(e -> observableList.add(new DepositListItem(e, walletService, formatter)));
|
||||
}
|
||||
|
||||
|
||||
@ -269,9 +299,9 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
// ColumnCellFactories
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setStatusColumnCellFactory() {
|
||||
statusColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
statusColumn.setCellFactory(new Callback<TableColumn<DepositListItem, DepositListItem>,
|
||||
private void setUsageColumnCellFactory() {
|
||||
usageColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
usageColumn.setCellFactory(new Callback<TableColumn<DepositListItem, DepositListItem>,
|
||||
TableCell<DepositListItem, DepositListItem>>() {
|
||||
|
||||
@Override
|
||||
@ -283,7 +313,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
public void updateItem(final DepositListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty) {
|
||||
setGraphic(new Label(item.getStatus()));
|
||||
setGraphic(new Label(item.getUsage()));
|
||||
} else {
|
||||
setGraphic(null);
|
||||
}
|
||||
@ -364,6 +394,33 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||
});
|
||||
}
|
||||
|
||||
private void setBalanceColumnCellFactory() {
|
||||
balanceColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
balanceColumn.setCellFactory(new Callback<TableColumn<DepositListItem, DepositListItem>,
|
||||
TableCell<DepositListItem, DepositListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<DepositListItem, DepositListItem> call(TableColumn<DepositListItem,
|
||||
DepositListItem> column) {
|
||||
return new TableCell<DepositListItem, DepositListItem>() {
|
||||
|
||||
@Override
|
||||
public void updateItem(final DepositListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty) {
|
||||
if (!textProperty().isBound())
|
||||
textProperty().bind(item.balanceProperty());
|
||||
} else {
|
||||
textProperty().unbind();
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void setConfidenceColumnCellFactory() {
|
||||
confidenceColumn.setCellValueFactory((addressListItem) ->
|
||||
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
|
@ -142,4 +142,9 @@ public class ReservedListItem {
|
||||
public String getFundsInfo() {
|
||||
return fundsInfo;
|
||||
}
|
||||
|
||||
public Tradable getTradable() {
|
||||
return tradable;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.funds.reserved.ReservedView"
|
||||
@ -27,15 +26,11 @@
|
||||
<Insets bottom="0.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="180" maxWidth="180"/>
|
||||
<TableColumn text="Details" fx:id="detailsColumn" minWidth="260"/>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="320">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="addressString"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="320"/>
|
||||
<TableColumn text="Balance (BTC)" fx:id="balanceColumn" minWidth="110"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
@ -38,6 +38,7 @@ import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
@ -53,7 +54,7 @@ import java.util.stream.Stream;
|
||||
@FxmlView
|
||||
public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
@FXML
|
||||
TableView<ReservedListItem> table;
|
||||
TableView<ReservedListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<ReservedListItem, ReservedListItem> dateColumn, detailsColumn, addressColumn, balanceColumn, confidenceColumn;
|
||||
|
||||
@ -64,7 +65,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
private final BSFormatter formatter;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private final ObservableList<ReservedListItem> reservedAddresses = FXCollections.observableArrayList();
|
||||
private final ObservableList<ReservedListItem> observableList = FXCollections.observableArrayList();
|
||||
private final SortedList<ReservedListItem> sortedList = new SortedList<>(observableList);
|
||||
private BalanceListener balanceListener;
|
||||
|
||||
|
||||
@ -87,13 +89,21 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
table.setPlaceholder(new Label("No funds are reserved in open offers or trades"));
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No funds are reserved in open offers or trades"));
|
||||
|
||||
setDateColumnCellFactory();
|
||||
setDetailsColumnCellFactory();
|
||||
setAddressColumnCellFactory();
|
||||
setBalanceColumnCellFactory();
|
||||
table.getSortOrder().add(dateColumn);
|
||||
|
||||
addressColumn.setComparator((o1, o2) -> o1.getAddressString().compareTo(o2.getAddressString()));
|
||||
detailsColumn.setComparator((o1, o2) -> o1.getTradable().getId().compareTo(o2.getTradable().getId()));
|
||||
balanceColumn.setComparator((o1, o2) -> o1.getBalance().compareTo(o2.getBalance()));
|
||||
dateColumn.setComparator((o1, o2) -> getTradable(o2).get().getDate().compareTo(getTradable(o1).get().getDate()));
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
|
||||
balanceListener = new BalanceListener() {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
@ -104,6 +114,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
updateList();
|
||||
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
@ -111,7 +123,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
reservedAddresses.forEach(ReservedListItem::cleanup);
|
||||
sortedList.comparatorProperty().unbind();
|
||||
observableList.forEach(ReservedListItem::cleanup);
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
@ -121,13 +134,12 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void updateList() {
|
||||
reservedAddresses.forEach(ReservedListItem::cleanup);
|
||||
reservedAddresses.setAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
||||
observableList.forEach(ReservedListItem::cleanup);
|
||||
observableList.clear();
|
||||
observableList.setAll(Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
||||
.filter(tradable -> !(tradable instanceof Trade) || ((Trade) tradable).getState().getPhase() != Trade.Phase.PAYOUT_PAID)
|
||||
.map(tradable -> new ReservedListItem(tradable, walletService.getTradeAddressEntry(tradable.getOffer().getId()), walletService, formatter))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
reservedAddresses.sort((o1, o2) -> getTradable(o2).get().getDate().compareTo(getTradable(o1).get().getDate()));
|
||||
table.setItems(reservedAddresses);
|
||||
}
|
||||
|
||||
private void openBlockExplorer(ReservedListItem item) {
|
||||
|
@ -24,24 +24,24 @@ import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.trade.Tradable;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.offer.OpenOffer;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import org.bitcoinj.core.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Optional;
|
||||
|
||||
public class TransactionsListItem {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
private final StringProperty date = new SimpleStringProperty();
|
||||
private final StringProperty amount = new SimpleStringProperty();
|
||||
|
||||
private String date;
|
||||
private final String txId;
|
||||
private final WalletService walletService;
|
||||
private final ConfidenceProgressIndicator progressIndicator;
|
||||
private final Tooltip tooltip;
|
||||
private boolean isInternal;
|
||||
@Nullable
|
||||
private Tradable tradable;
|
||||
private String details;
|
||||
private String addressString;
|
||||
@ -49,18 +49,19 @@ public class TransactionsListItem {
|
||||
private TxConfidenceListener txConfidenceListener;
|
||||
private boolean received;
|
||||
private boolean detailsAvailable;
|
||||
private Coin amountAsCoin = Coin.ZERO;
|
||||
private BSFormatter formatter;
|
||||
|
||||
public TransactionsListItem(Transaction transaction, WalletService walletService, Optional<Tradable> tradableOptional, BSFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
txId = transaction.getHashAsString();
|
||||
this.walletService = walletService;
|
||||
|
||||
Coin valueSentToMe = transaction.getValueSentToMe(walletService.getWallet());
|
||||
Coin valueSentFromMe = transaction.getValueSentFromMe(walletService.getWallet());
|
||||
Coin amountAsCoin;
|
||||
Address address = null;
|
||||
if (valueSentToMe.isZero()) {
|
||||
amountAsCoin = valueSentFromMe;
|
||||
amount.set("-" + formatter.formatCoin(amountAsCoin));
|
||||
amountAsCoin = valueSentFromMe.multiply(-1);
|
||||
|
||||
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
|
||||
if (!transactionOutput.isMine(walletService.getWallet())) {
|
||||
@ -75,7 +76,7 @@ public class TransactionsListItem {
|
||||
}
|
||||
} else if (valueSentFromMe.isZero()) {
|
||||
amountAsCoin = valueSentToMe;
|
||||
amount.set(formatter.formatCoin(amountAsCoin));
|
||||
|
||||
direction = "Received with:";
|
||||
received = true;
|
||||
|
||||
@ -88,9 +89,8 @@ public class TransactionsListItem {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else/* if (tradableOptional.isPresent())*/ {
|
||||
amountAsCoin = valueSentToMe.subtract(valueSentFromMe);
|
||||
amount.set(formatter.formatCoin(amountAsCoin));
|
||||
boolean outgoing = false;
|
||||
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
|
||||
if (!transactionOutput.isMine(walletService.getWallet())) {
|
||||
@ -107,7 +107,25 @@ public class TransactionsListItem {
|
||||
direction = "Sent to:";
|
||||
received = false;
|
||||
}
|
||||
}
|
||||
} /*else {
|
||||
// savings wallet tx
|
||||
for (TransactionOutput transactionOutput : transaction.getOutputs()) {
|
||||
if (transactionOutput.isMine(walletService.getWallet())) {
|
||||
if (transactionOutput.getScriptPubKey().isSentToAddress() ||
|
||||
transactionOutput.getScriptPubKey().isPayToScriptHash()) {
|
||||
address = transactionOutput.getScriptPubKey().getToAddress(walletService.getWallet().getParams());
|
||||
addressString = address.toString();
|
||||
|
||||
amountAsCoin = transactionOutput.getValue().multiply(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
direction = "Transferred to:";
|
||||
received = false;
|
||||
isInternal = true;
|
||||
details = "Change output";
|
||||
}*/
|
||||
|
||||
|
||||
if (tradableOptional.isPresent()) {
|
||||
@ -141,11 +159,11 @@ public class TransactionsListItem {
|
||||
} else {
|
||||
if (amountAsCoin.isZero())
|
||||
details = "No refund from dispute";
|
||||
else
|
||||
else if (!isInternal)
|
||||
details = received ? "Received funds" : "Withdrawn from wallet";
|
||||
}
|
||||
|
||||
date.set(formatter.formatDateTime(transaction.getUpdateTime()));
|
||||
date = formatter.formatDateTime(transaction.getUpdateTime());
|
||||
|
||||
// confidence
|
||||
progressIndicator = new ConfidenceProgressIndicator();
|
||||
@ -202,14 +220,20 @@ public class TransactionsListItem {
|
||||
return progressIndicator;
|
||||
}
|
||||
|
||||
public final StringProperty dateProperty() {
|
||||
return this.date;
|
||||
public final String getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public final StringProperty amountProperty() {
|
||||
return this.amount;
|
||||
|
||||
public String getAmount() {
|
||||
return formatter.formatCoin(amountAsCoin);
|
||||
}
|
||||
|
||||
public Coin getAmountAsCoin() {
|
||||
return amountAsCoin;
|
||||
}
|
||||
|
||||
|
||||
public String getAddressString() {
|
||||
return addressString;
|
||||
}
|
||||
@ -218,6 +242,10 @@ public class TransactionsListItem {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public boolean isInternal() {
|
||||
return isInternal;
|
||||
}
|
||||
|
||||
public String getTxId() {
|
||||
return txId;
|
||||
}
|
||||
@ -234,6 +262,7 @@ public class TransactionsListItem {
|
||||
return detailsAvailable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Tradable getTradable() {
|
||||
return tradable;
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.funds.transactions.TransactionsView"
|
||||
@ -26,22 +25,13 @@
|
||||
<padding>
|
||||
<Insets bottom="0.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="180" maxWidth="180">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="date"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="180" maxWidth="180"/>
|
||||
<TableColumn text="Details" fx:id="detailsColumn" minWidth="220" maxWidth="220"/>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="260"/>
|
||||
<TableColumn text="Transaction" fx:id="transactionColumn" minWidth="180"/>
|
||||
<TableColumn text="Amount (BTC)" fx:id="amountColumn" minWidth="110" maxWidth="110">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="amount"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
|
||||
<TableColumn text="Amount (BTC)" fx:id="amountColumn" minWidth="110" maxWidth="110"/>
|
||||
<TableColumn text="Confirmations" fx:id="confidenceColumn" minWidth="110" maxWidth="110"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
@ -40,6 +40,7 @@ import io.bitsquare.user.Preferences;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
@ -58,12 +59,12 @@ import java.util.stream.Stream;
|
||||
public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@FXML
|
||||
TableView<TransactionsListItem> table;
|
||||
TableView<TransactionsListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, typeColumn,
|
||||
confidenceColumn;
|
||||
TableColumn<TransactionsListItem, TransactionsListItem> dateColumn, detailsColumn, addressColumn, transactionColumn, amountColumn, confidenceColumn;
|
||||
|
||||
private final ObservableList<TransactionsListItem> transactionsListItems = FXCollections.observableArrayList();
|
||||
private final ObservableList<TransactionsListItem> observableList = FXCollections.observableArrayList();
|
||||
private final SortedList<TransactionsListItem> sortedList = new SortedList<>(observableList);
|
||||
|
||||
private final WalletService walletService;
|
||||
private final TradeManager tradeManager;
|
||||
@ -102,13 +103,31 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
table.setPlaceholder(new Label("No transactions available"));
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No transactions available"));
|
||||
|
||||
|
||||
setDateColumnCellFactory();
|
||||
setDetailsColumnCellFactory();
|
||||
setAddressColumnCellFactory();
|
||||
setTransactionColumnCellFactory();
|
||||
setAmountColumnCellFactory();
|
||||
setConfidenceColumnCellFactory();
|
||||
table.getSortOrder().add(dateColumn);
|
||||
|
||||
dateColumn.setComparator((o1, o2) -> o1.getDate().compareTo(o2.getDate()));
|
||||
detailsColumn.setComparator((o1, o2) -> {
|
||||
String id1 = o1.getTradable() != null ? o1.getTradable().getId() : o1.getDetails();
|
||||
String id2 = o2.getTradable() != null ? o2.getTradable().getId() : o2.getDetails();
|
||||
return id1.compareTo(id2);
|
||||
});
|
||||
addressColumn.setComparator((o1, o2) -> o1.getAddressString().compareTo(o2.getAddressString()));
|
||||
transactionColumn.setComparator((o1, o2) -> o1.getTxId().compareTo(o2.getTxId()));
|
||||
amountColumn.setComparator((o1, o2) -> o1.getAmountAsCoin().compareTo(o2.getAmountAsCoin()));
|
||||
confidenceColumn.setComparator((o1, o2) -> Double.valueOf(o1.getProgressIndicator().getProgress())
|
||||
.compareTo(o2.getProgressIndicator().getProgress()));
|
||||
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
|
||||
walletEventListener = new WalletEventListener() {
|
||||
@Override
|
||||
@ -149,13 +168,17 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
updateList();
|
||||
|
||||
walletService.getWallet().addEventListener(walletEventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
transactionsListItems.forEach(TransactionsListItem::cleanup);
|
||||
sortedList.comparatorProperty().unbind();
|
||||
observableList.forEach(TransactionsListItem::cleanup);
|
||||
walletService.getWallet().removeEventListener(walletEventListener);
|
||||
}
|
||||
|
||||
@ -170,7 +193,40 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
Stream<Tradable> concat3 = Stream.concat(concat2, failedTradesManager.getFailedTrades().stream());
|
||||
Set<Tradable> all = concat3.collect(Collectors.toSet());
|
||||
|
||||
List<TransactionsListItem> listItems = walletService.getWallet().getRecentTransactions(1000, true).stream()
|
||||
Set<Transaction> transactions = walletService.getWallet().getTransactions(true);
|
||||
/* List<TransactionsListItem> transactionsListItems = new ArrayList<>();
|
||||
for (Transaction transaction : transactions) {
|
||||
Optional<Tradable> tradableOptional = all.stream()
|
||||
.filter(tradable -> {
|
||||
String txId = transaction.getHashAsString();
|
||||
if (tradable instanceof OpenOffer)
|
||||
return tradable.getOffer().getOfferFeePaymentTxID().equals(txId);
|
||||
else if (tradable instanceof Trade) {
|
||||
Trade trade = (Trade) tradable;
|
||||
boolean isTakeOfferFeeTx = txId.equals(trade.getTakeOfferFeeTxId());
|
||||
boolean isOfferFeeTx = trade.getOffer() != null &&
|
||||
txId.equals(trade.getOffer().getOfferFeePaymentTxID());
|
||||
boolean isDepositTx = trade.getDepositTx() != null &&
|
||||
trade.getDepositTx().getHashAsString().equals(txId);
|
||||
boolean isPayoutTx = trade.getPayoutTx() != null &&
|
||||
trade.getPayoutTx().getHashAsString().equals(txId);
|
||||
|
||||
boolean isDisputedPayoutTx = disputeManager.getDisputesAsObservableList().stream()
|
||||
.filter(dispute -> txId.equals(dispute.getDisputePayoutTxId()) &&
|
||||
tradable.getId().equals(dispute.getTradeId()))
|
||||
.findAny()
|
||||
.isPresent();
|
||||
|
||||
return isTakeOfferFeeTx || isOfferFeeTx || isDepositTx || isPayoutTx || isDisputedPayoutTx;
|
||||
} else
|
||||
return false;
|
||||
})
|
||||
.findAny();
|
||||
// if (tradableOptional.isPresent())
|
||||
transactionsListItems.add(new TransactionsListItem(transaction, walletService, tradableOptional, formatter));
|
||||
}*/
|
||||
|
||||
List<TransactionsListItem> transactionsListItems = transactions.stream()
|
||||
.map(transaction -> {
|
||||
Optional<Tradable> tradableOptional = all.stream()
|
||||
.filter(tradable -> {
|
||||
@ -202,10 +258,15 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
/* List<TransactionsListItem> usedSavingWalletEntries = walletService.getUsedSavingWalletTransactions()
|
||||
.stream()
|
||||
.map(transaction -> new TransactionsListItem(transaction, walletService, Optional.<Tradable>empty(), formatter))
|
||||
.collect(Collectors.toList());
|
||||
transactionsListItems.addAll(usedSavingWalletEntries);*/
|
||||
|
||||
// are sorted by getRecentTransactions
|
||||
transactionsListItems.forEach(TransactionsListItem::cleanup);
|
||||
transactionsListItems.setAll(listItems);
|
||||
table.setItems(transactionsListItems);
|
||||
observableList.forEach(TransactionsListItem::cleanup);
|
||||
observableList.setAll(transactionsListItems);
|
||||
}
|
||||
|
||||
private void openBlockExplorer(TransactionsListItem item) {
|
||||
@ -232,6 +293,33 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
// ColumnCellFactories
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setDateColumnCellFactory() {
|
||||
dateColumn.setCellValueFactory((addressListItem) ->
|
||||
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
dateColumn.setCellFactory(
|
||||
new Callback<TableColumn<TransactionsListItem, TransactionsListItem>, TableCell<TransactionsListItem,
|
||||
TransactionsListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<TransactionsListItem, TransactionsListItem> call(TableColumn<TransactionsListItem,
|
||||
TransactionsListItem> column) {
|
||||
return new TableCell<TransactionsListItem, TransactionsListItem>() {
|
||||
|
||||
@Override
|
||||
public void updateItem(final TransactionsListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
setText(item.getDate());
|
||||
} else {
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setDetailsColumnCellFactory() {
|
||||
detailsColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
detailsColumn.setCellFactory(
|
||||
@ -289,7 +377,7 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
if (item != null && !empty) {
|
||||
String addressString = item.getAddressString();
|
||||
field = new AddressWithIconAndDirection(item.getDirection(), addressString,
|
||||
AwesomeIcon.EXTERNAL_LINK, item.getReceived());
|
||||
AwesomeIcon.EXTERNAL_LINK, item.getReceived(), item.isInternal());
|
||||
field.setOnAction(event -> openBlockExplorer(item));
|
||||
field.setTooltip(new Tooltip("Open external blockchain explorer for " +
|
||||
"address: " + addressString));
|
||||
@ -339,6 +427,33 @@ public class TransactionsView extends ActivatableView<VBox, Void> {
|
||||
});
|
||||
}
|
||||
|
||||
private void setAmountColumnCellFactory() {
|
||||
amountColumn.setCellValueFactory((addressListItem) ->
|
||||
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
amountColumn.setCellFactory(
|
||||
new Callback<TableColumn<TransactionsListItem, TransactionsListItem>, TableCell<TransactionsListItem,
|
||||
TransactionsListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<TransactionsListItem, TransactionsListItem> call(TableColumn<TransactionsListItem,
|
||||
TransactionsListItem> column) {
|
||||
return new TableCell<TransactionsListItem, TransactionsListItem>() {
|
||||
|
||||
@Override
|
||||
public void updateItem(final TransactionsListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
setText(item.getAmount());
|
||||
} else {
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setConfidenceColumnCellFactory() {
|
||||
confidenceColumn.setCellValueFactory((addressListItem) ->
|
||||
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.funds.withdrawal.WithdrawalView"
|
||||
spacing="10" xmlns:fx="http://javafx.com/fxml">
|
||||
@ -27,17 +26,11 @@
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Select" fx:id="selectColumn" minWidth="60" maxWidth="60" sortable="false"/>
|
||||
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="180" maxWidth="180"/>
|
||||
<TableColumn text="Details" fx:id="detailsColumn" minWidth="160"/>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="320">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="addressString"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Balance (BTC)" fx:id="balanceColumn" minWidth="110"/>
|
||||
<TableColumn text="Address" fx:id="addressColumn" minWidth="320"/>
|
||||
<TableColumn text="Balance (BTC)" fx:id="balanceColumn" minWidth="310" maxWidth="310"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
|
@ -20,7 +20,6 @@ package io.bitsquare.gui.main.funds.withdrawal;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.btc.AddressEntry;
|
||||
import io.bitsquare.btc.Restrictions;
|
||||
import io.bitsquare.btc.WalletService;
|
||||
import io.bitsquare.btc.listeners.BalanceListener;
|
||||
@ -46,6 +45,7 @@ import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
@ -59,10 +59,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.spongycastle.crypto.params.KeyParameter;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -72,11 +69,11 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
@FXML
|
||||
Button withdrawButton;
|
||||
@FXML
|
||||
TableView<WithdrawalListItem> table;
|
||||
TableView<WithdrawalListItem> tableView;
|
||||
@FXML
|
||||
TextField withdrawFromTextField, withdrawToTextField, amountTextField;
|
||||
@FXML
|
||||
TableColumn<WithdrawalListItem, WithdrawalListItem> dateColumn, detailsColumn, addressColumn, balanceColumn, selectColumn;
|
||||
TableColumn<WithdrawalListItem, WithdrawalListItem> addressColumn, balanceColumn, selectColumn;
|
||||
|
||||
private final WalletService walletService;
|
||||
private final TradeManager tradeManager;
|
||||
@ -89,7 +86,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
private final WalletPasswordWindow walletPasswordWindow;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private final ObservableList<WithdrawalListItem> fundedAddresses = FXCollections.observableArrayList();
|
||||
private final ObservableList<WithdrawalListItem> observableList = FXCollections.observableArrayList();
|
||||
private final SortedList<WithdrawalListItem> sortedList = new SortedList<>(observableList);
|
||||
private Set<WithdrawalListItem> selectedItems = new HashSet<>();
|
||||
private BalanceListener balanceListener;
|
||||
private Set<String> fromAddresses;
|
||||
@ -121,15 +119,18 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
table.setPlaceholder(new Label("No funds for withdrawal are available"));
|
||||
setDateColumnCellFactory();
|
||||
setDetailsColumnCellFactory();
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No funds for withdrawal are available"));
|
||||
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||
|
||||
setAddressColumnCellFactory();
|
||||
setBalanceColumnCellFactory();
|
||||
setSelectColumnCellFactory();
|
||||
table.getSortOrder().add(dateColumn);
|
||||
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||
|
||||
addressColumn.setComparator((o1, o2) -> o1.getAddressString().compareTo(o2.getAddressString()));
|
||||
balanceColumn.setComparator((o1, o2) -> o1.getBalance().compareTo(o2.getBalance()));
|
||||
balanceColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(balanceColumn);
|
||||
|
||||
balanceListener = new BalanceListener() {
|
||||
@Override
|
||||
@ -141,6 +142,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
updateList();
|
||||
|
||||
reset();
|
||||
@ -152,7 +155,8 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
fundedAddresses.forEach(WithdrawalListItem::cleanup);
|
||||
sortedList.comparatorProperty().unbind();
|
||||
observableList.forEach(WithdrawalListItem::cleanup);
|
||||
withdrawButton.disableProperty().unbind();
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
@ -174,6 +178,14 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
} else {
|
||||
log.error("onWithdraw transaction is null");
|
||||
}
|
||||
|
||||
List<Trade> trades = new ArrayList<>(tradeManager.getTrades());
|
||||
trades.stream()
|
||||
.filter(trade -> trade.getState().getPhase() == Trade.Phase.PAYOUT_PAID)
|
||||
.forEach(trade -> {
|
||||
if (walletService.getBalanceForAddress(walletService.getTradeAddressEntry(trade.getId()).getAddress()).isZero())
|
||||
tradeManager.addTradeToClosedTrades(trade);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -283,32 +295,16 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
|
||||
private void updateList() {
|
||||
Set<String> reservedTrades = Stream.concat(openOfferManager.getOpenOffers().stream(), tradeManager.getTrades().stream())
|
||||
.filter(tradable -> !(tradable instanceof Trade) || ((Trade) tradable).getState().getPhase() != Trade.Phase.PAYOUT_PAID)
|
||||
.map(tradable -> tradable.getOffer().getId())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
fundedAddresses.forEach(WithdrawalListItem::cleanup);
|
||||
fundedAddresses.setAll(walletService.getAddressEntryList().stream()
|
||||
observableList.forEach(WithdrawalListItem::cleanup);
|
||||
observableList.setAll(walletService.getAddressEntryList().stream()
|
||||
.filter(e -> walletService.getBalanceForAddress(e.getAddress()).isPositive())
|
||||
.filter(e -> !reservedTrades.contains(e.getOfferId()))
|
||||
.map(addressEntry -> new WithdrawalListItem(addressEntry, walletService, formatter))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
fundedAddresses.sort((o1, o2) -> {
|
||||
Optional<Tradable> tradable1 = getTradable(o1);
|
||||
Optional<Tradable> tradable2 = getTradable(o2);
|
||||
// if we dont have a date we set it to now as it is likely a recent funding tx
|
||||
// TODO get tx date from wallet instead
|
||||
Date date1 = new Date();
|
||||
Date date2 = new Date();
|
||||
if (tradable1.isPresent())
|
||||
date1 = tradable1.get().getDate();
|
||||
|
||||
if (tradable2.isPresent())
|
||||
date2 = tradable2.get().getDate();
|
||||
|
||||
return date2.compareTo(date1);
|
||||
});
|
||||
table.setItems(fundedAddresses);
|
||||
}
|
||||
|
||||
private void doWithdraw(Coin amount, FutureCallback<Transaction> callback) {
|
||||
@ -335,7 +331,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
private void reset() {
|
||||
selectedItems = new HashSet<>();
|
||||
|
||||
table.getSelectionModel().clearSelection();
|
||||
tableView.getSelectionModel().clearSelection();
|
||||
|
||||
withdrawFromTextField.setText("");
|
||||
withdrawFromTextField.setPromptText("Select a source address from the table");
|
||||
@ -348,7 +344,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
withdrawToTextField.setPromptText("Fill in your destination address");
|
||||
|
||||
if (BitsquareApp.DEV_MODE)
|
||||
withdrawToTextField.setText("mi8k5f9L972VgDaT4LgjAhriC9hHEPL7EW");
|
||||
withdrawToTextField.setText("mo6y756TnpdZQCeHStraavjqrndeXzVkxi");
|
||||
}
|
||||
|
||||
private Optional<Tradable> getTradable(WithdrawalListItem item) {
|
||||
@ -374,92 +370,6 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
||||
// ColumnCellFactories
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void setDateColumnCellFactory() {
|
||||
dateColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
dateColumn.setCellFactory(new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>,
|
||||
TableCell<WithdrawalListItem, WithdrawalListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<WithdrawalListItem, WithdrawalListItem> call(TableColumn<WithdrawalListItem,
|
||||
WithdrawalListItem> column) {
|
||||
return new TableCell<WithdrawalListItem, WithdrawalListItem>() {
|
||||
|
||||
@Override
|
||||
public void updateItem(final WithdrawalListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty) {
|
||||
if (getTradable(item).isPresent())
|
||||
setText(formatter.formatDateTime(getTradable(item).get().getDate()));
|
||||
else
|
||||
setText("No date available");
|
||||
} else {
|
||||
setText("");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setDetailsColumnCellFactory() {
|
||||
detailsColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
detailsColumn.setCellFactory(new Callback<TableColumn<WithdrawalListItem, WithdrawalListItem>,
|
||||
TableCell<WithdrawalListItem, WithdrawalListItem>>() {
|
||||
|
||||
@Override
|
||||
public TableCell<WithdrawalListItem, WithdrawalListItem> call(TableColumn<WithdrawalListItem,
|
||||
WithdrawalListItem> column) {
|
||||
return new TableCell<WithdrawalListItem, WithdrawalListItem>() {
|
||||
|
||||
private HyperlinkWithIcon field;
|
||||
|
||||
@Override
|
||||
public void updateItem(final WithdrawalListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (item != null && !empty) {
|
||||
Optional<Tradable> tradableOptional = getTradable(item);
|
||||
if (tradableOptional.isPresent()) {
|
||||
AddressEntry addressEntry = item.getAddressEntry();
|
||||
String details;
|
||||
if (addressEntry.getContext() == AddressEntry.Context.TRADE) {
|
||||
String prefix;
|
||||
Tradable tradable = tradableOptional.get();
|
||||
if (tradable instanceof Trade)
|
||||
prefix = "Trade ID: ";
|
||||
else if (tradable instanceof OpenOffer)
|
||||
prefix = "Offer ID: ";
|
||||
else
|
||||
prefix = "";
|
||||
|
||||
details = prefix + addressEntry.getShortOfferId();
|
||||
} else if (addressEntry.getContext() == AddressEntry.Context.ARBITRATOR) {
|
||||
details = "Arbitration fee";
|
||||
} else {
|
||||
details = "-";
|
||||
}
|
||||
|
||||
field = new HyperlinkWithIcon(details, AwesomeIcon.INFO_SIGN);
|
||||
field.setOnAction(event -> openDetailPopup(item));
|
||||
field.setTooltip(new Tooltip("Open popup for details"));
|
||||
setGraphic(field);
|
||||
} else if (item.getAddressEntry().getContext() == AddressEntry.Context.ARBITRATOR) {
|
||||
setGraphic(new Label("Arbitrators fee"));
|
||||
} else {
|
||||
setGraphic(new Label("No details available"));
|
||||
}
|
||||
|
||||
} else {
|
||||
setGraphic(null);
|
||||
if (field != null)
|
||||
field.setOnAction(null);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setAddressColumnCellFactory() {
|
||||
addressColumn.setCellValueFactory((addressListItem) -> new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
|
||||
addressColumn.setCellFactory(
|
||||
|
@ -23,6 +23,7 @@ import io.bitsquare.gui.components.TableGroupHeadline;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.locale.CurrencyUtil;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TableCell;
|
||||
@ -38,7 +39,8 @@ import javax.inject.Inject;
|
||||
public class MarketsStatisticsView extends ActivatableViewAndModel<GridPane, MarketsStatisticViewModel> {
|
||||
private final BSFormatter formatter;
|
||||
private final int gridRow = 0;
|
||||
private TableView<MarketStatisticItem> statisticsTableView;
|
||||
private TableView<MarketStatisticItem> tableView;
|
||||
private SortedList<MarketStatisticItem> sortedList;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -58,29 +60,44 @@ public class MarketsStatisticsView extends ActivatableViewAndModel<GridPane, Mar
|
||||
GridPane.setMargin(header, new Insets(0, -10, -10, -10));
|
||||
root.getChildren().add(header);
|
||||
|
||||
statisticsTableView = new TableView<>();
|
||||
GridPane.setRowIndex(statisticsTableView, gridRow);
|
||||
GridPane.setMargin(statisticsTableView, new Insets(20, -10, -10, -10));
|
||||
GridPane.setVgrow(statisticsTableView, Priority.ALWAYS);
|
||||
GridPane.setHgrow(statisticsTableView, Priority.ALWAYS);
|
||||
root.getChildren().add(statisticsTableView);
|
||||
statisticsTableView.getColumns().add(getCurrencyColumn());
|
||||
statisticsTableView.getColumns().add(getNumberOfOffersColumn());
|
||||
statisticsTableView.getColumns().add(getTotalAmountColumn());
|
||||
statisticsTableView.getColumns().add(getSpreadColumn());
|
||||
statisticsTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView = new TableView<>();
|
||||
GridPane.setRowIndex(tableView, gridRow);
|
||||
GridPane.setMargin(tableView, new Insets(20, -10, -10, -10));
|
||||
GridPane.setVgrow(tableView, Priority.ALWAYS);
|
||||
GridPane.setHgrow(tableView, Priority.ALWAYS);
|
||||
root.getChildren().add(tableView);
|
||||
Label placeholder = new Label("Currently there is no data available");
|
||||
placeholder.setWrapText(true);
|
||||
statisticsTableView.setPlaceholder(placeholder);
|
||||
tableView.setPlaceholder(placeholder);
|
||||
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> currencyColumn = getCurrencyColumn();
|
||||
tableView.getColumns().add(currencyColumn);
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> numberOfOffersColumn = getNumberOfOffersColumn();
|
||||
tableView.getColumns().add(numberOfOffersColumn);
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> totalAmountColumn = getTotalAmountColumn();
|
||||
tableView.getColumns().add(totalAmountColumn);
|
||||
TableColumn<MarketStatisticItem, MarketStatisticItem> spreadColumn = getSpreadColumn();
|
||||
tableView.getColumns().add(spreadColumn);
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
|
||||
currencyColumn.setComparator((o1, o2) -> o1.currencyCode.compareTo(o2.currencyCode));
|
||||
numberOfOffersColumn.setComparator((o1, o2) -> Integer.valueOf(o1.numberOfOffers).compareTo(o2.numberOfOffers));
|
||||
totalAmountColumn.setComparator((o1, o2) -> o1.totalAmount.compareTo(o2.totalAmount));
|
||||
spreadColumn.setComparator((o1, o2) -> o1.spread != null && o2.spread != null ? o1.spread.compareTo(o2.spread) : 0);
|
||||
|
||||
tableView.getSortOrder().add(numberOfOffersColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
statisticsTableView.setItems(model.marketStatisticItems);
|
||||
sortedList = new SortedList<>(model.marketStatisticItems);
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
sortedList.comparatorProperty().unbind();
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,6 +28,7 @@ import io.bitsquare.btc.listeners.BalanceListener;
|
||||
import io.bitsquare.btc.pricefeed.PriceFeed;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.common.crypto.KeyRing;
|
||||
import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.model.ActivatableDataModel;
|
||||
import io.bitsquare.gui.main.overlays.notifications.Notification;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
@ -64,13 +65,14 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
*/
|
||||
class CreateOfferDataModel extends ActivatableDataModel {
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final WalletService walletService;
|
||||
final WalletService walletService;
|
||||
private final TradeWalletService tradeWalletService;
|
||||
private final Preferences preferences;
|
||||
private final User user;
|
||||
private final KeyRing keyRing;
|
||||
private final P2PService p2PService;
|
||||
private final PriceFeed priceFeed;
|
||||
private Navigation navigation;
|
||||
private final WalletPasswordWindow walletPasswordWindow;
|
||||
private final BlockchainService blockchainService;
|
||||
private final BSFormatter formatter;
|
||||
@ -98,12 +100,16 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Coin> missingCoin = new SimpleObjectProperty<>(Coin.ZERO);
|
||||
final ObjectProperty<Coin> balance = new SimpleObjectProperty<>();
|
||||
|
||||
final ObservableList<PaymentAccount> paymentAccounts = FXCollections.observableArrayList();
|
||||
|
||||
PaymentAccount paymentAccount;
|
||||
private boolean isTabSelected;
|
||||
private Notification walletFundedNotification;
|
||||
boolean useSavingsWallet;
|
||||
Coin totalAvailableBalance;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -113,6 +119,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
@Inject
|
||||
CreateOfferDataModel(OpenOfferManager openOfferManager, WalletService walletService, TradeWalletService tradeWalletService,
|
||||
Preferences preferences, User user, KeyRing keyRing, P2PService p2PService, PriceFeed priceFeed,
|
||||
Navigation navigation,
|
||||
WalletPasswordWindow walletPasswordWindow, BlockchainService blockchainService, BSFormatter formatter) {
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.walletService = walletService;
|
||||
@ -122,6 +129,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
this.keyRing = keyRing;
|
||||
this.p2PService = p2PService;
|
||||
this.priceFeed = priceFeed;
|
||||
this.navigation = navigation;
|
||||
this.walletPasswordWindow = walletPasswordWindow;
|
||||
this.blockchainService = blockchainService;
|
||||
this.formatter = formatter;
|
||||
@ -135,7 +143,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
balanceListener = new BalanceListener(getAddressEntry().getAddress()) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
updateBalance();
|
||||
|
||||
if (preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET) {
|
||||
SettableFuture<Coin> future = blockchainService.requestFee(tx.getHashAsString());
|
||||
@ -172,7 +180,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
addListeners();
|
||||
|
||||
paymentAccounts.setAll(user.getPaymentAccounts());
|
||||
updateBalance(walletService.getBalanceForAddress(getAddressEntry().getAddress()));
|
||||
calculateTotalToPay();
|
||||
updateBalance();
|
||||
|
||||
if (direction == Offer.Direction.BUY)
|
||||
calculateTotalToPay();
|
||||
@ -290,7 +299,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
}
|
||||
|
||||
private void doPlaceOffer(Offer offer, TransactionResultHandler resultHandler) {
|
||||
openOfferManager.placeOffer(offer, resultHandler);
|
||||
openOfferManager.placeOffer(offer, totalToPayAsCoin.get().subtract(offerFeeAsCoin), useSavingsWallet, resultHandler);
|
||||
}
|
||||
|
||||
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
||||
@ -311,6 +320,11 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
}
|
||||
}
|
||||
|
||||
void useSavingsWalletForFunding() {
|
||||
useSavingsWallet = true;
|
||||
updateBalance();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
@ -380,16 +394,30 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
|
||||
void calculateTotalToPay() {
|
||||
if (securityDepositAsCoin != null) {
|
||||
if (direction == Offer.Direction.BUY)
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin));
|
||||
else
|
||||
totalToPayAsCoin.set(offerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin).add(amountAsCoin.get() == null ? Coin.ZERO : amountAsCoin.get()));
|
||||
Coin feeAndSecDeposit = offerFeeAsCoin.add(networkFeeAsCoin).add(securityDepositAsCoin);
|
||||
Coin feeAndSecDepositAndAmount = feeAndSecDeposit.add(amountAsCoin.get() == null ? Coin.ZERO : amountAsCoin.get());
|
||||
Coin required = direction == Offer.Direction.BUY ? feeAndSecDeposit : feeAndSecDepositAndAmount;
|
||||
totalToPayAsCoin.set(required);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBalance(Coin balance) {
|
||||
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
||||
void updateBalance() {
|
||||
Coin tradeWalletBalance = walletService.getBalanceForAddress(getAddressEntry().getAddress());
|
||||
if (useSavingsWallet) {
|
||||
Coin savingWalletBalance = walletService.getSavingWalletBalance();
|
||||
totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance);
|
||||
|
||||
if (totalAvailableBalance.compareTo(totalToPayAsCoin.get()) > 0)
|
||||
balance.set(totalToPayAsCoin.get());
|
||||
else
|
||||
balance.set(totalAvailableBalance);
|
||||
} else {
|
||||
balance.set(tradeWalletBalance);
|
||||
}
|
||||
|
||||
missingCoin.set(totalToPayAsCoin.get().subtract(balance.get()));
|
||||
|
||||
isWalletFunded.set(isBalanceSufficient(balance.get()));
|
||||
if (isWalletFunded.get()) {
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
if (walletFundedNotification == null) {
|
||||
@ -404,6 +432,10 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBalanceSufficient(Coin balance) {
|
||||
return totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0;
|
||||
}
|
||||
|
||||
public Coin getOfferFeeAsCoin() {
|
||||
return offerFeeAsCoin;
|
||||
}
|
||||
@ -423,4 +455,8 @@ class CreateOfferDataModel extends ActivatableDataModel {
|
||||
public Preferences getPreferences() {
|
||||
return preferences;
|
||||
}
|
||||
|
||||
public void swapTradeToSavings() {
|
||||
walletService.swapTradeToSavings(getOfferId());
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.bitsquare.gui.util.FormBuilder.*;
|
||||
@ -85,13 +86,12 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
private ImageView imageView;
|
||||
private AddressTextField addressTextField;
|
||||
private BalanceTextField balanceTextField;
|
||||
private ProgressIndicator spinner;
|
||||
private TitledGroupBg payFundsPane;
|
||||
private Button nextButton, cancelButton1, cancelButton2, placeOfferButton;
|
||||
private Button nextButton, cancelButton1, cancelButton2, fundFromSavingsWalletButton, fundFromExternalWalletButton, placeOfferButton;
|
||||
private InputTextField amountTextField, minAmountTextField, priceTextField, volumeTextField;
|
||||
private TextField currencyTextField;
|
||||
private Label directionLabel, amountDescriptionLabel, addressLabel, balanceLabel, totalToPayLabel, totalToPayInfoIconLabel, amountBtcLabel, priceCurrencyLabel,
|
||||
volumeCurrencyLabel, minAmountBtcLabel, priceDescriptionLabel, volumeDescriptionLabel, spinnerInfoLabel, currencyTextFieldLabel,
|
||||
volumeCurrencyLabel, minAmountBtcLabel, priceDescriptionLabel, volumeDescriptionLabel, currencyTextFieldLabel,
|
||||
currencyComboBoxLabel;
|
||||
private TextFieldWithCopyIcon totalToPayTextField;
|
||||
private ComboBox<PaymentAccount> paymentAccountsComboBox;
|
||||
@ -108,7 +108,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
private ChangeListener<Boolean> showWarningInvalidFiatDecimalPlacesPlacesListener;
|
||||
private ChangeListener<Boolean> showWarningAdjustedVolumeListener;
|
||||
private ChangeListener<String> errorMessageListener;
|
||||
private ChangeListener<Boolean> isSpinnerVisibleListener;
|
||||
private ChangeListener<Boolean> placeOfferCompletedListener;
|
||||
private ChangeListener<Coin> feeFromFundingTxListener;
|
||||
private EventHandler<ActionEvent> paymentAccountsComboBoxSelectionHandler;
|
||||
@ -118,6 +117,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
private final Preferences preferences;
|
||||
private ChangeListener<String> tradeCurrencyCodeListener;
|
||||
private ImageView qrCodeImageView;
|
||||
private ChangeListener<Coin> balanceListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -143,7 +143,9 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
|
||||
createListeners();
|
||||
|
||||
balanceTextField.setup(model.address.get(), model.getFormatter());
|
||||
balanceTextField.setFormatter(model.getFormatter());
|
||||
balanceListener = (observable, oldValue, newValue) -> balanceTextField.setBalance(newValue);
|
||||
|
||||
paymentAccountsComboBox.setConverter(new StringConverter<PaymentAccount>() {
|
||||
@Override
|
||||
public String toString(PaymentAccount paymentAccount) {
|
||||
@ -173,8 +175,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
|
||||
onPaymentAccountsComboBoxSelected();
|
||||
|
||||
if (spinner != null && placeOfferButton.isVisible())
|
||||
spinner.setProgress(-1);
|
||||
balanceTextField.setBalance(model.dataModel.balance.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -183,9 +184,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
removeListeners();
|
||||
if (balanceTextField != null)
|
||||
balanceTextField.cleanup();
|
||||
|
||||
if (spinner != null)
|
||||
spinner.setProgress(0);
|
||||
}
|
||||
|
||||
|
||||
@ -216,9 +214,14 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
// called form parent as the view does not get notified when the tab is closed
|
||||
public void onClose() {
|
||||
// we use model.placeOfferCompleted to not react on close which was triggered by a successful placeOffer
|
||||
if (model.dataModel.isWalletFunded.get() && !model.placeOfferCompleted.get())
|
||||
if (model.dataModel.balance.get().isPositive() && !model.placeOfferCompleted.get()) {
|
||||
model.dataModel.swapTradeToSavings();
|
||||
new Popup().information("You have already funds paid in.\n" +
|
||||
"In the \"Funds/Available for withdrawal\" section you can withdraw those funds.").show();
|
||||
"In the \"Funds/Available for withdrawal\" section you can withdraw those funds.")
|
||||
.actionButtonText("Go to \"Funds/Available for withdrawal\"")
|
||||
.onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class))
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
public void setCloseHandler(OfferView.CloseHandler closeHandler) {
|
||||
@ -264,8 +267,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
currencyComboBox.setMouseTransparent(true);
|
||||
paymentAccountsComboBox.setMouseTransparent(true);
|
||||
|
||||
spinner.setProgress(-1);
|
||||
|
||||
if (!BitsquareApp.DEV_MODE) {
|
||||
String key = "securityDepositInfo";
|
||||
new Popup().backgroundInfo("To ensure that both traders follow the trade protocol they need to pay a security deposit.\n\n" +
|
||||
@ -310,6 +311,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
qrCodeImageView.setVisible(true);
|
||||
balanceLabel.setVisible(true);
|
||||
balanceTextField.setVisible(true);
|
||||
fundFromSavingsWalletButton.setVisible(true);
|
||||
fundFromExternalWalletButton.setVisible(true);
|
||||
placeOfferButton.setVisible(true);
|
||||
cancelButton2.setVisible(true);
|
||||
//root.requestFocus();
|
||||
@ -392,7 +395,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
volumeTextField.promptTextProperty().bind(model.volumePromptLabel);
|
||||
|
||||
totalToPayTextField.textProperty().bind(model.totalToPay);
|
||||
addressTextField.amountAsCoinProperty().bind(model.totalToPayAsCoin);
|
||||
addressTextField.amountAsCoinProperty().bind(model.dataModel.missingCoin);
|
||||
|
||||
// Validation
|
||||
amountTextField.validationResultProperty().bind(model.amountValidationResult);
|
||||
@ -403,10 +406,8 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
// buttons
|
||||
placeOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled);
|
||||
cancelButton2.disableProperty().bind(model.cancelButtonDisabled);
|
||||
|
||||
spinner.visibleProperty().bind(model.isSpinnerVisible);
|
||||
spinnerInfoLabel.visibleProperty().bind(model.isSpinnerVisible);
|
||||
spinnerInfoLabel.textProperty().bind(model.spinnerInfoText);
|
||||
fundFromSavingsWalletButton.disableProperty().bind(model.dataModel.isWalletFunded);
|
||||
fundFromExternalWalletButton.disableProperty().bind(model.dataModel.isWalletFunded);
|
||||
|
||||
// payment account
|
||||
currencyComboBox.prefWidthProperty().bind(paymentAccountsComboBox.widthProperty());
|
||||
@ -438,9 +439,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
volumeTextField.validationResultProperty().unbind();
|
||||
placeOfferButton.disableProperty().unbind();
|
||||
cancelButton2.disableProperty().unbind();
|
||||
spinner.visibleProperty().unbind();
|
||||
spinnerInfoLabel.visibleProperty().unbind();
|
||||
spinnerInfoLabel.textProperty().unbind();
|
||||
currencyComboBox.managedProperty().unbind();
|
||||
currencyComboBoxLabel.visibleProperty().unbind();
|
||||
currencyComboBoxLabel.managedProperty().unbind();
|
||||
@ -496,7 +494,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
"Please try to restart you application and check your network connection to see if you can resolve the issue.")
|
||||
.show(), 100, TimeUnit.MILLISECONDS);
|
||||
};
|
||||
isSpinnerVisibleListener = (ov, oldValue, newValue) -> spinner.setProgress(newValue ? -1 : 0);
|
||||
|
||||
feeFromFundingTxListener = (observable, oldValue, newValue) -> {
|
||||
log.debug("feeFromFundingTxListener " + newValue);
|
||||
@ -514,6 +511,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
.closeButtonText("Close")
|
||||
.onClose(() -> {
|
||||
close();
|
||||
model.dataModel.swapTradeToSavings();
|
||||
navigation.navigateTo(MainView.class, FundsView.class, WithdrawalView.class);
|
||||
})
|
||||
.show();
|
||||
@ -558,6 +556,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
|
||||
private void addListeners() {
|
||||
model.tradeCurrencyCode.addListener(tradeCurrencyCodeListener);
|
||||
model.dataModel.balance.addListener(balanceListener);
|
||||
|
||||
// focus out
|
||||
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
||||
@ -570,7 +569,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
model.showWarningInvalidFiatDecimalPlaces.addListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
|
||||
model.showWarningAdjustedVolume.addListener(showWarningAdjustedVolumeListener);
|
||||
model.errorMessage.addListener(errorMessageListener);
|
||||
model.isSpinnerVisible.addListener(isSpinnerVisibleListener);
|
||||
model.dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
|
||||
|
||||
model.placeOfferCompleted.addListener(placeOfferCompletedListener);
|
||||
@ -582,6 +580,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
|
||||
private void removeListeners() {
|
||||
model.tradeCurrencyCode.removeListener(tradeCurrencyCodeListener);
|
||||
model.dataModel.balance.removeListener(balanceListener);
|
||||
|
||||
// focus out
|
||||
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
||||
@ -594,7 +593,6 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
model.showWarningInvalidFiatDecimalPlaces.removeListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
|
||||
model.showWarningAdjustedVolume.removeListener(showWarningAdjustedVolumeListener);
|
||||
model.errorMessage.removeListener(errorMessageListener);
|
||||
model.isSpinnerVisible.removeListener(isSpinnerVisibleListener);
|
||||
model.dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
|
||||
|
||||
model.placeOfferCompleted.removeListener(placeOfferCompletedListener);
|
||||
@ -698,7 +696,10 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
//UserThread.runAfter(() -> nextButton.requestFocus(), 100, TimeUnit.MILLISECONDS);
|
||||
cancelButton1 = tuple.second;
|
||||
cancelButton1.setDefaultButton(false);
|
||||
cancelButton1.setOnAction(e -> close());
|
||||
cancelButton1.setOnAction(e -> {
|
||||
close();
|
||||
model.dataModel.swapTradeToSavings();
|
||||
});
|
||||
cancelButton1.setId("cancel-button");
|
||||
|
||||
GridPane.setMargin(nextButton, new Insets(-35, 0, 0, 0));
|
||||
@ -757,27 +758,47 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
balanceTextField = balanceTuple.second;
|
||||
balanceTextField.setVisible(false);
|
||||
|
||||
Tuple3<Button, ProgressIndicator, Label> placeOfferTuple = addButtonWithStatusAfterGroup(gridPane, ++gridRow, "");
|
||||
placeOfferButton = placeOfferTuple.first;
|
||||
Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane, ++gridRow, "Transfer from Bitsquare wallet", "Fund from external wallet");
|
||||
fundFromSavingsWalletButton = tuple.first;
|
||||
fundFromSavingsWalletButton.setVisible(false);
|
||||
fundFromSavingsWalletButton.setDefaultButton(false);
|
||||
fundFromSavingsWalletButton.setOnAction(e -> model.useSavingsWalletForFunding());
|
||||
|
||||
fundFromExternalWalletButton = tuple.second;
|
||||
fundFromExternalWalletButton.setVisible(false);
|
||||
fundFromExternalWalletButton.setDefaultButton(false);
|
||||
fundFromExternalWalletButton.setOnAction(e -> {
|
||||
try {
|
||||
Utilities.openURI(URI.create(getBitcoinURI()));
|
||||
} catch (Exception ex) {
|
||||
log.warn(ex.getMessage());
|
||||
new Popup().warning("Opening a default bitcoin wallet application has failed. " +
|
||||
"Perhaps you don't have one installed?").show();
|
||||
}
|
||||
});
|
||||
|
||||
placeOfferButton = addButton(gridPane, ++gridRow, "");
|
||||
placeOfferButton.setVisible(false);
|
||||
placeOfferButton.setOnAction(e -> onPlaceOffer());
|
||||
placeOfferButton.setMinHeight(40);
|
||||
placeOfferButton.setPadding(new Insets(0, 20, 0, 20));
|
||||
|
||||
spinner = placeOfferTuple.second;
|
||||
spinnerInfoLabel = placeOfferTuple.third;
|
||||
|
||||
cancelButton2 = addButton(gridPane, ++gridRow, BSResources.get("shared.cancel"));
|
||||
cancelButton2.setOnAction(e -> {
|
||||
if (model.dataModel.isWalletFunded.get())
|
||||
if (model.dataModel.isWalletFunded.get()) {
|
||||
new Popup().warning("You have already paid in the funds.\n" +
|
||||
"Are you sure you want to cancel.")
|
||||
.actionButtonText("No")
|
||||
.closeButtonText("Yes, close")
|
||||
.onClose(() -> close())
|
||||
.onClose(() -> {
|
||||
close();
|
||||
model.dataModel.swapTradeToSavings();
|
||||
})
|
||||
.show();
|
||||
else
|
||||
} else {
|
||||
close();
|
||||
model.dataModel.swapTradeToSavings();
|
||||
}
|
||||
});
|
||||
cancelButton2.setDefaultButton(false);
|
||||
cancelButton2.setVisible(false);
|
||||
@ -786,7 +807,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
||||
|
||||
@NotNull
|
||||
private String getBitcoinURI() {
|
||||
return model.getAddressAsString() != null ? BitcoinURI.convertToBitcoinURI(model.getAddressAsString(), model.totalToPayAsCoin.get(),
|
||||
return model.getAddressAsString() != null ? BitcoinURI.convertToBitcoinURI(model.getAddressAsString(), model.dataModel.missingCoin.get(),
|
||||
model.getPaymentLabel(), null) : "";
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@
|
||||
package io.bitsquare.gui.main.offer.createoffer;
|
||||
|
||||
import io.bitsquare.app.BitsquareApp;
|
||||
import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.pricefeed.MarketPrice;
|
||||
import io.bitsquare.btc.pricefeed.PriceFeed;
|
||||
import io.bitsquare.common.Timer;
|
||||
@ -27,6 +26,8 @@ import io.bitsquare.gui.Navigation;
|
||||
import io.bitsquare.gui.common.model.ActivatableWithDataModel;
|
||||
import io.bitsquare.gui.common.model.ViewModel;
|
||||
import io.bitsquare.gui.main.MainView;
|
||||
import io.bitsquare.gui.main.funds.FundsView;
|
||||
import io.bitsquare.gui.main.funds.deposit.DepositView;
|
||||
import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.main.settings.SettingsView;
|
||||
import io.bitsquare.gui.main.settings.preferences.PreferencesView;
|
||||
@ -77,12 +78,10 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
final StringProperty errorMessage = new SimpleStringProperty();
|
||||
final StringProperty btcCode = new SimpleStringProperty();
|
||||
final StringProperty tradeCurrencyCode = new SimpleStringProperty();
|
||||
final StringProperty spinnerInfoText = new SimpleStringProperty("");
|
||||
|
||||
final BooleanProperty isPlaceOfferButtonDisabled = new SimpleBooleanProperty(true);
|
||||
final BooleanProperty cancelButtonDisabled = new SimpleBooleanProperty();
|
||||
final BooleanProperty isNextButtonDisabled = new SimpleBooleanProperty(true);
|
||||
final BooleanProperty isSpinnerVisible = new SimpleBooleanProperty();
|
||||
final BooleanProperty showWarningAdjustedVolume = new SimpleBooleanProperty();
|
||||
final BooleanProperty showWarningInvalidFiatDecimalPlaces = new SimpleBooleanProperty();
|
||||
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
|
||||
@ -95,7 +94,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
final ObjectProperty<InputValidator.ValidationResult> volumeValidationResult = new SimpleObjectProperty<>();
|
||||
|
||||
// Those are needed for the addressTextField
|
||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||
|
||||
private ChangeListener<String> amountListener;
|
||||
@ -108,7 +106,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
private ChangeListener<Fiat> volumeAsFiatListener;
|
||||
private ChangeListener<Boolean> isWalletFundedListener;
|
||||
private ChangeListener<Coin> feeFromFundingTxListener;
|
||||
private ChangeListener<String> requestPlaceOfferErrorMessageListener;
|
||||
private ChangeListener<String> errorMessageListener;
|
||||
private Offer offer;
|
||||
private Timer timeoutTimer;
|
||||
@ -170,8 +167,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
directionLabel = BSResources.get("shared.sellBitcoin");
|
||||
amountDescription = BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.sell"));
|
||||
}
|
||||
|
||||
updateSpinnerInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -181,16 +176,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
stopTimeoutTimer();
|
||||
}
|
||||
|
||||
private void updateSpinnerInfo() {
|
||||
if (dataModel.isWalletFunded.get() || !showPayFundsScreenDisplayed) {
|
||||
isSpinnerVisible.set(false);
|
||||
spinnerInfoText.set("");
|
||||
} else if (showPayFundsScreenDisplayed) {
|
||||
spinnerInfoText.set("Waiting for receiving funds...");
|
||||
isSpinnerVisible.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void addBindings() {
|
||||
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
||||
volumeDescriptionLabel.bind(createStringBinding(
|
||||
@ -212,7 +197,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
tradeAmount.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.amountAsCoin.get()),
|
||||
dataModel.amountAsCoin));
|
||||
|
||||
totalToPayAsCoin.bind(dataModel.totalToPayAsCoin);
|
||||
|
||||
btcCode.bind(dataModel.btcCode);
|
||||
tradeCurrencyCode.bind(dataModel.tradeCurrencyCode);
|
||||
@ -221,7 +205,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
private void removeBindings() {
|
||||
totalToPay.unbind();
|
||||
tradeAmount.unbind();
|
||||
totalToPayAsCoin.unbind();
|
||||
btcCode.unbind();
|
||||
tradeCurrencyCode.unbind();
|
||||
volumeDescriptionLabel.unbind();
|
||||
@ -265,20 +248,9 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
|
||||
isWalletFundedListener = (ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
spinnerInfoText.set("Checking funding tx miner fee...");
|
||||
};
|
||||
feeFromFundingTxListener = (ov, oldValue, newValue) -> {
|
||||
updateButtonDisableState();
|
||||
if (newValue.compareTo(FeePolicy.getMinRequiredFeeForFundingTx()) >= 0) {
|
||||
isSpinnerVisible.set(false);
|
||||
spinnerInfoText.set("");
|
||||
}
|
||||
};
|
||||
requestPlaceOfferErrorMessageListener = (ov, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
isSpinnerVisible.set(false);
|
||||
spinnerInfoText.set("");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -298,7 +270,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
|
||||
dataModel.feeFromFundingTxProperty.addListener(feeFromFundingTxListener);
|
||||
dataModel.isWalletFunded.addListener(isWalletFundedListener);
|
||||
errorMessage.addListener(requestPlaceOfferErrorMessageListener);
|
||||
}
|
||||
|
||||
private void removeListeners() {
|
||||
@ -315,7 +286,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
|
||||
dataModel.feeFromFundingTxProperty.removeListener(feeFromFundingTxListener);
|
||||
dataModel.isWalletFunded.removeListener(isWalletFundedListener);
|
||||
errorMessage.removeListener(requestPlaceOfferErrorMessageListener);
|
||||
|
||||
if (offer != null && errorMessageListener != null)
|
||||
offer.errorMessageProperty().removeListener(errorMessageListener);
|
||||
@ -420,9 +390,27 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
}
|
||||
}
|
||||
|
||||
public void onShowPayFundsScreen() {
|
||||
void onShowPayFundsScreen() {
|
||||
showPayFundsScreenDisplayed = true;
|
||||
updateSpinnerInfo();
|
||||
}
|
||||
|
||||
boolean useSavingsWalletForFunding() {
|
||||
dataModel.useSavingsWalletForFunding();
|
||||
if (dataModel.isWalletFunded.get()) {
|
||||
updateButtonDisableState();
|
||||
return true;
|
||||
} else {
|
||||
new Popup().warning("You don't have enough funds in your Bitsquare wallet.\n" +
|
||||
"You need " + formatter.formatCoinWithCode(dataModel.totalToPayAsCoin.get()) + " but you have only " +
|
||||
formatter.formatCoinWithCode(dataModel.totalAvailableBalance) + " in your Bitsquare wallet.\n\n" +
|
||||
"Please fund that trade from an external Bitcoin wallet or fund your Bitsquare " +
|
||||
"wallet at \"Funds/Depost funds\".")
|
||||
.actionButtonText("Go to \"Funds/Depost funds\"")
|
||||
.onAction(() -> navigation.navigateTo(MainView.class, FundsView.class, DepositView.class))
|
||||
.show();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -648,7 +636,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
||||
isNextButtonDisabled.set(!inputDataValid);
|
||||
isPlaceOfferButtonDisabled.set(!(inputDataValid &&
|
||||
dataModel.isWalletFunded.get() &&
|
||||
dataModel.isFeeFromFundingTxSufficient())
|
||||
(dataModel.useSavingsWallet || dataModel.isFeeFromFundingTxSufficient()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
private ComboBox<TradeCurrency> currencyComboBox;
|
||||
private ComboBox<PaymentMethod> paymentMethodComboBox;
|
||||
private Button createOfferButton;
|
||||
private TableColumn<OfferBookListItem, OfferBookListItem> amountColumn, volumeColumn, priceColumn, paymentMethodColumn;
|
||||
private TableColumn<OfferBookListItem, OfferBookListItem> amountColumn, volumeColumn, priceColumn, paymentMethodColumn, avatarColumn;
|
||||
private TableView<OfferBookListItem> tableView;
|
||||
|
||||
private OfferView.OfferActionHandler offerActionHandler;
|
||||
@ -155,7 +155,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
paymentMethodColumn = getPaymentMethodColumn();
|
||||
tableView.getColumns().add(paymentMethodColumn);
|
||||
tableView.getColumns().add(getActionColumn());
|
||||
tableView.getColumns().add(getAvatarColumn());
|
||||
avatarColumn = getAvatarColumn();
|
||||
tableView.getColumns().add(avatarColumn);
|
||||
|
||||
tableView.getSortOrder().add(priceColumn);
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
@ -167,6 +168,7 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
|
||||
volumeColumn.setComparator((o1, o2) -> o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
|
||||
paymentMethodColumn.setComparator((o1, o2) -> o1.getOffer().getPaymentMethod().compareTo(o2.getOffer().getPaymentMethod()));
|
||||
avatarColumn.setComparator((o1, o2) -> o1.getOffer().getOwnerNodeAddress().hostName.compareTo(o2.getOffer().getOwnerNodeAddress().hostName));
|
||||
|
||||
createOfferButton = addButton(root, ++gridRow, "");
|
||||
createOfferButton.setMinHeight(40);
|
||||
@ -218,7 +220,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
|
||||
tableView.setItems(model.getOfferList());
|
||||
priceColumn.setSortType((model.getDirection() == Offer.Direction.BUY) ? TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
|
||||
tableView.sort();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -572,7 +573,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
||||
if (button != null)
|
||||
button.setOnAction(null);
|
||||
TableRow tableRow = getTableRow();
|
||||
if (tableRow != null) tableRow.setOpacity(1);
|
||||
if (tableRow != null) {
|
||||
tableRow.setOpacity(1);
|
||||
tableRow.setOnMouseClicked(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -232,6 +232,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
|
||||
|
||||
private void doTakeOffer(TradeResultHandler tradeResultHandler) {
|
||||
tradeManager.onTakeOffer(amountAsCoin.get(),
|
||||
totalToPayAsCoin.get().subtract(takerFeeAsCoin),
|
||||
offer,
|
||||
paymentAccount.getId(),
|
||||
tradeResultHandler
|
||||
|
@ -353,7 +353,8 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
||||
takeOfferButton.setText("Review take offer for selling bitcoin");
|
||||
}
|
||||
|
||||
balanceTextField.setup(model.address.get(), model.getFormatter());
|
||||
balanceTextField.setFormatter(model.getFormatter());
|
||||
balanceTextField.setupBalanceListener(model.address.get());
|
||||
|
||||
boolean showComboBox = model.getPossiblePaymentAccounts().size() > 1;
|
||||
paymentAccountsLabel.setVisible(showComboBox);
|
||||
|
@ -625,7 +625,6 @@ public abstract class Overlay<T extends Overlay> {
|
||||
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));
|
||||
|
@ -2,9 +2,12 @@ package io.bitsquare.gui.main.overlays.windows;
|
||||
|
||||
import io.bitsquare.gui.main.overlays.Overlay;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.Priority;
|
||||
import net.glxn.qrgen.QRCode;
|
||||
import net.glxn.qrgen.image.ImageType;
|
||||
import org.slf4j.Logger;
|
||||
@ -15,9 +18,10 @@ import java.io.ByteArrayInputStream;
|
||||
public class QRCodeWindow extends Overlay<QRCodeWindow> {
|
||||
private static final Logger log = LoggerFactory.getLogger(QRCodeWindow.class);
|
||||
private final ImageView qrCodeImageView;
|
||||
private String bitcoinURI;
|
||||
|
||||
public QRCodeWindow(String bitcoinURI) {
|
||||
|
||||
this.bitcoinURI = bitcoinURI;
|
||||
final byte[] imageBytes = QRCode
|
||||
.from(bitcoinURI)
|
||||
.withSize(250, 250)
|
||||
@ -45,6 +49,18 @@ public class QRCodeWindow extends Overlay<QRCodeWindow> {
|
||||
GridPane.setHalignment(qrCodeImageView, HPos.CENTER);
|
||||
gridPane.getChildren().add(qrCodeImageView);
|
||||
|
||||
Label infoLabel = new Label("Payment request:\n" + bitcoinURI);
|
||||
infoLabel.setMouseTransparent(true);
|
||||
infoLabel.setWrapText(true);
|
||||
infoLabel.setId("popup-qr-code-info");
|
||||
GridPane.setHalignment(infoLabel, HPos.CENTER);
|
||||
GridPane.setHgrow(infoLabel, Priority.ALWAYS);
|
||||
GridPane.setMargin(infoLabel, new Insets(3, 0, 0, 0));
|
||||
GridPane.setRowIndex(infoLabel, ++rowIndex);
|
||||
GridPane.setColumnIndex(infoLabel, 0);
|
||||
GridPane.setColumnSpan(infoLabel, 2);
|
||||
gridPane.getChildren().add(infoLabel);
|
||||
|
||||
addCloseButton();
|
||||
applyStyles();
|
||||
display();
|
||||
|
@ -26,15 +26,15 @@
|
||||
<Insets bottom="0.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Trade ID" fx:id="tradeIdColumn" minWidth="120" maxWidth="120" sortable="false"/>
|
||||
<TableColumn text="Trade ID" fx:id="tradeIdColumn" minWidth="120" maxWidth="120"/>
|
||||
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="150"/>
|
||||
<TableColumn text="Trade amount in BTC" fx:id="amountColumn" minWidth="130"/>
|
||||
<TableColumn text="Price" fx:id="priceColumn" minWidth="100"/>
|
||||
<TableColumn text="Trade amount" fx:id="volumeColumn" minWidth="130"/>
|
||||
<TableColumn text="Trade type" fx:id="directionColumn" minWidth="80"/>
|
||||
<TableColumn text="State" fx:id="stateColumn" minWidth="80" sortable="false"/>
|
||||
<TableColumn text="State" fx:id="stateColumn" minWidth="80"/>
|
||||
<TableColumn text="" fx:id="avatarColumn" minWidth="32" maxWidth="32"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
@ -24,15 +24,19 @@ import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow;
|
||||
import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import io.bitsquare.p2p.NodeAddress;
|
||||
import io.bitsquare.trade.Tradable;
|
||||
import io.bitsquare.trade.Trade;
|
||||
import io.bitsquare.trade.offer.OpenOffer;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ -40,13 +44,14 @@ import javax.inject.Inject;
|
||||
public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTradesViewModel> {
|
||||
|
||||
@FXML
|
||||
TableView<ClosedTradableListItem> table;
|
||||
TableView<ClosedTradableListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<ClosedTradableListItem, ClosedTradableListItem> priceColumn, amountColumn, volumeColumn,
|
||||
directionColumn, dateColumn, tradeIdColumn, stateColumn, avatarColumn;
|
||||
private final BSFormatter formatter;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private SortedList<ClosedTradableListItem> sortedList;
|
||||
|
||||
@Inject
|
||||
public ClosedTradesView(ClosedTradesViewModel model, BSFormatter formatter, OfferDetailsWindow offerDetailsWindow, TradeDetailsWindow tradeDetailsWindow) {
|
||||
@ -58,6 +63,9 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No closed trades available"));
|
||||
|
||||
setTradeIdColumnCellFactory();
|
||||
setDirectionColumnCellFactory();
|
||||
setAmountColumnCellFactory();
|
||||
@ -67,13 +75,55 @@ public class ClosedTradesView extends ActivatableViewAndModel<VBox, ClosedTrades
|
||||
setStateColumnCellFactory();
|
||||
setAvatarColumnCellFactory();
|
||||
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
table.setPlaceholder(new Label("No closed trades available"));
|
||||
/* , , ,
|
||||
, , , , avatarColumn;
|
||||
*/
|
||||
tradeIdColumn.setComparator((o1, o2) -> o1.getTradable().getId().compareTo(o2.getTradable().getId()));
|
||||
dateColumn.setComparator((o1, o2) -> o1.getTradable().getDate().compareTo(o2.getTradable().getDate()));
|
||||
directionColumn.setComparator((o1, o2) -> o1.getTradable().getOffer().getDirection().compareTo(o2.getTradable().getOffer().getDirection()));
|
||||
priceColumn.setComparator((o1, o2) -> o1.getTradable().getOffer().getPrice().compareTo(o2.getTradable().getOffer().getPrice()));
|
||||
volumeColumn.setComparator((o1, o2) -> {
|
||||
if (o1.getTradable() instanceof Trade && o2.getTradable() instanceof Trade) {
|
||||
Fiat tradeVolume1 = ((Trade) o1.getTradable()).getTradeVolume();
|
||||
Fiat tradeVolume2 = ((Trade) o2.getTradable()).getTradeVolume();
|
||||
return tradeVolume1 != null && tradeVolume2 != null ? tradeVolume1.compareTo(tradeVolume2) : 0;
|
||||
} else
|
||||
return 0;
|
||||
});
|
||||
amountColumn.setComparator((o1, o2) -> {
|
||||
if (o1.getTradable() instanceof Trade && o2.getTradable() instanceof Trade) {
|
||||
Coin amount1 = ((Trade) o1.getTradable()).getTradeAmount();
|
||||
Coin amount2 = ((Trade) o2.getTradable()).getTradeAmount();
|
||||
return amount1 != null && amount2 != null ? amount1.compareTo(amount2) : 0;
|
||||
} else
|
||||
return 0;
|
||||
});
|
||||
avatarColumn.setComparator((o1, o2) -> {
|
||||
if (o1.getTradable() instanceof Trade && o2.getTradable() instanceof Trade) {
|
||||
NodeAddress tradingPeerNodeAddress1 = ((Trade) o1.getTradable()).getTradingPeerNodeAddress();
|
||||
NodeAddress tradingPeerNodeAddress2 = ((Trade) o2.getTradable()).getTradingPeerNodeAddress();
|
||||
String address1 = tradingPeerNodeAddress1 != null ? tradingPeerNodeAddress1.hostName : "";
|
||||
String address2 = tradingPeerNodeAddress2 != null ? tradingPeerNodeAddress2.hostName : "";
|
||||
return address1 != null && address2 != null ? address1.compareTo(address2) : 0;
|
||||
} else
|
||||
return 0;
|
||||
});
|
||||
stateColumn.setComparator((o1, o2) -> model.getState(o1).compareTo(model.getState(o2)));
|
||||
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
table.setItems(model.getList());
|
||||
sortedList = new SortedList<>(model.getList());
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
sortedList.comparatorProperty().unbind();
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,15 +26,15 @@
|
||||
<Insets bottom="0.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Trade ID" fx:id="tradeIdColumn" minWidth="120" maxWidth="120" sortable="false"/>
|
||||
<TableColumn text="Trade ID" fx:id="tradeIdColumn" minWidth="120" maxWidth="120"/>
|
||||
<TableColumn text="Date" fx:id="dateColumn" minWidth="130"/>
|
||||
<TableColumn text="Trade amount in BTC" fx:id="amountColumn" minWidth="130"/>
|
||||
<TableColumn text="Price" fx:id="priceColumn" minWidth="100"/>
|
||||
<TableColumn text="Trade amount in EUR" fx:id="volumeColumn" minWidth="130"/>
|
||||
<TableColumn text="Trade type" fx:id="directionColumn" minWidth="80"/>
|
||||
<TableColumn text="State" fx:id="stateColumn" minWidth="80" sortable="false"/>
|
||||
<TableColumn text="State" fx:id="stateColumn" minWidth="80"/>
|
||||
</columns>
|
||||
</TableView>
|
||||
|
||||
|
@ -22,6 +22,7 @@ import io.bitsquare.gui.common.view.FxmlView;
|
||||
import io.bitsquare.gui.components.HyperlinkWithIcon;
|
||||
import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.layout.VBox;
|
||||
@ -33,11 +34,12 @@ import javax.inject.Inject;
|
||||
public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTradesViewModel> {
|
||||
|
||||
@FXML
|
||||
TableView<FailedTradesListItem> table;
|
||||
TableView<FailedTradesListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<FailedTradesListItem, FailedTradesListItem> priceColumn, amountColumn, volumeColumn,
|
||||
directionColumn, dateColumn, tradeIdColumn, stateColumn;
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private SortedList<FailedTradesListItem> sortedList;
|
||||
|
||||
@Inject
|
||||
public FailedTradesView(FailedTradesViewModel model, TradeDetailsWindow tradeDetailsWindow) {
|
||||
@ -47,6 +49,9 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No closed trades available"));
|
||||
|
||||
setTradeIdColumnCellFactory();
|
||||
setDirectionColumnCellFactory();
|
||||
setAmountColumnCellFactory();
|
||||
@ -55,15 +60,31 @@ public class FailedTradesView extends ActivatableViewAndModel<VBox, FailedTrades
|
||||
setDateColumnCellFactory();
|
||||
setStateColumnCellFactory();
|
||||
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
table.setPlaceholder(new Label("No closed trades available"));
|
||||
tradeIdColumn.setComparator((o1, o2) -> o1.getTrade().getId().compareTo(o2.getTrade().getId()));
|
||||
dateColumn.setComparator((o1, o2) -> o1.getTrade().getDate().compareTo(o2.getTrade().getDate()));
|
||||
priceColumn.setComparator((o1, o2) -> o1.getTrade().getOffer().getPrice().compareTo(o2.getTrade().getOffer().getPrice()));
|
||||
volumeColumn.setComparator((o1, o2) -> o1.getTrade().getTradeVolume().compareTo(o2.getTrade().getTradeVolume()));
|
||||
amountColumn.setComparator((o1, o2) -> o1.getTrade().getTradeAmount().compareTo(o2.getTrade().getTradeAmount()));
|
||||
stateColumn.setComparator((o1, o2) -> model.getState(o1).compareTo(model.getState(o2)));
|
||||
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
table.setItems(model.getList());
|
||||
sortedList = new SortedList<>(model.getList());
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
sortedList.comparatorProperty().unbind();
|
||||
}
|
||||
|
||||
|
||||
private void setTradeIdColumnCellFactory() {
|
||||
tradeIdColumn.setCellValueFactory((offerListItem) -> new ReadOnlyObjectWrapper<>(offerListItem.getValue()));
|
||||
tradeIdColumn.setCellFactory(
|
||||
|
@ -26,9 +26,9 @@
|
||||
<Insets bottom="0.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="ALWAYS">
|
||||
<TableView fx:id="tableView" VBox.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Offer ID" fx:id="offerIdColumn" minWidth="100" sortable="false"/>
|
||||
<TableColumn text="Offer ID" fx:id="offerIdColumn" minWidth="100"/>
|
||||
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="130"/>
|
||||
<TableColumn text="Amount in BTC (Min.)" fx:id="amountColumn" minWidth="130"/>
|
||||
<TableColumn text="Price" fx:id="priceColumn" minWidth="100"/>
|
||||
|
@ -28,6 +28,7 @@ import io.bitsquare.gui.main.overlays.popups.Popup;
|
||||
import io.bitsquare.gui.main.overlays.windows.OfferDetailsWindow;
|
||||
import io.bitsquare.trade.offer.OpenOffer;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.ImageView;
|
||||
@ -40,12 +41,13 @@ import javax.inject.Inject;
|
||||
public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersViewModel> {
|
||||
|
||||
@FXML
|
||||
TableView<OpenOfferListItem> table;
|
||||
TableView<OpenOfferListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<OpenOfferListItem, OpenOfferListItem> priceColumn, amountColumn, volumeColumn,
|
||||
directionColumn, dateColumn, offerIdColumn, removeItemColumn;
|
||||
private final Navigation navigation;
|
||||
private final OfferDetailsWindow offerDetailsWindow;
|
||||
private SortedList<OpenOfferListItem> sortedList;
|
||||
|
||||
@Inject
|
||||
public OpenOffersView(OpenOffersViewModel model, Navigation navigation, OfferDetailsWindow offerDetailsWindow) {
|
||||
@ -64,13 +66,30 @@ public class OpenOffersView extends ActivatableViewAndModel<VBox, OpenOffersView
|
||||
setDateColumnCellFactory();
|
||||
setRemoveColumnCellFactory();
|
||||
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
table.setPlaceholder(new Label("No open offers available"));
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No open offers available"));
|
||||
|
||||
offerIdColumn.setComparator((o1, o2) -> o1.getOffer().getId().compareTo(o2.getOffer().getId()));
|
||||
directionColumn.setComparator((o1, o2) -> o1.getOffer().getDirection().compareTo(o2.getOffer().getDirection()));
|
||||
amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
|
||||
priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
|
||||
volumeColumn.setComparator((o1, o2) -> o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
|
||||
dateColumn.setComparator((o1, o2) -> o1.getOffer().getDate().compareTo(o2.getOffer().getDate()));
|
||||
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
table.setItems(model.getList());
|
||||
sortedList = new SortedList<>(model.getList());
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
sortedList.comparatorProperty().unbind();
|
||||
}
|
||||
|
||||
private void onRemoveOpenOffer(OpenOffer openOffer) {
|
||||
|
@ -18,7 +18,6 @@
|
||||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<VBox fx:id="root" fx:controller="io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView"
|
||||
@ -27,29 +26,13 @@
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
|
||||
</padding>
|
||||
|
||||
<TableView fx:id="table" VBox.vgrow="SOMETIMES">
|
||||
<TableView fx:id="tableView" VBox.vgrow="SOMETIMES">
|
||||
<columns>
|
||||
<TableColumn text="Trade ID" fx:id="idColumn" minWidth="100" sortable="false">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="id"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Trade ID" fx:id="idColumn" minWidth="100"/>
|
||||
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="130"/>
|
||||
<TableColumn text="Trade amount in BTC" fx:id="tradeAmountColumn" minWidth="130">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="tradeAmount"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Price" fx:id="priceColumn" minWidth="100">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="price"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Trade amount" fx:id="tradeVolumeColumn" minWidth="130">
|
||||
<cellValueFactory>
|
||||
<PropertyValueFactory property="tradeVolume"/>
|
||||
</cellValueFactory>
|
||||
</TableColumn>
|
||||
<TableColumn text="Trade amount in BTC" fx:id="tradeAmountColumn" minWidth="130"/>
|
||||
<TableColumn text="Price" fx:id="priceColumn" minWidth="100"/>
|
||||
<TableColumn text="Trade amount" fx:id="tradeVolumeColumn" minWidth="130"/>
|
||||
<TableColumn text="Payment method" fx:id="paymentMethodColumn" minWidth="120"/>
|
||||
<TableColumn text="My role" fx:id="roleColumn" minWidth="120" maxWidth="120"/>
|
||||
<TableColumn text="" fx:id="avatarColumn" minWidth="32" maxWidth="32"/>
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package io.bitsquare.gui.main.portfolio.pendingtrades;
|
||||
|
||||
import io.bitsquare.app.Log;
|
||||
import io.bitsquare.common.UserThread;
|
||||
import io.bitsquare.gui.common.view.ActivatableViewAndModel;
|
||||
import io.bitsquare.gui.common.view.FxmlView;
|
||||
@ -27,12 +26,12 @@ import io.bitsquare.gui.main.overlays.windows.TradeDetailsWindow;
|
||||
import io.bitsquare.gui.util.BSFormatter;
|
||||
import io.bitsquare.gui.util.ImageUtil;
|
||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.event.EventHandler;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.control.cell.TextFieldTableCell;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyCodeCombination;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
@ -40,9 +39,6 @@ import javafx.scene.input.KeyEvent;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
import javafx.util.StringConverter;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
|
||||
@ -54,14 +50,12 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
private final TradeDetailsWindow tradeDetailsWindow;
|
||||
private final BSFormatter formatter;
|
||||
@FXML
|
||||
TableView<PendingTradesListItem> table;
|
||||
TableView<PendingTradesListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<PendingTradesListItem, Fiat> priceColumn, tradeVolumeColumn;
|
||||
TableColumn<PendingTradesListItem, PendingTradesListItem> priceColumn, tradeVolumeColumn, tradeAmountColumn, avatarColumn, roleColumn, paymentMethodColumn, idColumn, dateColumn;
|
||||
@FXML
|
||||
TableColumn<PendingTradesListItem, PendingTradesListItem> avatarColumn, roleColumn, paymentMethodColumn, idColumn, dateColumn;
|
||||
@FXML
|
||||
TableColumn<PendingTradesListItem, Coin> tradeAmountColumn;
|
||||
|
||||
private SortedList<PendingTradesListItem> sortedList;
|
||||
private TradeSubView selectedSubView;
|
||||
private EventHandler<KeyEvent> keyEventEventHandler;
|
||||
private Scene scene;
|
||||
@ -92,9 +86,22 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
setRoleColumnCellFactory();
|
||||
setAvatarColumnCellFactory();
|
||||
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
table.setPlaceholder(new Label("No pending trades available"));
|
||||
table.setMinHeight(100);
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No pending trades available"));
|
||||
tableView.setMinHeight(100);
|
||||
|
||||
idColumn.setComparator((o1, o2) -> o1.getTrade().getId().compareTo(o2.getTrade().getId()));
|
||||
dateColumn.setComparator((o1, o2) -> o1.getTrade().getDate().compareTo(o2.getTrade().getDate()));
|
||||
tradeVolumeColumn.setComparator((o1, o2) -> o1.getTrade().getTradeVolume().compareTo(o2.getTrade().getTradeVolume()));
|
||||
tradeAmountColumn.setComparator((o1, o2) -> o1.getTrade().getTradeAmount().compareTo(o2.getTrade().getTradeAmount()));
|
||||
priceColumn.setComparator((o1, o2) -> o1.getPrice().compareTo(o2.getPrice()));
|
||||
paymentMethodColumn.setComparator((o1, o2) -> o1.getTrade().getOffer().getPaymentMethod().getId().compareTo(o2.getTrade().getOffer().getPaymentMethod().getId()));
|
||||
avatarColumn.setComparator((o1, o2) -> o1.getTrade().getTradingPeerNodeAddress().hostName.compareTo(o2.getTrade().getTradingPeerNodeAddress().hostName));
|
||||
roleColumn.setComparator((o1, o2) -> model.getMyRole(o1).compareTo(model.getMyRole(o2)));
|
||||
|
||||
dateColumn.setSortType(TableColumn.SortType.DESCENDING);
|
||||
tableView.getSortOrder().add(dateColumn);
|
||||
|
||||
|
||||
// we use a hidden emergency shortcut to open support ticket
|
||||
keyEventEventHandler = event -> {
|
||||
@ -116,7 +123,10 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
|
||||
@Override
|
||||
protected void activate() {
|
||||
Log.traceCall();
|
||||
sortedList = new SortedList<>(model.dataModel.list);
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
|
||||
scene = root.getScene();
|
||||
if (scene != null) {
|
||||
scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler);
|
||||
@ -133,7 +143,6 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
}
|
||||
});*/
|
||||
}
|
||||
table.setItems(model.dataModel.list);
|
||||
|
||||
selectedItemSubscription = EasyBind.subscribe(model.dataModel.selectedItemProperty, selectedItem -> {
|
||||
if (selectedItem != null) {
|
||||
@ -164,7 +173,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
selectedSubView.activate();
|
||||
});
|
||||
|
||||
selectedTableItemSubscription = EasyBind.subscribe(table.getSelectionModel().selectedItemProperty(),
|
||||
selectedTableItemSubscription = EasyBind.subscribe(tableView.getSelectionModel().selectedItemProperty(),
|
||||
selectedItem -> {
|
||||
if (selectedItem != null && !selectedItem.equals(model.dataModel.selectedItemProperty.get()))
|
||||
model.dataModel.onSelectItem(selectedItem);
|
||||
@ -175,6 +184,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
|
||||
@Override
|
||||
protected void deactivate() {
|
||||
sortedList.comparatorProperty().unbind();
|
||||
selectedItemSubscription.unsubscribe();
|
||||
selectedTableItemSubscription.unsubscribe();
|
||||
if (appFocusSubscription != null)
|
||||
@ -203,10 +213,10 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
PendingTradesListItem selectedItemFromModel = model.dataModel.selectedItemProperty.get();
|
||||
if (selectedItemFromModel != null) {
|
||||
// Select and focus selectedItem from model
|
||||
int index = table.getItems().indexOf(selectedItemFromModel);
|
||||
int index = tableView.getItems().indexOf(selectedItemFromModel);
|
||||
UserThread.execute(() -> {
|
||||
//TODO app wide focus
|
||||
table.getSelectionModel().select(index);
|
||||
tableView.getSelectionModel().select(index);
|
||||
//table.requestFocus();
|
||||
//UserThread.execute(() -> table.getFocusModel().focus(index));
|
||||
});
|
||||
@ -280,49 +290,69 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
|
||||
}
|
||||
|
||||
private void setAmountColumnCellFactory() {
|
||||
tradeAmountColumn.setCellFactory(TextFieldTableCell.<PendingTradesListItem, Coin>forTableColumn(
|
||||
new StringConverter<Coin>() {
|
||||
tradeAmountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
tradeAmountColumn.setCellFactory(
|
||||
new Callback<TableColumn<PendingTradesListItem, PendingTradesListItem>, TableCell<PendingTradesListItem,
|
||||
PendingTradesListItem>>() {
|
||||
@Override
|
||||
public String toString(Coin value) {
|
||||
return formatter.formatCoinWithCode(value);
|
||||
public TableCell<PendingTradesListItem, PendingTradesListItem> call(
|
||||
TableColumn<PendingTradesListItem, PendingTradesListItem> column) {
|
||||
return new TableCell<PendingTradesListItem, PendingTradesListItem>() {
|
||||
@Override
|
||||
public void updateItem(final PendingTradesListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty)
|
||||
setText(formatter.formatCoinWithCode(item.getTrade().getPayoutAmount()));
|
||||
else
|
||||
setText(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Coin fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private void setPriceColumnCellFactory() {
|
||||
priceColumn.setCellFactory(TextFieldTableCell.<PendingTradesListItem, Fiat>forTableColumn(
|
||||
new StringConverter<Fiat>() {
|
||||
priceColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
priceColumn.setCellFactory(
|
||||
new Callback<TableColumn<PendingTradesListItem, PendingTradesListItem>, TableCell<PendingTradesListItem,
|
||||
PendingTradesListItem>>() {
|
||||
@Override
|
||||
public String toString(Fiat value) {
|
||||
return formatter.formatPriceWithCode(value);
|
||||
public TableCell<PendingTradesListItem, PendingTradesListItem> call(
|
||||
TableColumn<PendingTradesListItem, PendingTradesListItem> column) {
|
||||
return new TableCell<PendingTradesListItem, PendingTradesListItem>() {
|
||||
@Override
|
||||
public void updateItem(final PendingTradesListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty)
|
||||
setText(formatter.formatPriceWithCode(item.getPrice()));
|
||||
else
|
||||
setText(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fiat fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void setVolumeColumnCellFactory() {
|
||||
tradeVolumeColumn.setCellFactory(TextFieldTableCell.<PendingTradesListItem, Fiat>forTableColumn(
|
||||
new StringConverter<Fiat>() {
|
||||
tradeVolumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||
tradeVolumeColumn.setCellFactory(
|
||||
new Callback<TableColumn<PendingTradesListItem, PendingTradesListItem>, TableCell<PendingTradesListItem,
|
||||
PendingTradesListItem>>() {
|
||||
@Override
|
||||
public String toString(Fiat value) {
|
||||
return formatter.formatFiatWithCode(value);
|
||||
public TableCell<PendingTradesListItem, PendingTradesListItem> call(
|
||||
TableColumn<PendingTradesListItem, PendingTradesListItem> column) {
|
||||
return new TableCell<PendingTradesListItem, PendingTradesListItem>() {
|
||||
@Override
|
||||
public void updateItem(final PendingTradesListItem item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
if (item != null && !empty)
|
||||
setText(formatter.formatPriceWithCode(item.getTrade().getTradeVolume()));
|
||||
else
|
||||
setText(null);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fiat fromString(String string) {
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private void setPaymentMethodColumnCellFactory() {
|
||||
|
@ -48,7 +48,7 @@ public class BuyerStep5View extends TradeStepView {
|
||||
protected Label btcTradeAmountLabel;
|
||||
protected Label fiatTradeAmountLabel;
|
||||
private InputTextField withdrawAddressTextField;
|
||||
private Button withdrawButton;
|
||||
private Button withdrawToExternalWalletButton, useSavingsWalletButton;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -116,11 +116,19 @@ public class BuyerStep5View extends TradeStepView {
|
||||
addTitledGroupBg(gridPane, ++gridRow, 2, "Withdraw your bitcoins", Layout.GROUP_DISTANCE);
|
||||
addLabelTextField(gridPane, gridRow, "Amount to withdraw:", model.getPayoutAmount(), Layout.FIRST_ROW_AND_GROUP_DISTANCE);
|
||||
withdrawAddressTextField = addLabelInputTextField(gridPane, ++gridRow, "Withdraw to address:").second;
|
||||
withdrawButton = addButtonAfterGroup(gridPane, ++gridRow, "Withdraw to external wallet");
|
||||
withdrawButton.setOnAction(e -> reviewWithdrawal());
|
||||
|
||||
Tuple2<Button, Button> tuple2 = add2ButtonsAfterGroup(gridPane, ++gridRow, "Move to Bitsquare wallet", "Withdraw to external wallet");
|
||||
useSavingsWalletButton = tuple2.first;
|
||||
withdrawToExternalWalletButton = tuple2.second;
|
||||
useSavingsWalletButton.setOnAction(e -> {
|
||||
model.dataModel.walletService.swapTradeToSavings(trade.getId());
|
||||
handleTradeCompleted();
|
||||
model.dataModel.tradeManager.addTradeToClosedTrades(trade);
|
||||
});
|
||||
withdrawToExternalWalletButton.setOnAction(e -> reviewWithdrawal());
|
||||
|
||||
if (BitsquareApp.DEV_MODE) {
|
||||
withdrawAddressTextField.setText("mi8k5f9L972VgDaT4LgjAhriC9hHEPL7EW");
|
||||
withdrawAddressTextField.setText("mo6y756TnpdZQCeHStraavjqrndeXzVkxi");
|
||||
} else {
|
||||
String key = "tradeCompleted" + trade.getId();
|
||||
if (preferences.showAgain(key)) {
|
||||
@ -133,29 +141,6 @@ public class BuyerStep5View extends TradeStepView {
|
||||
}
|
||||
}
|
||||
|
||||
private void doWithdrawal() {
|
||||
withdrawButton.setDisable(true);
|
||||
model.dataModel.onWithdrawRequest(withdrawAddressTextField.getText(),
|
||||
() -> {
|
||||
String key = "tradeCompleteWithdrawCompletedInfo";
|
||||
new Popup().headLine("Withdrawal completed")
|
||||
.feedback("Your completed trades are stored under \"Portfolio/History\".\n" +
|
||||
"You can review all your bitcoin transactions under \"Funds/Transactions\"")
|
||||
.actionButtonText("Go to \"Transactions\"")
|
||||
.onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class))
|
||||
.dontShowAgainId(key, preferences)
|
||||
.show();
|
||||
withdrawButton.setDisable(true);
|
||||
},
|
||||
(errorMessage, throwable) -> {
|
||||
withdrawButton.setDisable(false);
|
||||
if (throwable != null && throwable.getMessage() != null)
|
||||
new Popup().error(errorMessage + "\n\n" + throwable.getMessage()).show();
|
||||
else
|
||||
new Popup().error(errorMessage).show();
|
||||
});
|
||||
}
|
||||
|
||||
private void reviewWithdrawal() {
|
||||
Coin senderAmount = trade.getPayoutAmount();
|
||||
WalletService walletService = model.dataModel.walletService;
|
||||
@ -174,11 +159,11 @@ public class BuyerStep5View extends TradeStepView {
|
||||
validateWithdrawAddress();
|
||||
} else if (Restrictions.isAboveFixedTxFeeAndDust(senderAmount)) {
|
||||
try {
|
||||
Coin requiredFee = walletService.getRequiredFee(fromAddresses, toAddresses, senderAmount, null);
|
||||
Coin receiverAmount = senderAmount.subtract(requiredFee);
|
||||
if (BitsquareApp.DEV_MODE) {
|
||||
doWithdrawal();
|
||||
} else {
|
||||
Coin requiredFee = walletService.getRequiredFee(fromAddresses, toAddresses, senderAmount, null);
|
||||
Coin receiverAmount = senderAmount.subtract(requiredFee);
|
||||
BSFormatter formatter = model.formatter;
|
||||
String key = "reviewWithdrawalAtTradeComplete";
|
||||
if (preferences.showAgain(key)) {
|
||||
@ -190,9 +175,12 @@ public class BuyerStep5View extends TradeStepView {
|
||||
"The recipient will receive: " + formatter.formatCoinWithCode(receiverAmount) + "\n\n" +
|
||||
"Are you sure you want to proceed with the withdrawal?")
|
||||
.closeButtonText("Cancel")
|
||||
.onClose(() -> withdrawButton.setDisable(false))
|
||||
.onClose(() -> {
|
||||
useSavingsWalletButton.setDisable(false);
|
||||
withdrawToExternalWalletButton.setDisable(false);
|
||||
})
|
||||
.actionButtonText("Yes")
|
||||
.onAction(this::doWithdrawal)
|
||||
.onAction(() -> doWithdrawal())
|
||||
.dontShowAgainId(key, preferences)
|
||||
.show();
|
||||
} else {
|
||||
@ -210,10 +198,41 @@ public class BuyerStep5View extends TradeStepView {
|
||||
}
|
||||
}
|
||||
|
||||
private void doWithdrawal() {
|
||||
useSavingsWalletButton.setDisable(true);
|
||||
withdrawToExternalWalletButton.setDisable(true);
|
||||
|
||||
model.dataModel.onWithdrawRequest(withdrawAddressTextField.getText(),
|
||||
() -> {
|
||||
handleTradeCompleted();
|
||||
},
|
||||
(errorMessage, throwable) -> {
|
||||
useSavingsWalletButton.setDisable(false);
|
||||
withdrawToExternalWalletButton.setDisable(false);
|
||||
if (throwable != null && throwable.getMessage() != null)
|
||||
new Popup().error(errorMessage + "\n\n" + throwable.getMessage()).show();
|
||||
else
|
||||
new Popup().error(errorMessage).show();
|
||||
});
|
||||
}
|
||||
|
||||
private void handleTradeCompleted() {
|
||||
String key = "tradeCompleteWithdrawCompletedInfo";
|
||||
new Popup().headLine("Withdrawal completed")
|
||||
.feedback("Your completed trades are stored under \"Portfolio/History\".\n" +
|
||||
"You can review all your bitcoin transactions under \"Funds/Transactions\"")
|
||||
.actionButtonText("Go to \"Transactions\"")
|
||||
.onAction(() -> model.dataModel.navigation.navigateTo(MainView.class, FundsView.class, TransactionsView.class))
|
||||
.dontShowAgainId(key, preferences)
|
||||
.show();
|
||||
useSavingsWalletButton.setDisable(true);
|
||||
withdrawToExternalWalletButton.setDisable(true);
|
||||
}
|
||||
|
||||
private void validateWithdrawAddress() {
|
||||
withdrawAddressTextField.setValidator(model.btcAddressValidator);
|
||||
withdrawAddressTextField.requestFocus();
|
||||
withdrawButton.requestFocus();
|
||||
useSavingsWalletButton.requestFocus();
|
||||
}
|
||||
|
||||
protected String getBtcTradeAmountLabel() {
|
||||
|
@ -66,7 +66,7 @@
|
||||
</TextField>
|
||||
|
||||
<Label fx:id="p2PPeersLabel" text="Connected peers:" GridPane.rowIndex="4"/>
|
||||
<TableView fx:id="p2PPeerTable" GridPane.rowIndex="4" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
<TableView fx:id="tableView" GridPane.rowIndex="4" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS"
|
||||
GridPane.vgrow="ALWAYS">
|
||||
<columns>
|
||||
<TableColumn text="Onion address" fx:id="onionAddressColumn" minWidth="220">
|
||||
|
@ -32,6 +32,7 @@ import io.bitsquare.p2p.network.Statistic;
|
||||
import io.bitsquare.user.Preferences;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.VPos;
|
||||
@ -68,7 +69,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
@FXML
|
||||
CheckBox useTorCheckBox;
|
||||
@FXML
|
||||
TableView<P2pNetworkListItem> p2PPeerTable;
|
||||
TableView<P2pNetworkListItem> tableView;
|
||||
@FXML
|
||||
TableColumn<P2pNetworkListItem, String> onionAddressColumn, connectionTypeColumn, creationDateColumn,
|
||||
/*lastActivityColumn,*/ roundTripTimeColumn, sentBytesColumn, receivedBytesColumn, peerTypeColumn;
|
||||
@ -76,6 +77,7 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
private Subscription bitcoinPeersSubscription;
|
||||
private Subscription nodeAddressSubscription;
|
||||
private ObservableList<P2pNetworkListItem> networkListItems = FXCollections.observableArrayList();
|
||||
private final SortedList<P2pNetworkListItem> sortedList = new SortedList<>(networkListItems);
|
||||
|
||||
@Inject
|
||||
public NetworkSettingsView(WalletService walletService, P2PService p2PService, Preferences preferences, Clock clock,
|
||||
@ -110,10 +112,10 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
}
|
||||
});
|
||||
|
||||
p2PPeerTable.setMinHeight(300);
|
||||
p2PPeerTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
p2PPeerTable.setPlaceholder(new Label("No connections are available"));
|
||||
p2PPeerTable.getSortOrder().add(creationDateColumn);
|
||||
tableView.setMinHeight(300);
|
||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
tableView.setPlaceholder(new Label("No connections are available"));
|
||||
tableView.getSortOrder().add(creationDateColumn);
|
||||
creationDateColumn.setSortType(TableColumn.SortType.ASCENDING);
|
||||
|
||||
|
||||
@ -152,8 +154,8 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
totalTraffic.textProperty().bind(EasyBind.combine(Statistic.totalSentBytesProperty(), Statistic.totalReceivedBytesProperty(),
|
||||
(sent, received) -> "Sent: " + formatter.formatBytes((int) sent) + ", received: " + formatter.formatBytes((int) received)));
|
||||
|
||||
p2PPeerTable.setItems(networkListItems);
|
||||
p2PPeerTable.sort();
|
||||
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
|
||||
tableView.setItems(sortedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -169,12 +171,14 @@ public class NetworkSettingsView extends ActivatableViewAndModel<GridPane, Activ
|
||||
if (numP2PPeersSubscription != null)
|
||||
numP2PPeersSubscription.unsubscribe();
|
||||
|
||||
p2PPeerTable.getItems().forEach(P2pNetworkListItem::cleanup);
|
||||
totalTraffic.textProperty().unbind();
|
||||
|
||||
sortedList.comparatorProperty().unbind();
|
||||
tableView.getItems().forEach(P2pNetworkListItem::cleanup);
|
||||
}
|
||||
|
||||
private void updateP2PTable() {
|
||||
p2PPeerTable.getItems().forEach(P2pNetworkListItem::cleanup);
|
||||
tableView.getItems().forEach(P2pNetworkListItem::cleanup);
|
||||
networkListItems.clear();
|
||||
networkListItems.setAll(p2PService.getNetworkNode().getAllConnections().stream()
|
||||
.map(connection -> new P2pNetworkListItem(connection, clock, formatter))
|
||||
|
@ -838,17 +838,6 @@ public class FormBuilder {
|
||||
return new Tuple3<>(button, progressIndicator, label);
|
||||
}
|
||||
|
||||
public static void removeRowFromGridPane(GridPane gridPane, int gridRow) {
|
||||
removeRowsFromGridPane(gridPane, gridRow, gridRow);
|
||||
}
|
||||
|
||||
public static void removeRowsFromGridPane(GridPane gridPane, int fromGridRow, int toGridRow) {
|
||||
Set<Node> nodes = new CopyOnWriteArraySet<>(gridPane.getChildren());
|
||||
nodes.stream()
|
||||
.filter(e -> GridPane.getRowIndex(e) >= fromGridRow && GridPane.getRowIndex(e) <= toGridRow)
|
||||
.forEach(e -> gridPane.getChildren().remove(e));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Trade: HBox, InputTextField, Label
|
||||
@ -913,4 +902,21 @@ public class FormBuilder {
|
||||
return new Tuple2<>(label, listView);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Remove
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void removeRowFromGridPane(GridPane gridPane, int gridRow) {
|
||||
removeRowsFromGridPane(gridPane, gridRow, gridRow);
|
||||
}
|
||||
|
||||
public static void removeRowsFromGridPane(GridPane gridPane, int fromGridRow, int toGridRow) {
|
||||
Set<Node> nodes = new CopyOnWriteArraySet<>(gridPane.getChildren());
|
||||
nodes.stream()
|
||||
.filter(e -> GridPane.getRowIndex(e) >= fromGridRow && GridPane.getRowIndex(e) <= toGridRow)
|
||||
.forEach(e -> gridPane.getChildren().remove(e));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -442,7 +442,10 @@ public class P2PDataStorage implements MessageListener, ConnectionListener {
|
||||
else
|
||||
result = expirableMailboxStoragePayload.receiverPubKeyForRemoveOperation.equals(protectedStorageEntry.ownerPubKey);
|
||||
} else {
|
||||
result = protectedStorageEntry != null && protectedStorageEntry.getStoragePayload() != null &&
|
||||
// TODO We got sometimes a nullpointer at protectedStorageEntry.ownerPubKey
|
||||
// Probably caused by an exception at deserialization: Offer: Cannot be deserialized.null
|
||||
result = protectedStorageEntry != null && protectedStorageEntry.ownerPubKey != null &&
|
||||
protectedStorageEntry.getStoragePayload() != null &&
|
||||
protectedStorageEntry.ownerPubKey.equals(protectedStorageEntry.getStoragePayload().getOwnerPubKey());
|
||||
}
|
||||
|
||||
|
@ -69,8 +69,7 @@ public final class MailboxStoragePayload implements StoragePayload {
|
||||
senderPubKeyForAddOperation = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(senderPubKeyForAddOperationBytes));
|
||||
receiverPubKeyForRemoveOperation = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(receiverPubKeyForRemoveOperationBytes));
|
||||
} catch (Throwable t) {
|
||||
log.error("Exception at readObject: " + t.getMessage());
|
||||
t.printStackTrace();
|
||||
log.warn("Exception at readObject: " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,7 @@ public class ProtectedMailboxStorageEntry extends ProtectedStorageEntry {
|
||||
receiversPubKey = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(receiversPubKeyBytes));
|
||||
updateTimeStamp();
|
||||
} catch (Throwable t) {
|
||||
log.error("Exception at readObject: " + t.getMessage());
|
||||
t.printStackTrace();
|
||||
log.warn("Exception at readObject: " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,8 +43,7 @@ public class ProtectedStorageEntry implements Payload {
|
||||
ownerPubKey = KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(ownerPubKeyBytes));
|
||||
updateTimeStamp();
|
||||
} catch (Throwable t) {
|
||||
log.error("Exception at readObject: " + t.getMessage());
|
||||
t.printStackTrace();
|
||||
log.warn("Exception at readObject: " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user