mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Remove listeners/unbind at views. Add more error handling
This commit is contained in:
parent
919e31f0d5
commit
3f6f8dd160
@ -23,8 +23,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
public abstract class Task<T extends Model> {
|
public abstract class Task<T extends Model> {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Task.class);
|
private static final Logger log = LoggerFactory.getLogger(Task.class);
|
||||||
|
|
||||||
public static Class<? extends Task> taskToInterceptBeforeRun;
|
public static Class<? extends Task> taskToIntercept;
|
||||||
public static Class<? extends Task> taskToInterceptAfterRun;
|
|
||||||
|
|
||||||
private final TaskRunner taskHandler;
|
private final TaskRunner taskHandler;
|
||||||
protected final T model;
|
protected final T model;
|
||||||
@ -35,26 +34,11 @@ public abstract class Task<T extends Model> {
|
|||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void run() {
|
abstract protected void run();
|
||||||
try {
|
|
||||||
interceptBeforeRun();
|
|
||||||
doRun();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
appendExceptionToErrorMessage(t);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected void doRun();
|
protected void runInterceptHook() {
|
||||||
|
if (getClass() == taskToIntercept)
|
||||||
private void interceptBeforeRun() {
|
throw new InterceptTaskException("Task intercepted for testing purpose. Task = " + getClass().getSimpleName());
|
||||||
if (getClass() == taskToInterceptBeforeRun)
|
|
||||||
throw new InterceptTaskException("Task intercepted before run got executed. Task = " + getClass().getSimpleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void interceptBeforeComplete() {
|
|
||||||
if (getClass() == taskToInterceptAfterRun)
|
|
||||||
throw new InterceptTaskException("Task intercepted before complete was called. Task = " + getClass().getSimpleName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void appendToErrorMessage(String message) {
|
protected void appendToErrorMessage(String message) {
|
||||||
@ -69,12 +53,6 @@ public abstract class Task<T extends Model> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void complete() {
|
protected void complete() {
|
||||||
try {
|
|
||||||
interceptBeforeComplete();
|
|
||||||
} catch (Throwable t) {
|
|
||||||
appendExceptionToErrorMessage(t);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
taskHandler.handleComplete();
|
taskHandler.handleComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +62,7 @@ public abstract class Task<T extends Model> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void failed(Throwable t) {
|
protected void failed(Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
appendExceptionToErrorMessage(t);
|
appendExceptionToErrorMessage(t);
|
||||||
failed();
|
failed();
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,6 @@ import com.google.common.base.Throwables;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InvalidClassException;
|
|
||||||
import java.io.InvalidObjectException;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -131,7 +129,7 @@ public class Storage<T extends Serializable> {
|
|||||||
log.info("Backup {} completed in {}msec", serializable.getClass().getSimpleName(), System.currentTimeMillis() - now);
|
log.info("Backup {} completed in {}msec", serializable.getClass().getSimpleName(), System.currentTimeMillis() - now);
|
||||||
|
|
||||||
return persistedObject;
|
return persistedObject;
|
||||||
} catch (InvalidClassException | InvalidObjectException | ClassCastException | ClassNotFoundException e) {
|
} catch (ClassCastException | ClassNotFoundException | IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error("Version of persisted class has changed. We cannot read the persisted data anymore. We make a backup and remove the inconsistent " +
|
log.error("Version of persisted class has changed. We cannot read the persisted data anymore. We make a backup and remove the inconsistent " +
|
||||||
"file.");
|
"file.");
|
||||||
|
@ -33,6 +33,8 @@ import io.bitsquare.storage.Storage;
|
|||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.protocol.trade.ProcessModel;
|
import io.bitsquare.trade.protocol.trade.ProcessModel;
|
||||||
import io.bitsquare.trade.protocol.trade.TradeProtocol;
|
import io.bitsquare.trade.protocol.trade.TradeProtocol;
|
||||||
|
import io.bitsquare.trade.states.BuyerTradeState;
|
||||||
|
import io.bitsquare.trade.states.SellerTradeState;
|
||||||
import io.bitsquare.trade.states.TradeState;
|
import io.bitsquare.trade.states.TradeState;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
@ -245,6 +247,13 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
storage.queueUpForSave();
|
storage.queueUpForSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFaultState() {
|
||||||
|
if (this instanceof SellerTrade)
|
||||||
|
setProcessState(SellerTradeState.ProcessState.FAULT);
|
||||||
|
else if (this instanceof BuyerTrade)
|
||||||
|
setProcessState(BuyerTradeState.ProcessState.FAULT);
|
||||||
|
}
|
||||||
|
|
||||||
public void setLifeCycleState(Trade.LifeCycleState lifeCycleState) {
|
public void setLifeCycleState(Trade.LifeCycleState lifeCycleState) {
|
||||||
this.lifeCycleState = lifeCycleState;
|
this.lifeCycleState = lifeCycleState;
|
||||||
lifeCycleStateProperty.set(lifeCycleState);
|
lifeCycleStateProperty.set(lifeCycleState);
|
||||||
|
@ -59,7 +59,10 @@ public class Offer implements Serializable {
|
|||||||
AVAILABLE,
|
AVAILABLE,
|
||||||
NOT_AVAILABLE,
|
NOT_AVAILABLE,
|
||||||
REMOVED,
|
REMOVED,
|
||||||
OFFERER_OFFLINE
|
OFFERER_OFFLINE,
|
||||||
|
|
||||||
|
TIMEOUT,
|
||||||
|
FAULT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import io.bitsquare.p2p.DHTService;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javafx.beans.property.LongProperty;
|
import javafx.beans.property.ReadOnlyLongProperty;
|
||||||
|
|
||||||
public interface OfferBookService extends DHTService {
|
public interface OfferBookService extends DHTService {
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public interface OfferBookService extends DHTService {
|
|||||||
|
|
||||||
void removeListener(Listener listener);
|
void removeListener(Listener listener);
|
||||||
|
|
||||||
LongProperty invalidationTimestampProperty();
|
ReadOnlyLongProperty invalidationTimestampProperty();
|
||||||
|
|
||||||
void requestInvalidationTimeStampFromDHT(String fiatCode);
|
void requestInvalidationTimeStampFromDHT(String fiatCode);
|
||||||
|
|
||||||
|
@ -21,14 +21,12 @@ import io.bitsquare.app.Version;
|
|||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.Tradable;
|
import io.bitsquare.trade.Tradable;
|
||||||
import io.bitsquare.trade.TradableList;
|
import io.bitsquare.trade.TradableList;
|
||||||
|
import io.bitsquare.util.Utilities;
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -95,26 +93,16 @@ public class OpenOffer implements Tradable, Serializable {
|
|||||||
|
|
||||||
|
|
||||||
private void startTimeout() {
|
private void startTimeout() {
|
||||||
log.trace("startTimeout");
|
|
||||||
stopTimeout();
|
stopTimeout();
|
||||||
|
|
||||||
timeoutTimer = new Timer();
|
timeoutTimer = Utilities.setTimeout(TIMEOUT, () -> {
|
||||||
TimerTask task = new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Threading.USER_THREAD.execute(() -> {
|
|
||||||
log.debug("Timeout reached");
|
log.debug("Timeout reached");
|
||||||
if (state == State.RESERVED)
|
if (state == State.RESERVED)
|
||||||
setState(State.AVAILABLE);
|
setState(State.AVAILABLE);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
timeoutTimer.schedule(task, TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void stopTimeout() {
|
protected void stopTimeout() {
|
||||||
log.trace("stopTimeout");
|
|
||||||
if (timeoutTimer != null) {
|
if (timeoutTimer != null) {
|
||||||
timeoutTimer.cancel();
|
timeoutTimer.cancel();
|
||||||
timeoutTimer = null;
|
timeoutTimer = null;
|
||||||
|
@ -34,6 +34,7 @@ import java.util.Map;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import javafx.beans.property.LongProperty;
|
import javafx.beans.property.LongProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyLongProperty;
|
||||||
import javafx.beans.property.SimpleLongProperty;
|
import javafx.beans.property.SimpleLongProperty;
|
||||||
|
|
||||||
import net.tomp2p.dht.FutureGet;
|
import net.tomp2p.dht.FutureGet;
|
||||||
@ -220,7 +221,7 @@ public class TomP2POfferBookService extends TomP2PDHTService implements OfferBoo
|
|||||||
if (offerDataObject instanceof Offer) {
|
if (offerDataObject instanceof Offer) {
|
||||||
offers.add((Offer) offerDataObject);
|
offers.add((Offer) offerDataObject);
|
||||||
}
|
}
|
||||||
} catch (ClassNotFoundException | IOException e) {
|
} catch (ClassCastException | ClassNotFoundException | IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.warn(e.getMessage());
|
log.warn(e.getMessage());
|
||||||
}
|
}
|
||||||
@ -293,7 +294,7 @@ public class TomP2POfferBookService extends TomP2PDHTService implements OfferBoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LongProperty invalidationTimestampProperty() {
|
public ReadOnlyLongProperty invalidationTimestampProperty() {
|
||||||
return invalidationTimestamp;
|
return invalidationTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +30,9 @@ import io.bitsquare.trade.protocol.availability.messages.OfferMessage;
|
|||||||
import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress;
|
import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress;
|
||||||
import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse;
|
import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse;
|
||||||
import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest;
|
import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest;
|
||||||
|
import io.bitsquare.util.Utilities;
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -52,7 +50,6 @@ public class OfferAvailabilityProtocol {
|
|||||||
private final DecryptedMessageHandler decryptedMessageHandler;
|
private final DecryptedMessageHandler decryptedMessageHandler;
|
||||||
private Timer timeoutTimer;
|
private Timer timeoutTimer;
|
||||||
|
|
||||||
private boolean isCanceled;
|
|
||||||
private TaskRunner<OfferAvailabilityModel> taskRunner;
|
private TaskRunner<OfferAvailabilityModel> taskRunner;
|
||||||
|
|
||||||
|
|
||||||
@ -84,8 +81,14 @@ public class OfferAvailabilityProtocol {
|
|||||||
model.messageService.addDecryptedMessageHandler(decryptedMessageHandler);
|
model.messageService.addDecryptedMessageHandler(decryptedMessageHandler);
|
||||||
|
|
||||||
taskRunner = new TaskRunner<>(model,
|
taskRunner = new TaskRunner<>(model,
|
||||||
() -> log.debug("sequence at onCheckOfferAvailability completed"),
|
() -> {
|
||||||
log::error
|
log.debug("sequence at onCheckOfferAvailability completed");
|
||||||
|
stopTimeout();
|
||||||
|
},
|
||||||
|
(errorMessage) -> {
|
||||||
|
log.error(errorMessage);
|
||||||
|
stopTimeout();
|
||||||
|
}
|
||||||
);
|
);
|
||||||
taskRunner.addTasks(
|
taskRunner.addTasks(
|
||||||
GetPeerAddress.class,
|
GetPeerAddress.class,
|
||||||
@ -96,7 +99,6 @@ public class OfferAvailabilityProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
isCanceled = true;
|
|
||||||
taskRunner.cancel();
|
taskRunner.cancel();
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
@ -119,15 +121,18 @@ public class OfferAvailabilityProtocol {
|
|||||||
|
|
||||||
private void handle(OfferAvailabilityResponse message) {
|
private void handle(OfferAvailabilityResponse message) {
|
||||||
stopTimeout();
|
stopTimeout();
|
||||||
|
startTimeout();
|
||||||
model.setMessage(message);
|
model.setMessage(message);
|
||||||
|
|
||||||
taskRunner = new TaskRunner<>(model,
|
taskRunner = new TaskRunner<>(model,
|
||||||
() -> {
|
() -> {
|
||||||
log.debug("sequence at handleReportOfferAvailabilityMessage completed");
|
log.debug("sequence at handle OfferAvailabilityResponse completed");
|
||||||
|
stopTimeout();
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
},
|
},
|
||||||
(errorMessage) -> {
|
(errorMessage) -> {
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
|
stopTimeout();
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -136,26 +141,16 @@ public class OfferAvailabilityProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void startTimeout() {
|
protected void startTimeout() {
|
||||||
log.debug("startTimeout");
|
|
||||||
stopTimeout();
|
stopTimeout();
|
||||||
|
|
||||||
timeoutTimer = new Timer();
|
timeoutTimer = Utilities.setTimeout(TIMEOUT, () -> {
|
||||||
TimerTask task = new TimerTask() {
|
log.warn("Timeout reached");
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Threading.USER_THREAD.execute(() -> {
|
|
||||||
log.debug("Timeout reached");
|
|
||||||
errorMessageHandler.handleErrorMessage("Timeout reached: Peer has not responded.");
|
errorMessageHandler.handleErrorMessage("Timeout reached: Peer has not responded.");
|
||||||
model.offer.setState(Offer.State.OFFERER_OFFLINE);
|
model.offer.setState(Offer.State.TIMEOUT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
timeoutTimer.schedule(task, TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void stopTimeout() {
|
protected void stopTimeout() {
|
||||||
log.debug("stopTimeout");
|
|
||||||
if (timeoutTimer != null) {
|
if (timeoutTimer != null) {
|
||||||
timeoutTimer.cancel();
|
timeoutTimer.cancel();
|
||||||
timeoutTimer = null;
|
timeoutTimer = null;
|
||||||
|
@ -37,13 +37,13 @@ public class GetPeerAddress extends Task<OfferAvailabilityModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
model.addressService.findPeerAddress(model.offer.getPubKeyRing(), new GetPeerAddressListener() {
|
model.addressService.findPeerAddress(model.offer.getPubKeyRing(), new GetPeerAddressListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onResult(Peer peer) {
|
public void onResult(Peer peer) {
|
||||||
model.setPeer(peer);
|
model.setPeer(peer);
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +55,7 @@ public class GetPeerAddress extends Task<OfferAvailabilityModel> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
model.offer.setState(Offer.State.FAULT);
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,9 @@ public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
OfferAvailabilityResponse offerAvailabilityResponse = (OfferAvailabilityResponse) model.getMessage();
|
OfferAvailabilityResponse offerAvailabilityResponse = (OfferAvailabilityResponse) model.getMessage();
|
||||||
|
|
||||||
if (model.offer.getState() != Offer.State.REMOVED) {
|
if (model.offer.getState() != Offer.State.REMOVED) {
|
||||||
@ -47,6 +48,7 @@ public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityMode
|
|||||||
|
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
model.offer.setState(Offer.State.FAULT);
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,8 +35,9 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
OfferAvailabilityRequest message = new OfferAvailabilityRequest(model.offer.getId(), model.getPubKeyRing());
|
OfferAvailabilityRequest message = new OfferAvailabilityRequest(model.offer.getId(), model.getPubKeyRing());
|
||||||
model.messageService.sendEncryptedMessage(model.getPeer(),
|
model.messageService.sendEncryptedMessage(model.getPeer(),
|
||||||
model.offer.getPubKeyRing(),
|
model.offer.getPubKeyRing(),
|
||||||
@ -55,6 +56,7 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
model.offer.setState(Offer.State.FAULT);
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ public class PlaceOfferModel implements Model {
|
|||||||
public final WalletService walletService;
|
public final WalletService walletService;
|
||||||
public final TradeWalletService tradeWalletService;
|
public final TradeWalletService tradeWalletService;
|
||||||
public final OfferBookService offerBookService;
|
public final OfferBookService offerBookService;
|
||||||
|
public boolean offerAddedToOfferBook;
|
||||||
private Transaction transaction;
|
private Transaction transaction;
|
||||||
|
|
||||||
public PlaceOfferModel(Offer offer,
|
public PlaceOfferModel(Offer offer,
|
||||||
|
@ -61,6 +61,15 @@ public class PlaceOfferProtocol {
|
|||||||
},
|
},
|
||||||
(errorMessage) -> {
|
(errorMessage) -> {
|
||||||
log.error(errorMessage);
|
log.error(errorMessage);
|
||||||
|
|
||||||
|
if (model.offerAddedToOfferBook) {
|
||||||
|
model.offerBookService.removeOffer(model.offer,
|
||||||
|
() -> {
|
||||||
|
model.offerAddedToOfferBook = false;
|
||||||
|
log.debug("Offer removed from offer book.");
|
||||||
|
},
|
||||||
|
(message, throwable) -> log.error(message));
|
||||||
|
}
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -33,9 +33,17 @@ public class AddOfferToRemoteOfferBook extends Task<PlaceOfferModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
|
try {
|
||||||
|
runInterceptHook();
|
||||||
model.offerBookService.addOffer(model.offer,
|
model.offerBookService.addOffer(model.offer,
|
||||||
this::complete,
|
() -> {
|
||||||
|
model.offerAddedToOfferBook = true;
|
||||||
|
complete();
|
||||||
|
},
|
||||||
(message, throwable) -> failed(throwable));
|
(message, throwable) -> failed(throwable));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
failed(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,13 +47,13 @@ public class BroadcastCreateOfferFeeTx extends Task<PlaceOfferModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
|
try {
|
||||||
|
runInterceptHook();
|
||||||
Coin totalsNeeded = model.offer.getSecurityDeposit().add(FeePolicy.CREATE_OFFER_FEE).add(FeePolicy.TX_FEE);
|
Coin totalsNeeded = model.offer.getSecurityDeposit().add(FeePolicy.CREATE_OFFER_FEE).add(FeePolicy.TX_FEE);
|
||||||
AddressEntry addressEntry = model.walletService.getAddressEntry(model.offer.getId());
|
AddressEntry addressEntry = model.walletService.getAddressEntry(model.offer.getId());
|
||||||
Coin balance = model.walletService.getBalanceForAddress(addressEntry.getAddress());
|
Coin balance = model.walletService.getBalanceForAddress(addressEntry.getAddress());
|
||||||
if (balance.compareTo(totalsNeeded) >= 0) {
|
if (balance.compareTo(totalsNeeded) >= 0) {
|
||||||
|
|
||||||
model.tradeWalletService.broadcastTx(model.getTransaction(), new FutureCallback<Transaction>() {
|
model.tradeWalletService.broadcastTx(model.getTransaction(), new FutureCallback<Transaction>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Transaction transaction) {
|
public void onSuccess(Transaction transaction) {
|
||||||
@ -100,6 +100,9 @@ public class BroadcastCreateOfferFeeTx extends Task<PlaceOfferModel> {
|
|||||||
failed("Not enough balance for placing the offer.");
|
failed("Not enough balance for placing the offer.");
|
||||||
updateStateOnFault();
|
updateStateOnFault();
|
||||||
}
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
failed(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStateOnFault() {
|
private void updateStateOnFault() {
|
||||||
|
@ -34,8 +34,9 @@ public class CreateOfferFeeTx extends Task<PlaceOfferModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
Transaction transaction = model.tradeWalletService.createOfferFeeTx(
|
Transaction transaction = model.tradeWalletService.createOfferFeeTx(
|
||||||
model.walletService.getAddressEntry(model.offer.getId()));
|
model.walletService.getAddressEntry(model.offer.getId()));
|
||||||
|
|
||||||
|
@ -32,8 +32,9 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
model.offer.validate();
|
model.offer.validate();
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
|
@ -102,8 +102,8 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
|
|||||||
CreateDepositTxInputs.class,
|
CreateDepositTxInputs.class,
|
||||||
SendPayDepositRequest.class
|
SendPayDepositRequest.class
|
||||||
);
|
);
|
||||||
taskRunner.run();
|
|
||||||
startTimeout();
|
startTimeout();
|
||||||
|
taskRunner.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -116,8 +116,8 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
|
|||||||
CreateDepositTxInputs.class,
|
CreateDepositTxInputs.class,
|
||||||
SendPayDepositRequest.class
|
SendPayDepositRequest.class
|
||||||
);
|
);
|
||||||
taskRunner.run();
|
|
||||||
startTimeout();
|
startTimeout();
|
||||||
|
taskRunner.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,8 +100,8 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
|
|||||||
CreateAndSignDepositTx.class,
|
CreateAndSignDepositTx.class,
|
||||||
SendPublishDepositTxRequest.class
|
SendPublishDepositTxRequest.class
|
||||||
);
|
);
|
||||||
taskRunner.run();
|
|
||||||
startTimeout();
|
startTimeout();
|
||||||
|
taskRunner.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -128,8 +128,8 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
|
|||||||
BroadcastTakeOfferFeeTx.class,
|
BroadcastTakeOfferFeeTx.class,
|
||||||
SendDepositTxInputsRequest.class
|
SendDepositTxInputsRequest.class
|
||||||
);
|
);
|
||||||
taskRunner.run();
|
|
||||||
startTimeout();
|
startTimeout();
|
||||||
|
taskRunner.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -153,8 +153,8 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
|
|||||||
CreateAndSignDepositTx.class,
|
CreateAndSignDepositTx.class,
|
||||||
SendPublishDepositTxRequest.class
|
SendPublishDepositTxRequest.class
|
||||||
);
|
);
|
||||||
taskRunner.run();
|
|
||||||
startTimeout();
|
startTimeout();
|
||||||
|
taskRunner.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handle(DepositTxPublishedMessage tradeMessage) {
|
private void handle(DepositTxPublishedMessage tradeMessage) {
|
||||||
|
@ -32,11 +32,9 @@ import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
|||||||
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
|
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
|
||||||
import io.bitsquare.trade.states.BuyerTradeState;
|
import io.bitsquare.trade.states.BuyerTradeState;
|
||||||
import io.bitsquare.trade.states.SellerTradeState;
|
import io.bitsquare.trade.states.SellerTradeState;
|
||||||
|
import io.bitsquare.util.Utilities;
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -141,28 +139,18 @@ public abstract class TradeProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void startTimeout() {
|
protected void startTimeout() {
|
||||||
log.debug("startTimeout");
|
|
||||||
stopTimeout();
|
stopTimeout();
|
||||||
|
|
||||||
timeoutTimer = new Timer();
|
timeoutTimer = Utilities.setTimeout(TIMEOUT, () -> {
|
||||||
TimerTask task = new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Threading.USER_THREAD.execute(() -> {
|
|
||||||
log.debug("Timeout reached");
|
log.debug("Timeout reached");
|
||||||
/* if (trade instanceof SellerTrade)
|
if (trade instanceof SellerTrade)
|
||||||
trade.setProcessState(SellerTradeState.ProcessState.TIMEOUT);
|
trade.setProcessState(SellerTradeState.ProcessState.TIMEOUT);
|
||||||
else if (trade instanceof BuyerTrade)
|
else if (trade instanceof BuyerTrade)
|
||||||
trade.setProcessState(BuyerTradeState.ProcessState.TIMEOUT);*/
|
trade.setProcessState(BuyerTradeState.ProcessState.TIMEOUT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
timeoutTimer.schedule(task, TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void stopTimeout() {
|
protected void stopTimeout() {
|
||||||
log.debug("stopTimeout");
|
|
||||||
if (timeoutTimer != null) {
|
if (timeoutTimer != null) {
|
||||||
timeoutTimer.cancel();
|
timeoutTimer.cancel();
|
||||||
timeoutTimer = null;
|
timeoutTimer = null;
|
||||||
|
@ -38,6 +38,16 @@ public class TradeTask extends Task<Trade> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed(Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
appendExceptionToErrorMessage(t);
|
||||||
|
trade.setThrowable(t);
|
||||||
|
trade.setErrorMessage(errorMessage);
|
||||||
|
trade.setFaultState();
|
||||||
|
failed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,9 @@ public class CreateDepositTxInputs extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
log.debug("trade.id" + trade.getId());
|
log.debug("trade.id" + trade.getId());
|
||||||
Coin inputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE);
|
Coin inputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE);
|
||||||
TradeWalletService.Result result = processModel.getTradeWalletService().createDepositTxInputs(inputAmount,
|
TradeWalletService.Result result = processModel.getTradeWalletService().createDepositTxInputs(inputAmount,
|
||||||
|
@ -37,8 +37,9 @@ public class ProcessDepositTxInputsRequest extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
DepositTxInputsRequest message = (DepositTxInputsRequest) processModel.getTradeMessage();
|
DepositTxInputsRequest message = (DepositTxInputsRequest) processModel.getTradeMessage();
|
||||||
checkTradeId(processModel.getId(), message);
|
checkTradeId(processModel.getId(), message);
|
||||||
checkNotNull(message);
|
checkNotNull(message);
|
||||||
|
@ -38,8 +38,9 @@ public class ProcessFinalizePayoutTxRequest extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
FinalizePayoutTxRequest message = (FinalizePayoutTxRequest) processModel.getTradeMessage();
|
FinalizePayoutTxRequest message = (FinalizePayoutTxRequest) processModel.getTradeMessage();
|
||||||
checkTradeId(processModel.getId(), message);
|
checkTradeId(processModel.getId(), message);
|
||||||
checkNotNull(message);
|
checkNotNull(message);
|
||||||
|
@ -37,8 +37,9 @@ public class ProcessPublishDepositTxRequest extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
PublishDepositTxRequest message = (PublishDepositTxRequest) processModel.getTradeMessage();
|
PublishDepositTxRequest message = (PublishDepositTxRequest) processModel.getTradeMessage();
|
||||||
checkTradeId(processModel.getId(), message);
|
checkTradeId(processModel.getId(), message);
|
||||||
checkNotNull(message);
|
checkNotNull(message);
|
||||||
|
@ -36,8 +36,9 @@ public class SendDepositTxPublishedMessage extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(processModel.getId(), trade.getDepositTx());
|
DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(processModel.getId(), trade.getDepositTx());
|
||||||
|
|
||||||
processModel.getMessageService().sendEncryptedMessage(
|
processModel.getMessageService().sendEncryptedMessage(
|
||||||
|
@ -36,8 +36,9 @@ public class SendFiatTransferStartedMessage extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
FiatTransferStartedMessage tradeMessage = new FiatTransferStartedMessage(processModel.getId(),
|
FiatTransferStartedMessage tradeMessage = new FiatTransferStartedMessage(processModel.getId(),
|
||||||
processModel.getAddressEntry().getAddressString()
|
processModel.getAddressEntry().getAddressString()
|
||||||
);
|
);
|
||||||
|
@ -36,8 +36,9 @@ public class SendPayDepositRequest extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
boolean isInitialRequest = trade instanceof BuyerAsTakerTrade;
|
boolean isInitialRequest = trade instanceof BuyerAsTakerTrade;
|
||||||
PayDepositRequest tradeMessage = new PayDepositRequest(
|
PayDepositRequest tradeMessage = new PayDepositRequest(
|
||||||
processModel.getId(),
|
processModel.getId(),
|
||||||
|
@ -36,8 +36,9 @@ public class SendPayoutTxFinalizedMessage extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
PayoutTxFinalizedMessage tradeMessage = new PayoutTxFinalizedMessage(processModel.getId(), trade.getPayoutTx());
|
PayoutTxFinalizedMessage tradeMessage = new PayoutTxFinalizedMessage(processModel.getId(), trade.getPayoutTx());
|
||||||
processModel.getMessageService().sendEncryptedMessage(
|
processModel.getMessageService().sendEncryptedMessage(
|
||||||
trade.getTradingPeer(),
|
trade.getTradingPeer(),
|
||||||
|
@ -35,8 +35,9 @@ public class SignAndFinalizePayoutTx extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
assert trade.getTradeAmount() != null;
|
assert trade.getTradeAmount() != null;
|
||||||
assert trade.getSecurityDeposit() != null;
|
assert trade.getSecurityDeposit() != null;
|
||||||
Coin sellerPayoutAmount = trade.getSecurityDeposit();
|
Coin sellerPayoutAmount = trade.getSecurityDeposit();
|
||||||
|
@ -44,8 +44,9 @@ public class SignAndPublishDepositTx extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
Coin inputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE);
|
Coin inputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE);
|
||||||
|
|
||||||
processModel.getTradeWalletService().signAndPublishDepositTx(
|
processModel.getTradeWalletService().signAndPublishDepositTx(
|
||||||
|
@ -35,8 +35,9 @@ public class VerifyAndSignContract extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
Contract contract = new Contract(
|
Contract contract = new Contract(
|
||||||
processModel.getOffer(),
|
processModel.getOffer(),
|
||||||
trade.getTradeAmount(),
|
trade.getTradeAmount(),
|
||||||
|
@ -32,8 +32,9 @@ public class VerifyTakeOfferFeePayment extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
//TODO mocked yet, need a confidence listeners
|
//TODO mocked yet, need a confidence listeners
|
||||||
int numOfPeersSeenTx = processModel.getWalletService().getNumOfPeersSeenTx(processModel.getTakeOfferFeeTxId());
|
int numOfPeersSeenTx = processModel.getWalletService().getNumOfPeersSeenTx(processModel.getTakeOfferFeeTxId());
|
||||||
/* if (numOfPeersSeenTx > 2) {
|
/* if (numOfPeersSeenTx > 2) {
|
||||||
|
@ -33,8 +33,9 @@ public class VerifyTakerAccount extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
//TODO mocked yet
|
//TODO mocked yet
|
||||||
if (processModel.getBlockChainService().verifyAccountRegistration()) {
|
if (processModel.getBlockChainService().verifyAccountRegistration()) {
|
||||||
if (processModel.getBlockChainService().isAccountBlackListed(processModel.tradingPeer.getAccountId(),
|
if (processModel.getBlockChainService().isAccountBlackListed(processModel.tradingPeer.getAccountId(),
|
||||||
|
@ -34,8 +34,9 @@ public class CommitDepositTx extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
// To access tx confidence we need to add that tx into our wallet.
|
// To access tx confidence we need to add that tx into our wallet.
|
||||||
Transaction depositTx = processModel.getTradeWalletService().commitTx(trade.getDepositTx());
|
Transaction depositTx = processModel.getTradeWalletService().commitTx(trade.getDepositTx());
|
||||||
|
|
||||||
|
@ -34,8 +34,9 @@ public class CreateAndSignContract extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
assert processModel.getTakeOfferFeeTxId() != null;
|
assert processModel.getTakeOfferFeeTxId() != null;
|
||||||
Contract contract = new Contract(
|
Contract contract = new Contract(
|
||||||
processModel.getOffer(),
|
processModel.getOffer(),
|
||||||
|
@ -36,8 +36,9 @@ public class CreateAndSignDepositTx extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
assert trade.getTradeAmount() != null;
|
assert trade.getTradeAmount() != null;
|
||||||
Coin inputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE).add(trade.getTradeAmount());
|
Coin inputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE).add(trade.getTradeAmount());
|
||||||
Coin msOutputAmount = inputAmount.add(trade.getSecurityDeposit());
|
Coin msOutputAmount = inputAmount.add(trade.getSecurityDeposit());
|
||||||
|
@ -37,8 +37,9 @@ public class ProcessDepositTxPublishedMessage extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
DepositTxPublishedMessage message = (DepositTxPublishedMessage) processModel.getTradeMessage();
|
DepositTxPublishedMessage message = (DepositTxPublishedMessage) processModel.getTradeMessage();
|
||||||
checkTradeId(processModel.getId(), message);
|
checkTradeId(processModel.getId(), message);
|
||||||
checkNotNull(message);
|
checkNotNull(message);
|
||||||
|
@ -37,8 +37,9 @@ public class ProcessFiatTransferStartedMessage extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
FiatTransferStartedMessage message = (FiatTransferStartedMessage) processModel.getTradeMessage();
|
FiatTransferStartedMessage message = (FiatTransferStartedMessage) processModel.getTradeMessage();
|
||||||
checkTradeId(processModel.getId(), message);
|
checkTradeId(processModel.getId(), message);
|
||||||
checkNotNull(message);
|
checkNotNull(message);
|
||||||
|
@ -36,8 +36,9 @@ public class ProcessPayDepositRequest extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
PayDepositRequest message = (PayDepositRequest) processModel.getTradeMessage();
|
PayDepositRequest message = (PayDepositRequest) processModel.getTradeMessage();
|
||||||
checkTradeId(processModel.getId(), message);
|
checkTradeId(processModel.getId(), message);
|
||||||
checkNotNull(message);
|
checkNotNull(message);
|
||||||
|
@ -37,8 +37,9 @@ public class ProcessPayoutTxFinalizedMessage extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
PayoutTxFinalizedMessage message = (PayoutTxFinalizedMessage) processModel.getTradeMessage();
|
PayoutTxFinalizedMessage message = (PayoutTxFinalizedMessage) processModel.getTradeMessage();
|
||||||
checkTradeId(processModel.getId(), message);
|
checkTradeId(processModel.getId(), message);
|
||||||
checkNotNull(message);
|
checkNotNull(message);
|
||||||
|
@ -39,8 +39,9 @@ public class SendDepositTxInputsRequest extends TradeTask {
|
|||||||
private int retryCounter = 0;
|
private int retryCounter = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
assert processModel.getTakeOfferFeeTx() != null;
|
assert processModel.getTakeOfferFeeTx() != null;
|
||||||
DepositTxInputsRequest message = new DepositTxInputsRequest(
|
DepositTxInputsRequest message = new DepositTxInputsRequest(
|
||||||
processModel.getId(),
|
processModel.getId(),
|
||||||
@ -67,7 +68,7 @@ public class SendDepositTxInputsRequest extends TradeTask {
|
|||||||
// We try to repeat once and if that fails as well we persist the state for a later retry.
|
// We try to repeat once and if that fails as well we persist the state for a later retry.
|
||||||
if (retryCounter == 0) {
|
if (retryCounter == 0) {
|
||||||
retryCounter++;
|
retryCounter++;
|
||||||
Threading.USER_THREAD.execute(SendDepositTxInputsRequest.this::doRun);
|
Threading.USER_THREAD.execute(SendDepositTxInputsRequest.this::run);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
appendToErrorMessage("Sending TakeOfferFeePayedMessage to offerer failed. Maybe the network connection was " +
|
appendToErrorMessage("Sending TakeOfferFeePayedMessage to offerer failed. Maybe the network connection was " +
|
||||||
|
@ -36,8 +36,9 @@ public class SendFinalizePayoutTxRequest extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
FinalizePayoutTxRequest message = new FinalizePayoutTxRequest(
|
FinalizePayoutTxRequest message = new FinalizePayoutTxRequest(
|
||||||
processModel.getId(),
|
processModel.getId(),
|
||||||
processModel.getPayoutTxSignature(),
|
processModel.getPayoutTxSignature(),
|
||||||
|
@ -35,8 +35,9 @@ public class SendPublishDepositTxRequest extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
PublishDepositTxRequest tradeMessage = new PublishDepositTxRequest(
|
PublishDepositTxRequest tradeMessage = new PublishDepositTxRequest(
|
||||||
processModel.getId(),
|
processModel.getId(),
|
||||||
processModel.getFiatAccount(),
|
processModel.getFiatAccount(),
|
||||||
|
@ -34,8 +34,9 @@ public class SignPayoutTx extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
assert trade.getTradeAmount() != null;
|
assert trade.getTradeAmount() != null;
|
||||||
assert trade.getSecurityDeposit() != null;
|
assert trade.getSecurityDeposit() != null;
|
||||||
Coin sellerPayoutAmount = trade.getSecurityDeposit();
|
Coin sellerPayoutAmount = trade.getSecurityDeposit();
|
||||||
|
@ -38,8 +38,9 @@ public class CommitPayoutTx extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
Transaction transaction = processModel.getTradeWalletService().commitTx(trade.getPayoutTx());
|
Transaction transaction = processModel.getTradeWalletService().commitTx(trade.getPayoutTx());
|
||||||
|
|
||||||
trade.setPayoutTx(transaction);
|
trade.setPayoutTx(transaction);
|
||||||
|
@ -47,8 +47,9 @@ public class SetupPayoutTxLockTimeReachedListener extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
log.debug("ChainHeight/LockTime: {} / {}", processModel.getTradeWalletService().getBestChainHeight(), trade.getLockTime());
|
log.debug("ChainHeight/LockTime: {} / {}", processModel.getTradeWalletService().getBestChainHeight(), trade.getLockTime());
|
||||||
if (processModel.getTradeWalletService().getBestChainHeight() >= trade.getLockTime()) {
|
if (processModel.getTradeWalletService().getBestChainHeight() >= trade.getLockTime()) {
|
||||||
broadcastTx();
|
broadcastTx();
|
||||||
|
@ -38,35 +38,25 @@ public class BroadcastTakeOfferFeeTx extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
processModel.getTradeWalletService().broadcastTx(processModel.getTakeOfferFeeTx(),
|
processModel.getTradeWalletService().broadcastTx(processModel.getTakeOfferFeeTx(),
|
||||||
new FutureCallback<Transaction>() {
|
new FutureCallback<Transaction>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Transaction transaction) {
|
public void onSuccess(Transaction transaction) {
|
||||||
log.debug("Take offer fee published successfully. Transaction ID = " + transaction.getHashAsString());
|
log.debug("Take offer fee published successfully. Transaction ID = " + transaction.getHashAsString());
|
||||||
|
|
||||||
/* if (trade instanceof SellerTrade)
|
|
||||||
trade.setProcessState(TakerTradeState.ProcessState.TAKE_OFFER_FEE_PUBLISHED);*/
|
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable t) {
|
public void onFailure(@NotNull Throwable t) {
|
||||||
t.printStackTrace();
|
|
||||||
appendToErrorMessage("Take offer fee payment failed. Maybe your network connection was lost. Please try again.");
|
appendToErrorMessage("Take offer fee payment failed. Maybe your network connection was lost. Please try again.");
|
||||||
trade.setErrorMessage(errorMessage);
|
|
||||||
|
|
||||||
/* if (trade instanceof SellerTrade)
|
|
||||||
trade.setProcessState(TakerTradeState.ProcessState.TAKE_OFFER_FEE_PUBLISH_FAILED);*/
|
|
||||||
|
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
|
||||||
trade.setThrowable(t);
|
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,20 +34,16 @@ public class CreateTakeOfferFeeTx extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
Transaction createTakeOfferFeeTx = processModel.getTradeWalletService().createTakeOfferFeeTx(processModel.getAddressEntry());
|
Transaction createTakeOfferFeeTx = processModel.getTradeWalletService().createTakeOfferFeeTx(processModel.getAddressEntry());
|
||||||
|
|
||||||
processModel.setTakeOfferFeeTx(createTakeOfferFeeTx);
|
processModel.setTakeOfferFeeTx(createTakeOfferFeeTx);
|
||||||
processModel.setTakeOfferFeeTxId(createTakeOfferFeeTx.getHashAsString());
|
processModel.setTakeOfferFeeTxId(createTakeOfferFeeTx.getHashAsString());
|
||||||
|
|
||||||
/*if (trade instanceof SellerTrade)
|
|
||||||
trade.setProcessState(TakerTradeState.ProcessState.TAKE_OFFER_FEE_TX_CREATED);*/
|
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
t.printStackTrace();
|
|
||||||
trade.setThrowable(t);
|
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,9 @@ public class VerifyOfferFeePayment extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
//TODO impl. missing
|
//TODO impl. missing
|
||||||
int numOfPeersSeenTx = processModel.getWalletService().getNumOfPeersSeenTx(processModel.getTakeOfferFeeTx().getHashAsString());
|
int numOfPeersSeenTx = processModel.getWalletService().getNumOfPeersSeenTx(processModel.getTakeOfferFeeTx().getHashAsString());
|
||||||
/* if (numOfPeersSeenTx > 2) {
|
/* if (numOfPeersSeenTx > 2) {
|
||||||
|
@ -32,8 +32,9 @@ public class VerifyOffererAccount extends TradeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
|
runInterceptHook();
|
||||||
if (processModel.getBlockChainService().verifyAccountRegistration()) {
|
if (processModel.getBlockChainService().verifyAccountRegistration()) {
|
||||||
if (processModel.getBlockChainService().isAccountBlackListed(processModel.tradingPeer.getAccountId(),
|
if (processModel.getBlockChainService().isAccountBlackListed(processModel.tradingPeer.getAccountId(),
|
||||||
processModel.tradingPeer.getFiatAccount())) {
|
processModel.tradingPeer.getFiatAccount())) {
|
||||||
|
@ -38,6 +38,9 @@ public class BuyerTradeState {
|
|||||||
PAYOUT_TX_COMMITTED,
|
PAYOUT_TX_COMMITTED,
|
||||||
PAYOUT_TX_SENT,
|
PAYOUT_TX_SENT,
|
||||||
|
|
||||||
PAYOUT_BROAD_CASTED
|
PAYOUT_BROAD_CASTED,
|
||||||
|
|
||||||
|
TIMEOUT,
|
||||||
|
FAULT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,9 @@ public class SellerTradeState {
|
|||||||
PAYOUT_TX_RECEIVED,
|
PAYOUT_TX_RECEIVED,
|
||||||
PAYOUT_TX_COMMITTED,
|
PAYOUT_TX_COMMITTED,
|
||||||
|
|
||||||
PAYOUT_BROAD_CASTED
|
PAYOUT_BROAD_CASTED,
|
||||||
|
|
||||||
|
TIMEOUT,
|
||||||
|
FAULT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
26
core/src/main/java/io/bitsquare/trade/states/TradePhase.java
Normal file
26
core/src/main/java/io/bitsquare/trade/states/TradePhase.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Bitsquare.
|
||||||
|
*
|
||||||
|
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.bitsquare.trade.states;
|
||||||
|
|
||||||
|
public enum TradePhase {
|
||||||
|
PREPARATION, // No damage
|
||||||
|
TAKE_OFFER__FEE_PAID, // Offer fee can be lost
|
||||||
|
DEPOSIT_BROAD_CASTED, // Need arbitrator
|
||||||
|
PAYOUT_BROAD_CASTED // Only charge back risk open
|
||||||
|
|
||||||
|
}
|
@ -54,9 +54,9 @@ public class Preferences implements Serializable {
|
|||||||
|
|
||||||
// Persisted fields
|
// Persisted fields
|
||||||
private String btcDenomination = MonetaryFormat.CODE_BTC;
|
private String btcDenomination = MonetaryFormat.CODE_BTC;
|
||||||
private Boolean useAnimations = true;
|
private boolean useAnimations = true;
|
||||||
private Boolean useEffects = true;
|
private boolean useEffects = true;
|
||||||
private Boolean displaySecurityDepositInfo = true;
|
private boolean displaySecurityDepositInfo = true;
|
||||||
|
|
||||||
// Observable wrappers
|
// Observable wrappers
|
||||||
transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination);
|
transient private final StringProperty btcDenominationProperty = new SimpleStringProperty(btcDenomination);
|
||||||
@ -111,7 +111,7 @@ public class Preferences implements Serializable {
|
|||||||
this.useEffectsProperty.set(useEffectsProperty);
|
this.useEffectsProperty.set(useEffectsProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisplaySecurityDepositInfo(Boolean displaySecurityDepositInfo) {
|
public void setDisplaySecurityDepositInfo(boolean displaySecurityDepositInfo) {
|
||||||
this.displaySecurityDepositInfo = displaySecurityDepositInfo;
|
this.displaySecurityDepositInfo = displaySecurityDepositInfo;
|
||||||
storage.queueUpForSave();
|
storage.queueUpForSave();
|
||||||
}
|
}
|
||||||
@ -133,7 +133,7 @@ public class Preferences implements Serializable {
|
|||||||
return useAnimationsProperty.get();
|
return useAnimationsProperty.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getDisplaySecurityDepositInfo() {
|
public boolean getDisplaySecurityDepositInfo() {
|
||||||
return displaySecurityDepositInfo;
|
return displaySecurityDepositInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
|
|
||||||
package io.bitsquare.util;
|
package io.bitsquare.util;
|
||||||
|
|
||||||
|
import io.bitsquare.common.handlers.ResultHandler;
|
||||||
|
|
||||||
|
import org.bitcoinj.utils.Threading;
|
||||||
|
|
||||||
import com.google.gson.FieldNamingPolicy;
|
import com.google.gson.FieldNamingPolicy;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
@ -36,6 +40,9 @@ import java.io.Serializable;
|
|||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -47,6 +54,31 @@ public class Utilities {
|
|||||||
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
|
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
|
||||||
private static long lastTimeStamp = System.currentTimeMillis();
|
private static long lastTimeStamp = System.currentTimeMillis();
|
||||||
|
|
||||||
|
|
||||||
|
public static Timer setTimeout(long delay, ResultHandler handler) {
|
||||||
|
Timer timer = new Timer();
|
||||||
|
TimerTask task = new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Threading.USER_THREAD.execute(() -> handler.handleResult());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
timer.schedule(task, delay);
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Timer setInterval(long delay, ResultHandler handler) {
|
||||||
|
Timer timer = new Timer();
|
||||||
|
TimerTask task = new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Threading.USER_THREAD.execute(() -> handler.handleResult());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
timer.scheduleAtFixedRate(task, delay, delay);
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
public static String objectToJson(Object object) {
|
public static String objectToJson(Object object) {
|
||||||
Gson gson =
|
Gson gson =
|
||||||
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
|
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
|
||||||
|
@ -158,7 +158,7 @@ public class BitsquareApp extends Application {
|
|||||||
primaryStage.show();
|
primaryStage.show();
|
||||||
|
|
||||||
//TODO just temp.
|
//TODO just temp.
|
||||||
//showDebugWindow();
|
// showDebugWindow();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
showErrorPopup(throwable, true);
|
showErrorPopup(throwable, true);
|
||||||
}
|
}
|
||||||
@ -174,9 +174,8 @@ public class BitsquareApp extends Application {
|
|||||||
throwable.printStackTrace();
|
throwable.printStackTrace();
|
||||||
Dialogs.create()
|
Dialogs.create()
|
||||||
.owner(primaryStage)
|
.owner(primaryStage)
|
||||||
.title("")
|
.title("Error")
|
||||||
.message("")
|
.message("A fatal exception occurred at startup.")
|
||||||
.masthead("")
|
|
||||||
.showException(throwable);
|
.showException(throwable);
|
||||||
if (doShutDown)
|
if (doShutDown)
|
||||||
stop();
|
stop();
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
package io.bitsquare.app;
|
package io.bitsquare.app;
|
||||||
|
|
||||||
import io.bitsquare.gui.util.GUIUtil;
|
import io.bitsquare.util.Utilities;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
@ -26,8 +26,8 @@ import java.io.File;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Timer;
|
||||||
|
|
||||||
import javafx.animation.AnimationTimer;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ public class UpdateProcess {
|
|||||||
|
|
||||||
protected String errorMessage;
|
protected String errorMessage;
|
||||||
protected final Subject<State, State> process = BehaviorSubject.create();
|
protected final Subject<State, State> process = BehaviorSubject.create();
|
||||||
protected AnimationTimer timeoutTimer;
|
protected Timer timeoutTimer;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UpdateProcess(Environment environment) {
|
public UpdateProcess(Environment environment) {
|
||||||
@ -88,11 +88,7 @@ public class UpdateProcess {
|
|||||||
log.info("UpdateFX current version " + Version.PATCH_VERSION);
|
log.info("UpdateFX current version " + Version.PATCH_VERSION);
|
||||||
|
|
||||||
// process.timeout() will cause an error state back but we don't want to break startup in case of an timeout
|
// process.timeout() will cause an error state back but we don't want to break startup in case of an timeout
|
||||||
timeoutTimer = GUIUtil.setTimeout(10000, animationTimer -> {
|
timeoutTimer = Utilities.setTimeout(10000, () -> process.onCompleted());
|
||||||
process.onCompleted();
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
timeoutTimer.start();
|
|
||||||
|
|
||||||
String agent = environment.getProperty(BitsquareEnvironment.APP_NAME_KEY) + Version.VERSION;
|
String agent = environment.getProperty(BitsquareEnvironment.APP_NAME_KEY) + Version.VERSION;
|
||||||
Path dataDirPath = new File(environment.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY)).toPath();
|
Path dataDirPath = new File(environment.getProperty(BitsquareEnvironment.APP_DATA_DIR_KEY)).toPath();
|
||||||
@ -123,12 +119,12 @@ public class UpdateProcess {
|
|||||||
state.set(State.UPDATE_AVAILABLE);
|
state.set(State.UPDATE_AVAILABLE);
|
||||||
// We stop the timeout and treat it not completed.
|
// We stop the timeout and treat it not completed.
|
||||||
// The user should click the restart button manually if there are updates available.
|
// The user should click the restart button manually if there are updates available.
|
||||||
timeoutTimer.stop();
|
timeoutTimer.cancel();
|
||||||
}
|
}
|
||||||
else if (summary.highestVersion == Version.PATCH_VERSION) {
|
else if (summary.highestVersion == Version.PATCH_VERSION) {
|
||||||
log.info("UP_TO_DATE");
|
log.info("UP_TO_DATE");
|
||||||
state.set(State.UP_TO_DATE);
|
state.set(State.UP_TO_DATE);
|
||||||
timeoutTimer.stop();
|
timeoutTimer.cancel();
|
||||||
process.onCompleted();
|
process.onCompleted();
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
@ -138,7 +134,7 @@ public class UpdateProcess {
|
|||||||
// so we use state.onCompleted() instead of state.onError()
|
// so we use state.onCompleted() instead of state.onError()
|
||||||
errorMessage = "Exception at processing UpdateSummary: " + e.getMessage();
|
errorMessage = "Exception at processing UpdateSummary: " + e.getMessage();
|
||||||
state.set(State.FAILURE);
|
state.set(State.FAILURE);
|
||||||
timeoutTimer.stop();
|
timeoutTimer.cancel();
|
||||||
process.onCompleted();
|
process.onCompleted();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -150,7 +146,7 @@ public class UpdateProcess {
|
|||||||
// so we use state.onCompleted() instead of state.onError()
|
// so we use state.onCompleted() instead of state.onError()
|
||||||
errorMessage = "Update failed: " + updater.getException();
|
errorMessage = "Update failed: " + updater.getException();
|
||||||
state.set(State.FAILURE);
|
state.set(State.FAILURE);
|
||||||
timeoutTimer.stop();
|
timeoutTimer.cancel();
|
||||||
process.onCompleted();
|
process.onCompleted();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ public class InputTextField extends TextField {
|
|||||||
|
|
||||||
private InputValidator validator;
|
private InputValidator validator;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Static
|
// Static
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -72,6 +73,8 @@ public class InputTextField extends TextField {
|
|||||||
if (errorMessageDisplay != null)
|
if (errorMessageDisplay != null)
|
||||||
errorMessageDisplay.hide();
|
errorMessageDisplay.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -34,13 +34,11 @@ import io.bitsquare.trade.Trade;
|
|||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
import io.bitsquare.util.Utilities;
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
@ -345,7 +343,7 @@ class MainViewModel implements ViewModel {
|
|||||||
if (numPendingTrades > 0)
|
if (numPendingTrades > 0)
|
||||||
numPendingTradesAsString.set(String.valueOf(numPendingTrades));
|
numPendingTradesAsString.set(String.valueOf(numPendingTrades));
|
||||||
if (numPendingTrades > 9)
|
if (numPendingTrades > 9)
|
||||||
numPendingTradesAsString.set("*");
|
numPendingTradesAsString.set("-");
|
||||||
|
|
||||||
showPendingTradesNotification.set(numPendingTrades > 0);
|
showPendingTradesNotification.set(numPendingTrades > 0);
|
||||||
}
|
}
|
||||||
@ -371,18 +369,11 @@ class MainViewModel implements ViewModel {
|
|||||||
log.trace("startBlockchainSyncTimeout");
|
log.trace("startBlockchainSyncTimeout");
|
||||||
stopBlockchainSyncTimeout();
|
stopBlockchainSyncTimeout();
|
||||||
|
|
||||||
blockchainSyncTimeoutTimer = new Timer();
|
|
||||||
TimerTask task = new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Threading.USER_THREAD.execute(() -> {
|
|
||||||
log.trace("Timeout reached");
|
|
||||||
Platform.runLater(() -> setWalletServiceException(new TimeoutException()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
blockchainSyncTimeoutTimer.schedule(task, BLOCKCHAIN_SYNC_TIMEOUT);
|
blockchainSyncTimeoutTimer = Utilities.setTimeout(BLOCKCHAIN_SYNC_TIMEOUT, () -> {
|
||||||
|
log.trace("Timeout reached");
|
||||||
|
setWalletServiceException(new TimeoutException());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopBlockchainSyncTimeout() {
|
private void stopBlockchainSyncTimeout() {
|
||||||
|
@ -44,39 +44,11 @@
|
|||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|
||||||
|
|
||||||
<!-- <Label text="P2P network connection:" GridPane.rowIndex="1"/>
|
|
||||||
<TextField fx:id="connectionType" GridPane.rowIndex="1" GridPane.columnIndex="1"
|
|
||||||
mouseTransparent="true" focusTraversable="false"/>
|
|
||||||
|
|
||||||
<Label text="My external visible P2P network address:" GridPane.rowIndex="2"/>
|
|
||||||
<TextField fx:id="nodeAddress" GridPane.rowIndex="2" GridPane.columnIndex="1"
|
|
||||||
mouseTransparent="true" focusTraversable="false"/>
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
<Label text="Intercept before run?:" GridPane.rowIndex="3">
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets bottom="-15"/>
|
|
||||||
</GridPane.margin>
|
|
||||||
</Label>
|
|
||||||
<CheckBox fx:id="interceptBeforeCheckBox" onAction="#onCheckBoxChanged" GridPane.rowIndex="3" GridPane.columnIndex="1">
|
|
||||||
<GridPane.margin>
|
|
||||||
<Insets bottom="-15"/>
|
|
||||||
</GridPane.margin>
|
|
||||||
</CheckBox>
|
|
||||||
|
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="200.0"/>
|
<ColumnConstraints hgrow="SOMETIMES" halignment="RIGHT" minWidth="200.0"/>
|
||||||
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
<ColumnConstraints hgrow="ALWAYS" minWidth="300.0"/>
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
|
|
||||||
<rowConstraints>
|
|
||||||
<RowConstraints vgrow="NEVER"/>
|
|
||||||
<RowConstraints vgrow="NEVER"/>
|
|
||||||
<RowConstraints vgrow="NEVER"/>
|
|
||||||
</rowConstraints>
|
|
||||||
|
|
||||||
</GridPane>
|
</GridPane>
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import io.bitsquare.common.taskrunner.Task;
|
|||||||
import io.bitsquare.gui.common.view.FxmlView;
|
import io.bitsquare.gui.common.view.FxmlView;
|
||||||
import io.bitsquare.gui.common.view.InitializableView;
|
import io.bitsquare.gui.common.view.InitializableView;
|
||||||
import io.bitsquare.trade.protocol.availability.OfferAvailabilityProtocol;
|
import io.bitsquare.trade.protocol.availability.OfferAvailabilityProtocol;
|
||||||
|
import io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress;
|
||||||
import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse;
|
import io.bitsquare.trade.protocol.availability.tasks.ProcessOfferAvailabilityResponse;
|
||||||
import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest;
|
import io.bitsquare.trade.protocol.availability.tasks.SendOfferAvailabilityRequest;
|
||||||
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
|
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
|
||||||
@ -32,6 +33,7 @@ import io.bitsquare.trade.protocol.trade.BuyerAsOffererProtocol;
|
|||||||
import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol;
|
import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateDepositTxInputs;
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateDepositTxInputs;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessDepositTxInputsRequest;
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessDepositTxInputsRequest;
|
||||||
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessFinalizePayoutTxRequest;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPublishDepositTxRequest;
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPublishDepositTxRequest;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendDepositTxPublishedMessage;
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendDepositTxPublishedMessage;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendFiatTransferStartedMessage;
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendFiatTransferStartedMessage;
|
||||||
@ -39,16 +41,23 @@ import io.bitsquare.trade.protocol.trade.tasks.buyer.SendPayDepositRequest;
|
|||||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendPayoutTxFinalizedMessage;
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.SendPayoutTxFinalizedMessage;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SignAndFinalizePayoutTx;
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.SignAndFinalizePayoutTx;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.buyer.SignAndPublishDepositTx;
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.SignAndPublishDepositTx;
|
||||||
|
import io.bitsquare.trade.protocol.trade.tasks.buyer.VerifyAndSignContract;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakeOfferFeePayment;
|
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakeOfferFeePayment;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakerAccount;
|
import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakerAccount;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.seller.CommitDepositTx;
|
import io.bitsquare.trade.protocol.trade.tasks.seller.CommitDepositTx;
|
||||||
|
import io.bitsquare.trade.protocol.trade.tasks.seller.CreateAndSignContract;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.seller.CreateAndSignDepositTx;
|
import io.bitsquare.trade.protocol.trade.tasks.seller.CreateAndSignDepositTx;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessDepositTxPublishedMessage;
|
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessDepositTxPublishedMessage;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessFiatTransferStartedMessage;
|
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessFiatTransferStartedMessage;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessPayDepositRequest;
|
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessPayDepositRequest;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessPayoutTxFinalizedMessage;
|
import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessPayoutTxFinalizedMessage;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.seller.SendDepositTxInputsRequest;
|
import io.bitsquare.trade.protocol.trade.tasks.seller.SendDepositTxInputsRequest;
|
||||||
|
import io.bitsquare.trade.protocol.trade.tasks.seller.SendFinalizePayoutTxRequest;
|
||||||
|
import io.bitsquare.trade.protocol.trade.tasks.seller.SendPublishDepositTxRequest;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.seller.SignPayoutTx;
|
import io.bitsquare.trade.protocol.trade.tasks.seller.SignPayoutTx;
|
||||||
|
import io.bitsquare.trade.protocol.trade.tasks.shared.CommitPayoutTx;
|
||||||
|
import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener;
|
||||||
|
import io.bitsquare.trade.protocol.trade.tasks.taker.BroadcastTakeOfferFeeTx;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.taker.CreateTakeOfferFeeTx;
|
import io.bitsquare.trade.protocol.trade.tasks.taker.CreateTakeOfferFeeTx;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOfferFeePayment;
|
import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOfferFeePayment;
|
||||||
import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOffererAccount;
|
import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOffererAccount;
|
||||||
@ -68,7 +77,6 @@ public class DebugView extends InitializableView {
|
|||||||
|
|
||||||
|
|
||||||
@FXML ComboBox<Class> taskComboBox;
|
@FXML ComboBox<Class> taskComboBox;
|
||||||
@FXML CheckBox interceptBeforeCheckBox;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public DebugView() {
|
public DebugView() {
|
||||||
@ -76,12 +84,10 @@ public class DebugView extends InitializableView {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
interceptBeforeCheckBox.setSelected(true);
|
|
||||||
|
|
||||||
final ObservableList<Class> items = FXCollections.observableArrayList(Arrays.asList(
|
final ObservableList<Class> items = FXCollections.observableArrayList(Arrays.asList(
|
||||||
/*---- Protocol ----*/
|
/*---- Protocol ----*/
|
||||||
OfferAvailabilityProtocol.class,
|
OfferAvailabilityProtocol.class,
|
||||||
io.bitsquare.trade.protocol.availability.tasks.GetPeerAddress.class,
|
GetPeerAddress.class,
|
||||||
SendOfferAvailabilityRequest.class,
|
SendOfferAvailabilityRequest.class,
|
||||||
ProcessOfferAvailabilityResponse.class,
|
ProcessOfferAvailabilityResponse.class,
|
||||||
Boolean.class, /* used as seperator*/
|
Boolean.class, /* used as seperator*/
|
||||||
@ -104,34 +110,45 @@ public class DebugView extends InitializableView {
|
|||||||
|
|
||||||
ProcessPublishDepositTxRequest.class,
|
ProcessPublishDepositTxRequest.class,
|
||||||
VerifyTakerAccount.class,
|
VerifyTakerAccount.class,
|
||||||
|
VerifyAndSignContract.class,
|
||||||
SignAndPublishDepositTx.class,
|
SignAndPublishDepositTx.class,
|
||||||
SendDepositTxPublishedMessage.class,
|
SendDepositTxPublishedMessage.class,
|
||||||
|
|
||||||
SignPayoutTx.class,
|
|
||||||
VerifyTakeOfferFeePayment.class,
|
VerifyTakeOfferFeePayment.class,
|
||||||
SendFiatTransferStartedMessage.class,
|
SendFiatTransferStartedMessage.class,
|
||||||
|
|
||||||
ProcessPayoutTxFinalizedMessage.class,
|
ProcessFinalizePayoutTxRequest.class,
|
||||||
|
SignAndFinalizePayoutTx.class,
|
||||||
|
CommitPayoutTx.class,
|
||||||
|
SendPayoutTxFinalizedMessage.class,
|
||||||
|
SetupPayoutTxLockTimeReachedListener.class,
|
||||||
Boolean.class, /* used as seperator*/
|
Boolean.class, /* used as seperator*/
|
||||||
|
|
||||||
|
|
||||||
/*---- Protocol ----*/
|
/*---- Protocol ----*/
|
||||||
SellerAsTakerProtocol.class,
|
SellerAsTakerProtocol.class,
|
||||||
CreateTakeOfferFeeTx.class,
|
CreateTakeOfferFeeTx.class,
|
||||||
|
BroadcastTakeOfferFeeTx.class,
|
||||||
SendDepositTxInputsRequest.class,
|
SendDepositTxInputsRequest.class,
|
||||||
|
|
||||||
ProcessPayDepositRequest.class,
|
ProcessPayDepositRequest.class,
|
||||||
VerifyOffererAccount.class,
|
VerifyOffererAccount.class,
|
||||||
|
CreateAndSignContract.class,
|
||||||
CreateAndSignDepositTx.class,
|
CreateAndSignDepositTx.class,
|
||||||
|
SendPublishDepositTxRequest.class,
|
||||||
|
|
||||||
ProcessDepositTxPublishedMessage.class,
|
ProcessDepositTxPublishedMessage.class,
|
||||||
CommitDepositTx.class,
|
CommitDepositTx.class,
|
||||||
|
|
||||||
ProcessFiatTransferStartedMessage.class,
|
ProcessFiatTransferStartedMessage.class,
|
||||||
|
|
||||||
SignAndFinalizePayoutTx.class,
|
|
||||||
VerifyOfferFeePayment.class,
|
VerifyOfferFeePayment.class,
|
||||||
SendPayoutTxFinalizedMessage.class
|
SignPayoutTx.class,
|
||||||
|
SendFinalizePayoutTxRequest.class,
|
||||||
|
|
||||||
|
ProcessPayoutTxFinalizedMessage.class,
|
||||||
|
CommitPayoutTx.class,
|
||||||
|
SetupPayoutTxLockTimeReachedListener.class
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -160,20 +177,8 @@ public class DebugView extends InitializableView {
|
|||||||
void onSelectTask() {
|
void onSelectTask() {
|
||||||
Class item = taskComboBox.getSelectionModel().getSelectedItem();
|
Class item = taskComboBox.getSelectionModel().getSelectedItem();
|
||||||
if (!item.getSimpleName().contains("Protocol")) {
|
if (!item.getSimpleName().contains("Protocol")) {
|
||||||
if (interceptBeforeCheckBox.isSelected()) {
|
Task.taskToIntercept = item;
|
||||||
Task.taskToInterceptBeforeRun = item;
|
|
||||||
Task.taskToInterceptAfterRun = null;
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
Task.taskToInterceptAfterRun = item;
|
|
||||||
Task.taskToInterceptBeforeRun = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
void onCheckBoxChanged() {
|
|
||||||
onSelectTask();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
package io.bitsquare.gui.main.offer.createoffer;
|
package io.bitsquare.gui.main.offer.createoffer;
|
||||||
|
|
||||||
import io.bitsquare.arbitration.Arbitrator;
|
import io.bitsquare.arbitration.Arbitrator;
|
||||||
import io.bitsquare.arbitration.ArbitratorService;
|
|
||||||
import io.bitsquare.btc.AddressEntry;
|
import io.bitsquare.btc.AddressEntry;
|
||||||
import io.bitsquare.btc.FeePolicy;
|
import io.bitsquare.btc.FeePolicy;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
@ -48,17 +47,15 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Domain for that UI element.
|
* Domain for that UI element.
|
||||||
* Note that the create offer domain has a deeper scope in the application domain (TradeManager).
|
* Note that the create offer domain has a deeper scope in the application domain (TradeManager).
|
||||||
@ -67,16 +64,22 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
class CreateOfferDataModel implements Activatable, DataModel {
|
class CreateOfferDataModel implements Activatable, DataModel {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CreateOfferDataModel.class);
|
private static final Logger log = LoggerFactory.getLogger(CreateOfferDataModel.class);
|
||||||
|
|
||||||
private OpenOfferManager openOfferManager;
|
|
||||||
|
private final OpenOfferManager openOfferManager;
|
||||||
private final WalletService walletService;
|
private final WalletService walletService;
|
||||||
private final AccountSettings accountSettings;
|
private final AccountSettings accountSettings;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
|
private final User user;
|
||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
|
|
||||||
private final String offerId;
|
private final String offerId;
|
||||||
|
private final AddressEntry addressEntry;
|
||||||
|
final ObjectProperty<Coin> offerFeeAsCoin = new SimpleObjectProperty<>();
|
||||||
|
final ObjectProperty<Coin> networkFeeAsCoin = new SimpleObjectProperty<>();
|
||||||
|
final ObjectProperty<Coin> securityDepositAsCoin = new SimpleObjectProperty<>();
|
||||||
|
private final BalanceListener balanceListener;
|
||||||
|
private final ChangeListener<FiatAccount> currentFiatAccountListener;
|
||||||
|
|
||||||
private Offer.Direction direction;
|
private Offer.Direction direction;
|
||||||
private AddressEntry addressEntry;
|
|
||||||
|
|
||||||
final StringProperty requestPlaceOfferErrorMessage = new SimpleStringProperty();
|
final StringProperty requestPlaceOfferErrorMessage = new SimpleStringProperty();
|
||||||
final StringProperty transactionId = new SimpleStringProperty();
|
final StringProperty transactionId = new SimpleStringProperty();
|
||||||
@ -95,57 +98,53 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||||||
final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
final ObjectProperty<Fiat> volumeAsFiat = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Coin> offerFeeAsCoin = new SimpleObjectProperty<>();
|
|
||||||
final ObjectProperty<Coin> networkFeeAsCoin = new SimpleObjectProperty<>();
|
|
||||||
final ObjectProperty<Coin> securityDepositAsCoin = new SimpleObjectProperty<>();
|
|
||||||
|
|
||||||
final ObservableList<Country> acceptedCountries = FXCollections.observableArrayList();
|
final ObservableList<Country> acceptedCountries = FXCollections.observableArrayList();
|
||||||
final ObservableList<String> acceptedLanguageCodes = FXCollections.observableArrayList();
|
final ObservableList<String> acceptedLanguageCodes = FXCollections.observableArrayList();
|
||||||
final ObservableList<Arbitrator> acceptedArbitrators = FXCollections.observableArrayList();
|
final ObservableList<Arbitrator> acceptedArbitrators = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// non private for testing
|
// non private for testing
|
||||||
@Inject
|
@Inject
|
||||||
public CreateOfferDataModel(OpenOfferManager openOfferManager, WalletService walletService, ArbitratorService arbitratorService,
|
public CreateOfferDataModel(OpenOfferManager openOfferManager, WalletService walletService,
|
||||||
AccountSettings accountSettings, Preferences preferences, User user, BSFormatter formatter) {
|
AccountSettings accountSettings, Preferences preferences, User user, BSFormatter formatter) {
|
||||||
this.openOfferManager = openOfferManager;
|
this.openOfferManager = openOfferManager;
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.accountSettings = accountSettings;
|
this.accountSettings = accountSettings;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
|
this.user = user;
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
this.offerId = UUID.randomUUID().toString();
|
this.offerId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
addressEntry = walletService.getAddressEntry(offerId);
|
||||||
|
|
||||||
offerFeeAsCoin.set(FeePolicy.CREATE_OFFER_FEE);
|
offerFeeAsCoin.set(FeePolicy.CREATE_OFFER_FEE);
|
||||||
networkFeeAsCoin.set(FeePolicy.TX_FEE);
|
networkFeeAsCoin.set(FeePolicy.TX_FEE);
|
||||||
|
|
||||||
if (walletService != null && walletService.getWallet() != null) {
|
// we need to set it here already as it is used before activate
|
||||||
addressEntry = walletService.getAddressEntry(offerId);
|
securityDepositAsCoin.set(accountSettings.getSecurityDeposit());
|
||||||
|
|
||||||
walletService.addBalanceListener(new BalanceListener(getAddressEntry().getAddress()) {
|
balanceListener = new BalanceListener(getAddressEntry().getAddress()) {
|
||||||
@Override
|
@Override
|
||||||
public void onBalanceChanged(@NotNull Coin balance) {
|
public void onBalanceChanged(@NotNull Coin balance) {
|
||||||
updateBalance(balance);
|
updateBalance(balance);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
updateBalance(walletService.getBalanceForAddress(getAddressEntry().getAddress()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user != null) {
|
currentFiatAccountListener = (observable, oldValue, newValue) -> {
|
||||||
user.currentFiatAccountProperty().addListener((ov, oldValue, newValue) -> applyBankAccount(newValue));
|
applyBankAccount(newValue);
|
||||||
|
};
|
||||||
applyBankAccount(user.currentFiatAccountProperty().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (accountSettings != null)
|
|
||||||
btcCode.bind(preferences.btcDenominationProperty());
|
|
||||||
|
|
||||||
// we need to set it here already as initWithData is called before activate
|
|
||||||
if (accountSettings != null)
|
|
||||||
securityDepositAsCoin.set(accountSettings.getSecurityDeposit());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activate() {
|
public void activate() {
|
||||||
|
addBindings();
|
||||||
|
addListeners();
|
||||||
|
|
||||||
// might be changed after screen change
|
// might be changed after screen change
|
||||||
if (accountSettings != null) {
|
if (accountSettings != null) {
|
||||||
// set it here again to cover the case of an securityDeposit change after a screen change
|
// set it here again to cover the case of an securityDeposit change after a screen change
|
||||||
@ -155,13 +154,50 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||||||
acceptedLanguageCodes.setAll(accountSettings.getAcceptedLanguageLocaleCodes());
|
acceptedLanguageCodes.setAll(accountSettings.getAcceptedLanguageLocaleCodes());
|
||||||
acceptedArbitrators.setAll(accountSettings.getAcceptedArbitrators());
|
acceptedArbitrators.setAll(accountSettings.getAcceptedArbitrators());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateBalance(walletService.getBalanceForAddress(getAddressEntry().getAddress()));
|
||||||
|
applyBankAccount(user.currentFiatAccountProperty().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deactivate() {
|
public void deactivate() {
|
||||||
// no-op
|
removeBindings();
|
||||||
|
removeListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addBindings() {
|
||||||
|
btcCode.bind(preferences.btcDenominationProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBindings() {
|
||||||
|
btcCode.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
|
walletService.addBalanceListener(balanceListener);
|
||||||
|
user.currentFiatAccountProperty().addListener(currentFiatAccountListener);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeListeners() {
|
||||||
|
walletService.removeBalanceListener(balanceListener);
|
||||||
|
user.currentFiatAccountProperty().removeListener(currentFiatAccountListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void initWithData(Offer.Direction direction) {
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI actions
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void onPlaceOffer() {
|
void onPlaceOffer() {
|
||||||
// data validation is done in the trade domain
|
// data validation is done in the trade domain
|
||||||
openOfferManager.onPlaceOffer(offerId,
|
openOfferManager.onPlaceOffer(offerId,
|
||||||
@ -177,47 +213,14 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateVolume() {
|
void onSecurityDepositInfoDisplayed() {
|
||||||
try {
|
preferences.setDisplaySecurityDepositInfo(false);
|
||||||
if (priceAsFiat.get() != null &&
|
|
||||||
amountAsCoin.get() != null &&
|
|
||||||
!amountAsCoin.get().isZero() &&
|
|
||||||
!priceAsFiat.get().isZero()) {
|
|
||||||
volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
// Should be never reached
|
|
||||||
log.error(t.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateAmount() {
|
|
||||||
try {
|
|
||||||
if (volumeAsFiat.get() != null &&
|
|
||||||
priceAsFiat.get() != null &&
|
|
||||||
!volumeAsFiat.get().isZero() &&
|
|
||||||
!priceAsFiat.get().isZero()) {
|
|
||||||
// If we got a btc value with more then 4 decimals we convert it to max 4 decimals
|
|
||||||
amountAsCoin.set(formatter.reduceTo4Decimals(new ExchangeRate(priceAsFiat.get()).fiatToCoin
|
|
||||||
(volumeAsFiat.get())));
|
|
||||||
|
|
||||||
calculateTotalToPay();
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
// Should be never reached
|
|
||||||
log.error(t.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void calculateTotalToPay() {
|
|
||||||
if (securityDepositAsCoin.get() != null) {
|
|
||||||
if (direction == Offer.Direction.BUY)
|
|
||||||
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()));
|
|
||||||
else
|
|
||||||
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()).add(amountAsCoin.get()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Getters
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
boolean isMinAmountLessOrEqualAmount() {
|
boolean isMinAmountLessOrEqualAmount() {
|
||||||
@ -227,12 +230,6 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void securityDepositInfoDisplayed() {
|
|
||||||
preferences.setDisplaySecurityDepositInfo(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Offer.Direction getDirection() {
|
Offer.Direction getDirection() {
|
||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
@ -245,12 +242,51 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||||||
return offerId;
|
return offerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBalance(@NotNull Coin balance) {
|
AddressEntry getAddressEntry() {
|
||||||
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
return addressEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressEntry getAddressEntry() {
|
boolean getDisplaySecurityDepositInfo() {
|
||||||
return addressEntry;
|
return preferences.getDisplaySecurityDepositInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void calculateVolume() {
|
||||||
|
if (priceAsFiat.get() != null &&
|
||||||
|
amountAsCoin.get() != null &&
|
||||||
|
!amountAsCoin.get().isZero() &&
|
||||||
|
!priceAsFiat.get().isZero()) {
|
||||||
|
volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void calculateAmount() {
|
||||||
|
if (volumeAsFiat.get() != null &&
|
||||||
|
priceAsFiat.get() != null &&
|
||||||
|
!volumeAsFiat.get().isZero() &&
|
||||||
|
!priceAsFiat.get().isZero()) {
|
||||||
|
// If we got a btc value with more then 4 decimals we convert it to max 4 decimals
|
||||||
|
amountAsCoin.set(formatter.reduceTo4Decimals(new ExchangeRate(priceAsFiat.get()).fiatToCoin(volumeAsFiat.get())));
|
||||||
|
|
||||||
|
calculateTotalToPay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void calculateTotalToPay() {
|
||||||
|
if (securityDepositAsCoin.get() != null) {
|
||||||
|
if (direction == Offer.Direction.BUY)
|
||||||
|
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()));
|
||||||
|
else
|
||||||
|
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()).add(amountAsCoin.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBalance(Coin balance) {
|
||||||
|
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyBankAccount(FiatAccount fiatAccount) {
|
private void applyBankAccount(FiatAccount fiatAccount) {
|
||||||
@ -262,13 +298,4 @@ class CreateOfferDataModel implements Activatable, DataModel {
|
|||||||
fiatCode.set(fiatAccount.currencyCode);
|
fiatCode.set(fiatAccount.currencyCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getDisplaySecurityDepositInfo() {
|
|
||||||
return preferences.getDisplaySecurityDepositInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initWithData(Offer.Direction direction, Coin amount, Fiat price) {
|
|
||||||
checkNotNull(direction);
|
|
||||||
this.direction = direction;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
<AnchorPane fx:id="root" fx:controller="io.bitsquare.gui.main.offer.createoffer.CreateOfferView"
|
<AnchorPane fx:id="root" fx:controller="io.bitsquare.gui.main.offer.createoffer.CreateOfferView"
|
||||||
xmlns:fx="http://javafx.com/fxml">
|
xmlns:fx="http://javafx.com/fxml">
|
||||||
|
|
||||||
<ScrollPane fx:id="scrollPane" hbarPolicy="NEVER" vbarPolicy="NEVER" fitToWidth="true" fitToHeight="true"
|
<ScrollPane fx:id="scrollPane" onScroll="#onScroll" hbarPolicy="NEVER" vbarPolicy="NEVER" fitToWidth="true" fitToHeight="true"
|
||||||
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||||
AnchorPane.bottomAnchor="0.0">
|
AnchorPane.bottomAnchor="0.0">
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.HPos;
|
import javafx.geometry.HPos;
|
||||||
@ -75,6 +76,9 @@ import static javafx.beans.binding.Bindings.createStringBinding;
|
|||||||
@FxmlView
|
@FxmlView
|
||||||
public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateOfferViewModel> {
|
public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateOfferViewModel> {
|
||||||
|
|
||||||
|
private final Navigation navigation;
|
||||||
|
private final OverlayManager overlayManager;
|
||||||
|
|
||||||
@FXML ScrollPane scrollPane;
|
@FXML ScrollPane scrollPane;
|
||||||
@FXML ImageView imageView;
|
@FXML ImageView imageView;
|
||||||
@FXML AddressTextField addressTextField;
|
@FXML AddressTextField addressTextField;
|
||||||
@ -100,32 +104,258 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||||||
private boolean detailsVisible;
|
private boolean detailsVisible;
|
||||||
private boolean advancedScreenInited;
|
private boolean advancedScreenInited;
|
||||||
|
|
||||||
private final Navigation navigation;
|
|
||||||
private final OverlayManager overlayManager;
|
|
||||||
private OfferView.CloseHandler closeHandler;
|
private OfferView.CloseHandler closeHandler;
|
||||||
|
|
||||||
|
private ChangeListener<Boolean> amountFocusedListener;
|
||||||
|
private ChangeListener<Boolean> minAmountFocusedListener;
|
||||||
|
private ChangeListener<Boolean> priceFocusedListener;
|
||||||
|
private ChangeListener<Boolean> volumeFocusedListener;
|
||||||
|
private ChangeListener<Boolean> showWarningInvalidBtcDecimalPlacesListener;
|
||||||
|
private ChangeListener<Boolean> showWarningInvalidFiatDecimalPlacesPlacesListener;
|
||||||
|
private ChangeListener<Boolean> showWarningAdjustedVolumeListener;
|
||||||
|
private ChangeListener<String> requestPlaceOfferErrorMessageListener;
|
||||||
|
private ChangeListener<Boolean> isPlaceOfferSpinnerVisibleListener;
|
||||||
|
private ChangeListener<Boolean> showTransactionPublishedScreen;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private CreateOfferView(CreateOfferViewModel model, Navigation navigation,
|
private CreateOfferView(CreateOfferViewModel model, Navigation navigation,
|
||||||
OverlayManager overlayManager) {
|
OverlayManager overlayManager) {
|
||||||
super(model);
|
super(model);
|
||||||
|
|
||||||
this.navigation = navigation;
|
this.navigation = navigation;
|
||||||
this.overlayManager = overlayManager;
|
this.overlayManager = overlayManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initialize() {
|
protected void initialize() {
|
||||||
setupListeners();
|
createListeners();
|
||||||
setupBindings();
|
|
||||||
|
|
||||||
balanceTextField.setup(model.getWalletService(), model.address.get(), model.getFormatter());
|
balanceTextField.setup(model.getWalletService(), model.address.get(), model.getFormatter());
|
||||||
volumeTextField.setPromptText(BSResources.get("createOffer.volume.prompt", model.fiatCode.get()));
|
volumeTextField.setPromptText(BSResources.get("createOffer.volume.prompt", model.fiatCode.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doDeactivate() {
|
protected void doActivate() {
|
||||||
|
addBindings();
|
||||||
|
addListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDeactivate() {
|
||||||
|
removeBindings();
|
||||||
|
removeListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBindings() {
|
||||||
|
amountBtcLabel.textProperty().bind(model.btcCode);
|
||||||
|
priceFiatLabel.textProperty().bind(model.fiatCode);
|
||||||
|
volumeFiatLabel.textProperty().bind(model.fiatCode);
|
||||||
|
minAmountBtcLabel.textProperty().bind(model.btcCode);
|
||||||
|
|
||||||
|
priceDescriptionLabel.textProperty().bind(createStringBinding(() ->
|
||||||
|
BSResources.get("createOffer.amountPriceBox.priceDescription", model.fiatCode.get()), model.fiatCode));
|
||||||
|
|
||||||
|
volumeDescriptionLabel.textProperty().bind(createStringBinding(() -> model.volumeDescriptionLabel.get(), model.fiatCode, model.volumeDescriptionLabel));
|
||||||
|
|
||||||
|
buyLabel.textProperty().bind(model.directionLabel);
|
||||||
|
amountToTradeLabel.textProperty().bind(model.amountToTradeLabel);
|
||||||
|
amountTextField.textProperty().bindBidirectional(model.amount);
|
||||||
|
minAmountTextField.textProperty().bindBidirectional(model.minAmount);
|
||||||
|
priceTextField.textProperty().bindBidirectional(model.price);
|
||||||
|
volumeTextField.textProperty().bindBidirectional(model.volume);
|
||||||
|
amountPriceBoxInfo.textProperty().bind(model.amountPriceBoxInfo);
|
||||||
|
|
||||||
|
totalToPayTextField.textProperty().bind(model.totalToPay);
|
||||||
|
|
||||||
|
addressTextField.amountAsCoinProperty().bind(model.totalToPayAsCoin);
|
||||||
|
addressTextField.paymentLabelProperty().bind(model.paymentLabel);
|
||||||
|
addressTextField.addressProperty().bind(model.addressAsString);
|
||||||
|
|
||||||
|
bankAccountTypeTextField.textProperty().bind(model.bankAccountType);
|
||||||
|
bankAccountCurrencyTextField.textProperty().bind(model.bankAccountCurrency);
|
||||||
|
bankAccountCountyTextField.textProperty().bind(model.bankAccountCounty);
|
||||||
|
|
||||||
|
acceptedCountriesTextField.textProperty().bind(model.acceptedCountries);
|
||||||
|
acceptedLanguagesTextField.textProperty().bind(model.acceptedLanguages);
|
||||||
|
acceptedArbitratorsTextField.textProperty().bind(model.acceptedArbitrators);
|
||||||
|
|
||||||
|
// Validation
|
||||||
|
amountTextField.validationResultProperty().bind(model.amountValidationResult);
|
||||||
|
minAmountTextField.validationResultProperty().bind(model.minAmountValidationResult);
|
||||||
|
priceTextField.validationResultProperty().bind(model.priceValidationResult);
|
||||||
|
volumeTextField.validationResultProperty().bind(model.volumeValidationResult);
|
||||||
|
|
||||||
|
// buttons
|
||||||
|
placeOfferButton.visibleProperty().bind(model.isPlaceOfferButtonVisible);
|
||||||
|
placeOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled);
|
||||||
|
|
||||||
|
placeOfferSpinnerInfoLabel.visibleProperty().bind(model.isPlaceOfferSpinnerVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBindings() {
|
||||||
|
amountBtcLabel.textProperty().unbind();
|
||||||
|
priceFiatLabel.textProperty().unbind();
|
||||||
|
volumeFiatLabel.textProperty().unbind();
|
||||||
|
minAmountBtcLabel.textProperty().unbind();
|
||||||
|
priceDescriptionLabel.textProperty().unbind();
|
||||||
|
volumeDescriptionLabel.textProperty().unbind();
|
||||||
|
buyLabel.textProperty().unbind();
|
||||||
|
amountToTradeLabel.textProperty().unbind();
|
||||||
|
amountTextField.textProperty().unbindBidirectional(model.amount);
|
||||||
|
minAmountTextField.textProperty().unbindBidirectional(model.minAmount);
|
||||||
|
priceTextField.textProperty().unbindBidirectional(model.price);
|
||||||
|
volumeTextField.textProperty().unbindBidirectional(model.volume);
|
||||||
|
amountPriceBoxInfo.textProperty().unbind();
|
||||||
|
totalToPayTextField.textProperty().unbind();
|
||||||
|
addressTextField.amountAsCoinProperty().unbind();
|
||||||
|
addressTextField.paymentLabelProperty().unbind();
|
||||||
|
addressTextField.addressProperty().unbind();
|
||||||
|
bankAccountTypeTextField.textProperty().unbind();
|
||||||
|
bankAccountCurrencyTextField.textProperty().unbind();
|
||||||
|
bankAccountCountyTextField.textProperty().unbind();
|
||||||
|
acceptedCountriesTextField.textProperty().unbind();
|
||||||
|
acceptedLanguagesTextField.textProperty().unbind();
|
||||||
|
acceptedArbitratorsTextField.textProperty().unbind();
|
||||||
|
amountTextField.validationResultProperty().unbind();
|
||||||
|
minAmountTextField.validationResultProperty().unbind();
|
||||||
|
priceTextField.validationResultProperty().unbind();
|
||||||
|
volumeTextField.validationResultProperty().unbind();
|
||||||
|
placeOfferButton.visibleProperty().unbind();
|
||||||
|
placeOfferButton.disableProperty().unbind();
|
||||||
|
placeOfferSpinnerInfoLabel.visibleProperty().unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createListeners() {
|
||||||
|
amountFocusedListener = (o, oldValue, newValue) -> {
|
||||||
|
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
||||||
|
amountTextField.setText(model.amount.get());
|
||||||
|
};
|
||||||
|
minAmountFocusedListener = (o, oldValue, newValue) -> {
|
||||||
|
model.onFocusOutMinAmountTextField(oldValue, newValue, minAmountTextField.getText());
|
||||||
|
minAmountTextField.setText(model.minAmount.get());
|
||||||
|
};
|
||||||
|
priceFocusedListener = (o, oldValue, newValue) -> {
|
||||||
|
model.onFocusOutPriceTextField(oldValue, newValue, priceTextField.getText());
|
||||||
|
priceTextField.setText(model.price.get());
|
||||||
|
};
|
||||||
|
volumeFocusedListener = (o, oldValue, newValue) -> {
|
||||||
|
model.onFocusOutVolumeTextField(oldValue, newValue, volumeTextField.getText());
|
||||||
|
volumeTextField.setText(model.volume.get());
|
||||||
|
};
|
||||||
|
showWarningInvalidBtcDecimalPlacesListener = (o, oldValue, newValue) -> {
|
||||||
|
if (newValue) {
|
||||||
|
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.invalidBtcDecimalPlaces"));
|
||||||
|
model.showWarningInvalidBtcDecimalPlaces.set(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
showWarningInvalidFiatDecimalPlacesPlacesListener = (o, oldValue, newValue) -> {
|
||||||
|
if (newValue) {
|
||||||
|
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.invalidFiatDecimalPlaces"));
|
||||||
|
model.showWarningInvalidFiatDecimalPlaces.set(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
showWarningAdjustedVolumeListener = (o, oldValue, newValue) -> {
|
||||||
|
if (newValue) {
|
||||||
|
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.adjustedVolume"));
|
||||||
|
model.showWarningAdjustedVolume.set(false);
|
||||||
|
volumeTextField.setText(model.volume.get());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
requestPlaceOfferErrorMessageListener = (o, oldValue, newValue) -> {
|
||||||
|
if (newValue != null) {
|
||||||
|
Popups.openErrorPopup(BSResources.get("shared.error"), BSResources.get("createOffer.amountPriceBox.error.message",
|
||||||
|
model.requestPlaceOfferErrorMessage.get()));
|
||||||
|
Popups.removeBlurContent();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
isPlaceOfferSpinnerVisibleListener = (ov, oldValue, newValue) -> {
|
||||||
|
placeOfferSpinner.setProgress(newValue ? -1 : 0);
|
||||||
|
placeOfferSpinner.setVisible(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
showTransactionPublishedScreen = (o, oldValue, newValue) -> {
|
||||||
|
// TODO temp just for testing
|
||||||
|
newValue = false;
|
||||||
|
close();
|
||||||
|
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class);
|
||||||
|
|
||||||
|
if (newValue) {
|
||||||
|
overlayManager.blurContent();
|
||||||
|
|
||||||
|
// Dialogs are a bit limited. There is no callback for the InformationDialog button click, so we added
|
||||||
|
// our own actions.
|
||||||
|
List<Action> actions = new ArrayList<>();
|
||||||
|
/* actions.add(new AbstractAction(BSResources.get("shared.copyTxId")) {
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent actionEvent) {
|
||||||
|
getProperties().put("type", "COPY");
|
||||||
|
Utilities.copyToClipboard(model.transactionId.get());
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent actionEvent) {
|
||||||
|
getProperties().put("type", "CLOSE");
|
||||||
|
try {
|
||||||
|
close();
|
||||||
|
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Popups.openInfoPopup(BSResources.get("createOffer.success.headline"),
|
||||||
|
BSResources.get("createOffer.success.info"),
|
||||||
|
actions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
|
// focus out
|
||||||
|
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
||||||
|
minAmountTextField.focusedProperty().addListener(minAmountFocusedListener);
|
||||||
|
priceTextField.focusedProperty().addListener(priceFocusedListener);
|
||||||
|
volumeTextField.focusedProperty().addListener(volumeFocusedListener);
|
||||||
|
|
||||||
|
// warnings
|
||||||
|
model.showWarningInvalidBtcDecimalPlaces.addListener(showWarningInvalidBtcDecimalPlacesListener);
|
||||||
|
model.showWarningInvalidFiatDecimalPlaces.addListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
|
||||||
|
model.showWarningAdjustedVolume.addListener(showWarningAdjustedVolumeListener);
|
||||||
|
model.requestPlaceOfferErrorMessage.addListener(requestPlaceOfferErrorMessageListener);
|
||||||
|
model.isPlaceOfferSpinnerVisible.addListener(isPlaceOfferSpinnerVisibleListener);
|
||||||
|
|
||||||
|
model.showTransactionPublishedScreen.addListener(showTransactionPublishedScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeListeners() {
|
||||||
|
// focus out
|
||||||
|
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
||||||
|
minAmountTextField.focusedProperty().removeListener(minAmountFocusedListener);
|
||||||
|
priceTextField.focusedProperty().removeListener(priceFocusedListener);
|
||||||
|
volumeTextField.focusedProperty().removeListener(volumeFocusedListener);
|
||||||
|
|
||||||
|
// warnings
|
||||||
|
model.showWarningInvalidBtcDecimalPlaces.removeListener(showWarningInvalidBtcDecimalPlacesListener);
|
||||||
|
model.showWarningInvalidFiatDecimalPlaces.removeListener(showWarningInvalidFiatDecimalPlacesPlacesListener);
|
||||||
|
model.showWarningAdjustedVolume.removeListener(showWarningAdjustedVolumeListener);
|
||||||
|
model.requestPlaceOfferErrorMessage.removeListener(requestPlaceOfferErrorMessageListener);
|
||||||
|
model.isPlaceOfferSpinnerVisible.removeListener(isPlaceOfferSpinnerVisibleListener);
|
||||||
|
|
||||||
|
model.showTransactionPublishedScreen.removeListener(showTransactionPublishedScreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void initWithData(Offer.Direction direction, Coin amount, Fiat price) {
|
public void initWithData(Offer.Direction direction, Coin amount, Fiat price) {
|
||||||
model.initWithData(direction, amount, price);
|
model.initWithData(direction, amount, price);
|
||||||
|
|
||||||
@ -139,11 +369,21 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||||||
this.closeHandler = closeHandler;
|
this.closeHandler = closeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI actions
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onPlaceOffer() {
|
void onPlaceOffer() {
|
||||||
model.onPlaceOffer();
|
model.onPlaceOffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void onScroll() {
|
||||||
|
InputTextField.hideErrorMessageDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onShowPayFundsScreen() {
|
void onShowPayFundsScreen() {
|
||||||
// TODO deactivate for testing the moment
|
// TODO deactivate for testing the moment
|
||||||
@ -226,6 +466,11 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||||||
Help.openWindow(HelpId.CREATE_OFFER_ADVANCED);
|
Help.openWindow(HelpId.CREATE_OFFER_ADVANCED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Navigation
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void openAccountSettings() {
|
private void openAccountSettings() {
|
||||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, RestrictionsView.class);
|
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, RestrictionsView.class);
|
||||||
}
|
}
|
||||||
@ -235,150 +480,10 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||||||
closeHandler.close();
|
closeHandler.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupListeners() {
|
|
||||||
scrollPane.setOnScroll(e -> InputTextField.hideErrorMessageDisplay());
|
|
||||||
|
|
||||||
// focus out
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
amountTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
// State
|
||||||
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
amountTextField.setText(model.amount.get());
|
|
||||||
});
|
|
||||||
|
|
||||||
minAmountTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
|
||||||
model.onFocusOutMinAmountTextField(oldValue, newValue, minAmountTextField.getText());
|
|
||||||
minAmountTextField.setText(model.minAmount.get());
|
|
||||||
});
|
|
||||||
|
|
||||||
priceTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
|
||||||
model.onFocusOutPriceTextField(oldValue, newValue, priceTextField.getText());
|
|
||||||
priceTextField.setText(model.price.get());
|
|
||||||
});
|
|
||||||
|
|
||||||
volumeTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
|
||||||
model.onFocusOutVolumeTextField(oldValue, newValue, volumeTextField.getText());
|
|
||||||
volumeTextField.setText(model.volume.get());
|
|
||||||
});
|
|
||||||
|
|
||||||
// warnings
|
|
||||||
model.showWarningInvalidBtcDecimalPlaces.addListener((o, oldValue, newValue) -> {
|
|
||||||
if (newValue) {
|
|
||||||
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.invalidBtcDecimalPlaces"));
|
|
||||||
model.showWarningInvalidBtcDecimalPlaces.set(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
model.showWarningInvalidFiatDecimalPlaces.addListener((o, oldValue, newValue) -> {
|
|
||||||
if (newValue) {
|
|
||||||
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.invalidFiatDecimalPlaces"));
|
|
||||||
model.showWarningInvalidFiatDecimalPlaces.set(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
model.showWarningAdjustedVolume.addListener((o, oldValue, newValue) -> {
|
|
||||||
if (newValue) {
|
|
||||||
Popups.openWarningPopup(BSResources.get("shared.warning"), BSResources.get("createOffer.amountPriceBox.warning.adjustedVolume"));
|
|
||||||
model.showWarningAdjustedVolume.set(false);
|
|
||||||
volumeTextField.setText(model.volume.get());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
model.requestPlaceOfferErrorMessage.addListener((o, oldValue, newValue) -> {
|
|
||||||
if (newValue != null) {
|
|
||||||
Popups.openErrorPopup(BSResources.get("shared.error"), BSResources.get("createOffer.amountPriceBox.error.message",
|
|
||||||
model.requestPlaceOfferErrorMessage.get()));
|
|
||||||
Popups.removeBlurContent();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
model.showTransactionPublishedScreen.addListener((o, oldValue, newValue) -> {
|
|
||||||
// TODO temp just for testing
|
|
||||||
newValue = false;
|
|
||||||
close();
|
|
||||||
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class);
|
|
||||||
|
|
||||||
if (newValue) {
|
|
||||||
overlayManager.blurContent();
|
|
||||||
|
|
||||||
// Dialogs are a bit limited. There is no callback for the InformationDialog button click, so we added
|
|
||||||
// our own actions.
|
|
||||||
List<Action> actions = new ArrayList<>();
|
|
||||||
/* actions.add(new AbstractAction(BSResources.get("shared.copyTxId")) {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent actionEvent) {
|
|
||||||
getProperties().put("type", "COPY");
|
|
||||||
Utilities.copyToClipboard(model.transactionId.get());
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent actionEvent) {
|
|
||||||
getProperties().put("type", "CLOSE");
|
|
||||||
try {
|
|
||||||
close();
|
|
||||||
navigation.navigateTo(MainView.class, PortfolioView.class, OpenOffersView.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Popups.openInfoPopup(BSResources.get("createOffer.success.headline"),
|
|
||||||
BSResources.get("createOffer.success.info"),
|
|
||||||
actions);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupBindings() {
|
|
||||||
amountBtcLabel.textProperty().bind(model.btcCode);
|
|
||||||
priceFiatLabel.textProperty().bind(model.fiatCode);
|
|
||||||
volumeFiatLabel.textProperty().bind(model.fiatCode);
|
|
||||||
minAmountBtcLabel.textProperty().bind(model.btcCode);
|
|
||||||
|
|
||||||
priceDescriptionLabel.textProperty().bind(createStringBinding(() ->
|
|
||||||
BSResources.get("createOffer.amountPriceBox.priceDescription", model.fiatCode.get()), model.fiatCode));
|
|
||||||
|
|
||||||
volumeDescriptionLabel.textProperty().bind(createStringBinding(() -> model.volumeDescriptionLabel.get(), model.fiatCode, model.volumeDescriptionLabel));
|
|
||||||
|
|
||||||
buyLabel.textProperty().bind(model.directionLabel);
|
|
||||||
amountToTradeLabel.textProperty().bind(model.amountToTradeLabel);
|
|
||||||
amountTextField.textProperty().bindBidirectional(model.amount);
|
|
||||||
minAmountTextField.textProperty().bindBidirectional(model.minAmount);
|
|
||||||
priceTextField.textProperty().bindBidirectional(model.price);
|
|
||||||
volumeTextField.textProperty().bindBidirectional(model.volume);
|
|
||||||
amountPriceBoxInfo.textProperty().bind(model.amountPriceBoxInfo);
|
|
||||||
|
|
||||||
totalToPayTextField.textProperty().bind(model.totalToPay);
|
|
||||||
|
|
||||||
addressTextField.amountAsCoinProperty().bind(model.totalToPayAsCoin);
|
|
||||||
addressTextField.paymentLabelProperty().bind(model.paymentLabel);
|
|
||||||
addressTextField.addressProperty().bind(model.addressAsString);
|
|
||||||
|
|
||||||
bankAccountTypeTextField.textProperty().bind(model.bankAccountType);
|
|
||||||
bankAccountCurrencyTextField.textProperty().bind(model.bankAccountCurrency);
|
|
||||||
bankAccountCountyTextField.textProperty().bind(model.bankAccountCounty);
|
|
||||||
|
|
||||||
acceptedCountriesTextField.textProperty().bind(model.acceptedCountries);
|
|
||||||
acceptedLanguagesTextField.textProperty().bind(model.acceptedLanguages);
|
|
||||||
acceptedArbitratorsTextField.textProperty().bind(model.acceptedArbitrators);
|
|
||||||
|
|
||||||
// Validation
|
|
||||||
amountTextField.validationResultProperty().bind(model.amountValidationResult);
|
|
||||||
minAmountTextField.validationResultProperty().bind(model.minAmountValidationResult);
|
|
||||||
priceTextField.validationResultProperty().bind(model.priceValidationResult);
|
|
||||||
volumeTextField.validationResultProperty().bind(model.volumeValidationResult);
|
|
||||||
|
|
||||||
// buttons
|
|
||||||
placeOfferButton.visibleProperty().bind(model.isPlaceOfferButtonVisible);
|
|
||||||
placeOfferButton.disableProperty().bind(model.isPlaceOfferButtonDisabled);
|
|
||||||
|
|
||||||
placeOfferSpinnerInfoLabel.visibleProperty().bind(model.isPlaceOfferSpinnerVisible);
|
|
||||||
|
|
||||||
model.isPlaceOfferSpinnerVisible.addListener((ov, oldValue, newValue) -> {
|
|
||||||
placeOfferSpinner.setProgress(newValue ? -1 : 0);
|
|
||||||
placeOfferSpinner.setVisible(newValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDetailsScreen() {
|
private void showDetailsScreen() {
|
||||||
payFundsPane.setInactive();
|
payFundsPane.setInactive();
|
||||||
@ -433,6 +538,11 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
|
|||||||
advancedInfoDisplay.setVisible(visible);
|
advancedInfoDisplay.setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void initEditIcons() {
|
private void initEditIcons() {
|
||||||
acceptedCountriesLabelIcon.setId("clickable-icon");
|
acceptedCountriesLabelIcon.setId("clickable-icon");
|
||||||
AwesomeDude.setIcon(acceptedCountriesLabelIcon, AwesomeIcon.EDIT_SIGN);
|
AwesomeDude.setIcon(acceptedCountriesLabelIcon, AwesomeIcon.EDIT_SIGN);
|
||||||
|
@ -33,6 +33,7 @@ import org.bitcoinj.utils.Fiat;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
@ -40,6 +41,7 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
|
||||||
import static javafx.beans.binding.Bindings.createStringBinding;
|
import static javafx.beans.binding.Bindings.createStringBinding;
|
||||||
|
|
||||||
@ -49,7 +51,6 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
private final FiatValidator fiatValidator;
|
private final FiatValidator fiatValidator;
|
||||||
|
|
||||||
|
|
||||||
final StringProperty amount = new SimpleStringProperty();
|
final StringProperty amount = new SimpleStringProperty();
|
||||||
final StringProperty minAmount = new SimpleStringProperty();
|
final StringProperty minAmount = new SimpleStringProperty();
|
||||||
final StringProperty price = new SimpleStringProperty();
|
final StringProperty price = new SimpleStringProperty();
|
||||||
@ -94,6 +95,25 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
|
private ChangeListener<String> amountListener;
|
||||||
|
private ChangeListener<String> minAmountListener;
|
||||||
|
private ChangeListener<String> priceListener;
|
||||||
|
private ChangeListener<String> volumeListener;
|
||||||
|
private ChangeListener<Coin> amountAsCoinListener;
|
||||||
|
private ChangeListener<Coin> minAmountAsCoinListener;
|
||||||
|
private ChangeListener<Fiat> priceAsFiatListener;
|
||||||
|
private ChangeListener<Fiat> volumeAsFiatListener;
|
||||||
|
private ChangeListener<Boolean> isWalletFundedListener;
|
||||||
|
private ChangeListener<Boolean> requestPlaceOfferSuccessListener;
|
||||||
|
private ChangeListener<String> requestPlaceOfferErrorMessageListener;
|
||||||
|
private InvalidationListener acceptedCountriesListener;
|
||||||
|
private InvalidationListener acceptedLanguageCodesListener;
|
||||||
|
private InvalidationListener acceptedArbitratorsListener;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CreateOfferViewModel(CreateOfferDataModel dataModel, FiatValidator fiatValidator, BtcValidator btcValidator,
|
public CreateOfferViewModel(CreateOfferDataModel dataModel, FiatValidator fiatValidator, BtcValidator btcValidator,
|
||||||
@ -110,234 +130,22 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||||||
addressAsString.set(dataModel.getAddressEntry().getAddress().toString());
|
addressAsString.set(dataModel.getAddressEntry().getAddress().toString());
|
||||||
address.set(dataModel.getAddressEntry().getAddress());
|
address.set(dataModel.getAddressEntry().getAddress());
|
||||||
}
|
}
|
||||||
|
createListeners();
|
||||||
setupBindings();
|
|
||||||
setupListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setOfferBookFilter is a one time call
|
@Override
|
||||||
void initWithData(Offer.Direction direction, Coin amount, Fiat price) {
|
protected void doActivate() {
|
||||||
dataModel.initWithData(direction, amount, price);
|
addBindings();
|
||||||
|
addListeners();
|
||||||
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
|
||||||
directionLabel.set(BSResources.get("shared.buyBitcoin"));
|
|
||||||
amountToTradeLabel.set(BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.buy")));
|
|
||||||
volumeDescriptionLabel.set(BSResources.get("createOffer.amountPriceBox.buy.volumeDescription", fiatCode.get()));
|
|
||||||
amountPriceBoxInfo.set(BSResources.get("createOffer.amountPriceBox.buy.info"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
directionLabel.set(BSResources.get("shared.sellBitcoin"));
|
|
||||||
amountToTradeLabel.set(BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.sell")));
|
|
||||||
volumeDescriptionLabel.set(BSResources.get("createOffer.amountPriceBox.sell.volumeDescription", fiatCode.get()));
|
|
||||||
amountPriceBoxInfo.set(BSResources.get("createOffer.amountPriceBox.sell.info"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
// apply only if valid
|
protected void doDeactivate() {
|
||||||
boolean amountValid = false;
|
removeBindings();
|
||||||
if (amount != null && isBtcInputValid(amount.toPlainString())
|
removeListeners();
|
||||||
.isValid) {
|
|
||||||
dataModel.amountAsCoin.set(amount);
|
|
||||||
dataModel.minAmountAsCoin.set(amount);
|
|
||||||
amountValid = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply only if valid
|
private void addBindings() {
|
||||||
boolean priceValid = false;
|
|
||||||
if (price != null && isBtcInputValid(price.toPlainString()).isValid) {
|
|
||||||
dataModel.priceAsFiat.set(formatter.parseToFiatWith2Decimals(price.toPlainString()));
|
|
||||||
priceValid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amountValid && priceValid)
|
|
||||||
dataModel.calculateTotalToPay();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void onPlaceOffer() {
|
|
||||||
dataModel.requestPlaceOfferErrorMessage.set(null);
|
|
||||||
dataModel.requestPlaceOfferSuccess.set(false);
|
|
||||||
|
|
||||||
isPlaceOfferSpinnerVisible.set(true);
|
|
||||||
|
|
||||||
dataModel.onPlaceOffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void onShowPayFundsScreen() {
|
|
||||||
isPlaceOfferButtonVisible.set(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// On focus out we do validation and apply the data to the model
|
|
||||||
void onFocusOutAmountTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
|
||||||
if (oldValue && !newValue) {
|
|
||||||
InputValidator.ValidationResult result = isBtcInputValid(amount.get());
|
|
||||||
amountValidationResult.set(result);
|
|
||||||
if (result.isValid) {
|
|
||||||
showWarningInvalidBtcDecimalPlaces.set(!formatter.hasBtcValidDecimals(userInput));
|
|
||||||
// only allow max 4 decimal places for btc values
|
|
||||||
setAmountToModel();
|
|
||||||
// reformat input
|
|
||||||
amount.set(formatter.formatCoin(dataModel.amountAsCoin.get()));
|
|
||||||
|
|
||||||
calculateVolume();
|
|
||||||
|
|
||||||
// handle minAmount/amount relationship
|
|
||||||
if (!dataModel.isMinAmountLessOrEqualAmount()) {
|
|
||||||
amountValidationResult.set(new InputValidator.ValidationResult(false,
|
|
||||||
BSResources.get("createOffer.validation.amountSmallerThanMinAmount")));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
amountValidationResult.set(result);
|
|
||||||
if (minAmount.get() != null)
|
|
||||||
minAmountValidationResult.set(isBtcInputValid(minAmount.get()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onFocusOutMinAmountTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
|
||||||
if (oldValue && !newValue) {
|
|
||||||
InputValidator.ValidationResult result = isBtcInputValid(minAmount.get());
|
|
||||||
minAmountValidationResult.set(result);
|
|
||||||
if (result.isValid) {
|
|
||||||
showWarningInvalidBtcDecimalPlaces.set(!formatter.hasBtcValidDecimals(userInput));
|
|
||||||
setMinAmountToModel();
|
|
||||||
minAmount.set(formatter.formatCoin(dataModel.minAmountAsCoin.get()));
|
|
||||||
|
|
||||||
if (!dataModel.isMinAmountLessOrEqualAmount()) {
|
|
||||||
minAmountValidationResult.set(new InputValidator.ValidationResult(false,
|
|
||||||
BSResources.get("createOffer.validation.minAmountLargerThanAmount")));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
minAmountValidationResult.set(result);
|
|
||||||
if (amount.get() != null)
|
|
||||||
amountValidationResult.set(isBtcInputValid(amount.get()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onFocusOutPriceTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
|
||||||
if (oldValue && !newValue) {
|
|
||||||
InputValidator.ValidationResult result = isFiatInputValid(price.get());
|
|
||||||
boolean isValid = result.isValid;
|
|
||||||
priceValidationResult.set(result);
|
|
||||||
if (isValid) {
|
|
||||||
showWarningInvalidFiatDecimalPlaces.set(!formatter.hasFiatValidDecimals(userInput));
|
|
||||||
setPriceToModel();
|
|
||||||
price.set(formatter.formatFiat(dataModel.priceAsFiat.get()));
|
|
||||||
|
|
||||||
calculateVolume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onFocusOutVolumeTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
|
||||||
if (oldValue && !newValue) {
|
|
||||||
InputValidator.ValidationResult result = isBtcInputValid(volume.get());
|
|
||||||
volumeValidationResult.set(result);
|
|
||||||
if (result.isValid) {
|
|
||||||
showWarningInvalidFiatDecimalPlaces.set(!formatter.hasFiatValidDecimals(userInput));
|
|
||||||
setVolumeToModel();
|
|
||||||
volume.set(formatter.formatFiat(dataModel.volumeAsFiat.get()));
|
|
||||||
|
|
||||||
calculateAmount();
|
|
||||||
|
|
||||||
// must be placed after calculateAmount (btc value has been adjusted in case the calculation leads to
|
|
||||||
// invalid decimal places for the amount value
|
|
||||||
showWarningAdjustedVolume.set(!formatter.formatFiat(formatter.parseToFiatWith2Decimals(userInput))
|
|
||||||
.equals(volume
|
|
||||||
.get()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void securityDepositInfoDisplayed() {
|
|
||||||
dataModel.securityDepositInfoDisplayed();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WalletService getWalletService() {
|
|
||||||
return dataModel.getWalletService();
|
|
||||||
}
|
|
||||||
|
|
||||||
BSFormatter getFormatter() {
|
|
||||||
return formatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
Boolean getDisplaySecurityDepositInfo() {
|
|
||||||
return dataModel.getDisplaySecurityDepositInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupListeners() {
|
|
||||||
// Bidirectional bindings are used for all input fields: amount, price, volume and minAmount
|
|
||||||
// We do volume/amount calculation during input, so user has immediate feedback
|
|
||||||
amount.addListener((ov, oldValue, newValue) -> {
|
|
||||||
if (isBtcInputValid(newValue).isValid) {
|
|
||||||
setAmountToModel();
|
|
||||||
calculateVolume();
|
|
||||||
dataModel.calculateTotalToPay();
|
|
||||||
}
|
|
||||||
updateButtonDisableState();
|
|
||||||
});
|
|
||||||
|
|
||||||
minAmount.addListener((ov, oldValue, newValue) -> {
|
|
||||||
setMinAmountToModel();
|
|
||||||
updateButtonDisableState();
|
|
||||||
});
|
|
||||||
|
|
||||||
price.addListener((ov, oldValue, newValue) -> {
|
|
||||||
if (isFiatInputValid(newValue).isValid) {
|
|
||||||
setPriceToModel();
|
|
||||||
calculateVolume();
|
|
||||||
dataModel.calculateTotalToPay();
|
|
||||||
}
|
|
||||||
updateButtonDisableState();
|
|
||||||
});
|
|
||||||
|
|
||||||
volume.addListener((ov, oldValue, newValue) -> {
|
|
||||||
if (isFiatInputValid(newValue).isValid) {
|
|
||||||
setVolumeToModel();
|
|
||||||
setPriceToModel();
|
|
||||||
dataModel.calculateAmount();
|
|
||||||
dataModel.calculateTotalToPay();
|
|
||||||
}
|
|
||||||
updateButtonDisableState();
|
|
||||||
});
|
|
||||||
dataModel.isWalletFunded.addListener((ov, oldValue, newValue) -> {
|
|
||||||
updateButtonDisableState();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
|
||||||
dataModel.amountAsCoin.addListener((ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue)));
|
|
||||||
dataModel.minAmountAsCoin.addListener((ov, oldValue, newValue) -> minAmount.set(formatter.formatCoin(newValue)));
|
|
||||||
dataModel.priceAsFiat.addListener((ov, oldValue, newValue) -> price.set(formatter.formatFiat(newValue)));
|
|
||||||
dataModel.volumeAsFiat.addListener((ov, oldValue, newValue) -> volume.set(formatter.formatFiat(newValue)));
|
|
||||||
|
|
||||||
dataModel.requestPlaceOfferErrorMessage.addListener((ov, oldValue, newValue) -> {
|
|
||||||
if (newValue != null) {
|
|
||||||
isPlaceOfferSpinnerVisible.set(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
dataModel.requestPlaceOfferSuccess.addListener((ov, oldValue, newValue) -> {
|
|
||||||
isPlaceOfferButtonVisible.set(!newValue);
|
|
||||||
isPlaceOfferSpinnerVisible.set(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ObservableLists
|
|
||||||
dataModel.acceptedCountries.addListener((Observable o) -> acceptedCountries.set(formatter
|
|
||||||
.countryLocalesToString(dataModel.acceptedCountries)));
|
|
||||||
dataModel.acceptedLanguageCodes.addListener((Observable o) -> acceptedLanguages.set(formatter
|
|
||||||
.languageCodesToString(dataModel.acceptedLanguageCodes)));
|
|
||||||
|
|
||||||
|
|
||||||
dataModel.acceptedArbitrators.addListener((Observable o) ->
|
|
||||||
acceptedArbitrators.set(formatter.arbitratorsToNames(dataModel.acceptedArbitrators)));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupBindings() {
|
|
||||||
totalToPay.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.totalToPayAsCoin.get()),
|
totalToPay.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.totalToPayAsCoin.get()),
|
||||||
dataModel.totalToPayAsCoin));
|
dataModel.totalToPayAsCoin));
|
||||||
securityDeposit.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.securityDepositAsCoin.get()),
|
securityDeposit.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.securityDepositAsCoin.get()),
|
||||||
@ -366,6 +174,307 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||||||
fiatCode.bind(dataModel.fiatCode);
|
fiatCode.bind(dataModel.fiatCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeBindings() {
|
||||||
|
totalToPay.unbind();
|
||||||
|
securityDeposit.unbind();
|
||||||
|
tradeAmount.unbind();
|
||||||
|
totalToPayAsCoin.unbind();
|
||||||
|
offerFee.unbind();
|
||||||
|
networkFee.unbind();
|
||||||
|
bankAccountType.unbind();
|
||||||
|
bankAccountCurrency.unbind();
|
||||||
|
bankAccountCounty.unbind();
|
||||||
|
requestPlaceOfferErrorMessage.unbind();
|
||||||
|
showTransactionPublishedScreen.unbind();
|
||||||
|
transactionId.unbind();
|
||||||
|
btcCode.unbind();
|
||||||
|
fiatCode.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createListeners() {
|
||||||
|
amountListener = (ov, oldValue, newValue) -> {
|
||||||
|
if (isBtcInputValid(newValue).isValid) {
|
||||||
|
setAmountToModel();
|
||||||
|
calculateVolume();
|
||||||
|
dataModel.calculateTotalToPay();
|
||||||
|
}
|
||||||
|
updateButtonDisableState();
|
||||||
|
};
|
||||||
|
minAmountListener = (ov, oldValue, newValue) -> {
|
||||||
|
setMinAmountToModel();
|
||||||
|
updateButtonDisableState();
|
||||||
|
};
|
||||||
|
priceListener = (ov, oldValue, newValue) -> {
|
||||||
|
if (isFiatInputValid(newValue).isValid) {
|
||||||
|
setPriceToModel();
|
||||||
|
calculateVolume();
|
||||||
|
dataModel.calculateTotalToPay();
|
||||||
|
}
|
||||||
|
updateButtonDisableState();
|
||||||
|
};
|
||||||
|
volumeListener = (ov, oldValue, newValue) -> {
|
||||||
|
if (isFiatInputValid(newValue).isValid) {
|
||||||
|
setVolumeToModel();
|
||||||
|
setPriceToModel();
|
||||||
|
dataModel.calculateAmount();
|
||||||
|
dataModel.calculateTotalToPay();
|
||||||
|
}
|
||||||
|
updateButtonDisableState();
|
||||||
|
};
|
||||||
|
amountAsCoinListener = (ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue));
|
||||||
|
minAmountAsCoinListener = (ov, oldValue, newValue) -> minAmount.set(formatter.formatCoin(newValue));
|
||||||
|
priceAsFiatListener = (ov, oldValue, newValue) -> price.set(formatter.formatFiat(newValue));
|
||||||
|
volumeAsFiatListener = (ov, oldValue, newValue) -> volume.set(formatter.formatFiat(newValue));
|
||||||
|
|
||||||
|
isWalletFundedListener = (ov, oldValue, newValue) -> {
|
||||||
|
updateButtonDisableState();
|
||||||
|
};
|
||||||
|
requestPlaceOfferSuccessListener = (ov, oldValue, newValue) -> {
|
||||||
|
isPlaceOfferButtonVisible.set(!newValue);
|
||||||
|
isPlaceOfferSpinnerVisible.set(false);
|
||||||
|
};
|
||||||
|
requestPlaceOfferErrorMessageListener = (ov, oldValue, newValue) -> {
|
||||||
|
if (newValue != null) {
|
||||||
|
isPlaceOfferSpinnerVisible.set(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
acceptedCountriesListener = (Observable o) ->
|
||||||
|
acceptedCountries.set(formatter.countryLocalesToString(dataModel.acceptedCountries));
|
||||||
|
acceptedLanguageCodesListener = (Observable o) -> acceptedLanguages.set(formatter.languageCodesToString(dataModel.acceptedLanguageCodes));
|
||||||
|
acceptedArbitratorsListener = (Observable o) -> acceptedArbitrators.set(formatter.arbitratorsToNames(dataModel.acceptedArbitrators));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
|
// Bidirectional bindings are used for all input fields: amount, price, volume and minAmount
|
||||||
|
// We do volume/amount calculation during input, so user has immediate feedback
|
||||||
|
amount.addListener(amountListener);
|
||||||
|
minAmount.addListener(minAmountListener);
|
||||||
|
price.addListener(priceListener);
|
||||||
|
volume.addListener(volumeListener);
|
||||||
|
|
||||||
|
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||||
|
dataModel.amountAsCoin.addListener(amountAsCoinListener);
|
||||||
|
dataModel.minAmountAsCoin.addListener(minAmountAsCoinListener);
|
||||||
|
dataModel.priceAsFiat.addListener(priceAsFiatListener);
|
||||||
|
dataModel.volumeAsFiat.addListener(volumeAsFiatListener);
|
||||||
|
|
||||||
|
dataModel.isWalletFunded.addListener(isWalletFundedListener);
|
||||||
|
dataModel.requestPlaceOfferSuccess.addListener(requestPlaceOfferSuccessListener);
|
||||||
|
dataModel.requestPlaceOfferErrorMessage.addListener(requestPlaceOfferErrorMessageListener);
|
||||||
|
|
||||||
|
// ObservableLists
|
||||||
|
dataModel.acceptedCountries.addListener(acceptedCountriesListener);
|
||||||
|
dataModel.acceptedLanguageCodes.addListener(acceptedLanguageCodesListener);
|
||||||
|
dataModel.acceptedArbitrators.addListener(acceptedArbitratorsListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeListeners() {
|
||||||
|
amount.removeListener(amountListener);
|
||||||
|
minAmount.removeListener(minAmountListener);
|
||||||
|
price.removeListener(priceListener);
|
||||||
|
volume.removeListener(volumeListener);
|
||||||
|
|
||||||
|
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||||
|
dataModel.amountAsCoin.removeListener(amountAsCoinListener);
|
||||||
|
dataModel.minAmountAsCoin.removeListener(minAmountAsCoinListener);
|
||||||
|
dataModel.priceAsFiat.removeListener(priceAsFiatListener);
|
||||||
|
dataModel.volumeAsFiat.removeListener(volumeAsFiatListener);
|
||||||
|
|
||||||
|
dataModel.isWalletFunded.removeListener(isWalletFundedListener);
|
||||||
|
dataModel.requestPlaceOfferSuccess.removeListener(requestPlaceOfferSuccessListener);
|
||||||
|
dataModel.requestPlaceOfferErrorMessage.removeListener(requestPlaceOfferErrorMessageListener);
|
||||||
|
|
||||||
|
// ObservableLists
|
||||||
|
dataModel.acceptedCountries.removeListener(acceptedCountriesListener);
|
||||||
|
dataModel.acceptedLanguageCodes.removeListener(acceptedLanguageCodesListener);
|
||||||
|
dataModel.acceptedArbitrators.removeListener(acceptedArbitratorsListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void initWithData(Offer.Direction direction, Coin amountAsCoin, Fiat priceAsFiat) {
|
||||||
|
addListeners();
|
||||||
|
|
||||||
|
dataModel.initWithData(direction);
|
||||||
|
|
||||||
|
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
||||||
|
directionLabel.set(BSResources.get("shared.buyBitcoin"));
|
||||||
|
amountToTradeLabel.set(BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.buy")));
|
||||||
|
volumeDescriptionLabel.set(BSResources.get("createOffer.amountPriceBox.buy.volumeDescription", fiatCode.get()));
|
||||||
|
amountPriceBoxInfo.set(BSResources.get("createOffer.amountPriceBox.buy.info"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
directionLabel.set(BSResources.get("shared.sellBitcoin"));
|
||||||
|
amountToTradeLabel.set(BSResources.get("createOffer.amountPriceBox.amountDescription", BSResources.get("shared.sell")));
|
||||||
|
volumeDescriptionLabel.set(BSResources.get("createOffer.amountPriceBox.sell.volumeDescription", fiatCode.get()));
|
||||||
|
amountPriceBoxInfo.set(BSResources.get("createOffer.amountPriceBox.sell.info"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// apply only if valid
|
||||||
|
boolean amountValid = false;
|
||||||
|
if (amountAsCoin != null && isBtcInputValid(amountAsCoin.toPlainString())
|
||||||
|
.isValid) {
|
||||||
|
dataModel.amountAsCoin.set(amountAsCoin);
|
||||||
|
dataModel.minAmountAsCoin.set(amountAsCoin);
|
||||||
|
amountValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply only if valid
|
||||||
|
boolean priceValid = false;
|
||||||
|
if (priceAsFiat != null && isBtcInputValid(priceAsFiat.toPlainString()).isValid) {
|
||||||
|
dataModel.priceAsFiat.set(formatter.parseToFiatWith2Decimals(priceAsFiat.toPlainString()));
|
||||||
|
priceValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amountValid && priceValid)
|
||||||
|
dataModel.calculateTotalToPay();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI actions
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void onPlaceOffer() {
|
||||||
|
dataModel.requestPlaceOfferErrorMessage.set(null);
|
||||||
|
dataModel.requestPlaceOfferSuccess.set(false);
|
||||||
|
|
||||||
|
isPlaceOfferSpinnerVisible.set(true);
|
||||||
|
|
||||||
|
dataModel.onPlaceOffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void onShowPayFundsScreen() {
|
||||||
|
isPlaceOfferButtonVisible.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onSecurityDepositInfoDisplayed() {
|
||||||
|
dataModel.onSecurityDepositInfoDisplayed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Handle focus
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// On focus out we do validation and apply the data to the model
|
||||||
|
void onFocusOutAmountTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||||
|
if (oldValue && !newValue) {
|
||||||
|
InputValidator.ValidationResult result = isBtcInputValid(amount.get());
|
||||||
|
amountValidationResult.set(result);
|
||||||
|
if (result.isValid) {
|
||||||
|
showWarningInvalidBtcDecimalPlaces.set(!formatter.hasBtcValidDecimals(userInput));
|
||||||
|
// only allow max 4 decimal places for btc values
|
||||||
|
setAmountToModel();
|
||||||
|
// reformat input
|
||||||
|
amount.set(formatter.formatCoin(dataModel.amountAsCoin.get()));
|
||||||
|
|
||||||
|
calculateVolume();
|
||||||
|
|
||||||
|
// handle minAmount/amount relationship
|
||||||
|
if (!dataModel.isMinAmountLessOrEqualAmount()) {
|
||||||
|
amountValidationResult.set(new InputValidator.ValidationResult(false,
|
||||||
|
BSResources.get("createOffer.validation.amountSmallerThanMinAmount")));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
amountValidationResult.set(result);
|
||||||
|
if (minAmount.get() != null)
|
||||||
|
minAmountValidationResult.set(isBtcInputValid(minAmount.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onFocusOutMinAmountTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||||
|
if (oldValue && !newValue) {
|
||||||
|
InputValidator.ValidationResult result = isBtcInputValid(minAmount.get());
|
||||||
|
minAmountValidationResult.set(result);
|
||||||
|
if (result.isValid) {
|
||||||
|
showWarningInvalidBtcDecimalPlaces.set(!formatter.hasBtcValidDecimals(userInput));
|
||||||
|
setMinAmountToModel();
|
||||||
|
minAmount.set(formatter.formatCoin(dataModel.minAmountAsCoin.get()));
|
||||||
|
|
||||||
|
if (!dataModel.isMinAmountLessOrEqualAmount()) {
|
||||||
|
minAmountValidationResult.set(new InputValidator.ValidationResult(false,
|
||||||
|
BSResources.get("createOffer.validation.minAmountLargerThanAmount")));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
minAmountValidationResult.set(result);
|
||||||
|
if (amount.get() != null)
|
||||||
|
amountValidationResult.set(isBtcInputValid(amount.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onFocusOutPriceTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||||
|
if (oldValue && !newValue) {
|
||||||
|
InputValidator.ValidationResult result = isFiatInputValid(price.get());
|
||||||
|
boolean isValid = result.isValid;
|
||||||
|
priceValidationResult.set(result);
|
||||||
|
if (isValid) {
|
||||||
|
showWarningInvalidFiatDecimalPlaces.set(!formatter.hasFiatValidDecimals(userInput));
|
||||||
|
setPriceToModel();
|
||||||
|
price.set(formatter.formatFiat(dataModel.priceAsFiat.get()));
|
||||||
|
|
||||||
|
calculateVolume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onFocusOutVolumeTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||||
|
if (oldValue && !newValue) {
|
||||||
|
InputValidator.ValidationResult result = isBtcInputValid(volume.get());
|
||||||
|
volumeValidationResult.set(result);
|
||||||
|
if (result.isValid) {
|
||||||
|
showWarningInvalidFiatDecimalPlaces.set(!formatter.hasFiatValidDecimals(userInput));
|
||||||
|
setVolumeToModel();
|
||||||
|
volume.set(formatter.formatFiat(dataModel.volumeAsFiat.get()));
|
||||||
|
|
||||||
|
calculateAmount();
|
||||||
|
|
||||||
|
// must be placed after calculateAmount (btc value has been adjusted in case the calculation leads to
|
||||||
|
// invalid decimal places for the amount value
|
||||||
|
showWarningAdjustedVolume.set(!formatter.formatFiat(formatter.parseToFiatWith2Decimals(userInput))
|
||||||
|
.equals(volume
|
||||||
|
.get()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Getters
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
WalletService getWalletService() {
|
||||||
|
return dataModel.getWalletService();
|
||||||
|
}
|
||||||
|
|
||||||
|
BSFormatter getFormatter() {
|
||||||
|
return formatter;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean getDisplaySecurityDepositInfo() {
|
||||||
|
return dataModel.getDisplaySecurityDepositInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isSeller() {
|
||||||
|
return dataModel.getDirection() == Offer.Direction.SELL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void calculateVolume() {
|
private void calculateVolume() {
|
||||||
setAmountToModel();
|
setAmountToModel();
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
@ -417,16 +526,11 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
||||||
|
|
||||||
return btcValidator.validate(input);
|
return btcValidator.validate(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputValidator.ValidationResult isFiatInputValid(String input) {
|
private InputValidator.ValidationResult isFiatInputValid(String input) {
|
||||||
|
|
||||||
return fiatValidator.validate(input);
|
return fiatValidator.validate(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isSeller() {
|
|
||||||
return dataModel.getDirection() == Offer.Direction.SELL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,19 @@
|
|||||||
package io.bitsquare.gui.main.offer.offerbook;
|
package io.bitsquare.gui.main.offer.offerbook;
|
||||||
|
|
||||||
import io.bitsquare.fiat.FiatAccount;
|
import io.bitsquare.fiat.FiatAccount;
|
||||||
import io.bitsquare.gui.util.GUIUtil;
|
|
||||||
import io.bitsquare.locale.Country;
|
import io.bitsquare.locale.Country;
|
||||||
import io.bitsquare.locale.CurrencyUtil;
|
import io.bitsquare.locale.CurrencyUtil;
|
||||||
import io.bitsquare.trade.TradeManager;
|
import io.bitsquare.trade.TradeManager;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.offer.OfferBookService;
|
import io.bitsquare.trade.offer.OfferBookService;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
import io.bitsquare.util.Utilities;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Timer;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import javafx.animation.AnimationTimer;
|
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
@ -38,8 +38,6 @@ import javafx.collections.ObservableList;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds and manages the unsorted and unfiltered offerbook list of both buy and sell offers.
|
* Holds and manages the unsorted and unfiltered offerbook list of both buy and sell offers.
|
||||||
* It is handled as singleton by Guice and is used by 2 instances of OfferBookDataModel (one for Buy one for Sell).
|
* It is handled as singleton by Guice and is used by 2 instances of OfferBookDataModel (one for Buy one for Sell).
|
||||||
@ -54,15 +52,15 @@ public class OfferBook {
|
|||||||
|
|
||||||
private final OfferBookService offerBookService;
|
private final OfferBookService offerBookService;
|
||||||
private final User user;
|
private final User user;
|
||||||
|
|
||||||
private final ObservableList<OfferBookListItem> offerBookListItems = FXCollections.observableArrayList();
|
|
||||||
private final OfferBookService.Listener offerBookServiceListener;
|
|
||||||
private final ChangeListener<FiatAccount> bankAccountChangeListener;
|
private final ChangeListener<FiatAccount> bankAccountChangeListener;
|
||||||
private final ChangeListener<Number> invalidationListener;
|
private final ChangeListener<Number> invalidationListener;
|
||||||
|
private final OfferBookService.Listener offerBookServiceListener;
|
||||||
|
|
||||||
|
private final ObservableList<OfferBookListItem> offerBookListItems = FXCollections.observableArrayList();
|
||||||
|
|
||||||
private String fiatCode;
|
private String fiatCode;
|
||||||
private AnimationTimer pollingTimer;
|
private Timer pollingTimer;
|
||||||
private Country country;
|
private Country country;
|
||||||
private int numClients = 0;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -75,7 +73,7 @@ public class OfferBook {
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
|
|
||||||
bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue);
|
bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue);
|
||||||
invalidationListener = (ov, oldValue, newValue) -> requestGetOffers();
|
invalidationListener = (ov, oldValue, newValue) -> offerBookService.getOffers(fiatCode);
|
||||||
|
|
||||||
offerBookServiceListener = new OfferBookService.Listener() {
|
offerBookServiceListener = new OfferBookService.Listener() {
|
||||||
@Override
|
@Override
|
||||||
@ -105,20 +103,31 @@ public class OfferBook {
|
|||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Package scope
|
// API
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void addClient() {
|
void startPolling() {
|
||||||
numClients++;
|
addListeners();
|
||||||
if (numClients == 1)
|
setBankAccount(user.currentFiatAccountProperty().get());
|
||||||
startPolling();
|
pollingTimer = Utilities.setInterval(POLLING_INTERVAL, () -> offerBookService.requestInvalidationTimeStampFromDHT(fiatCode));
|
||||||
|
offerBookService.getOffers(fiatCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeClient() {
|
void stopPolling() {
|
||||||
numClients--;
|
pollingTimer.cancel();
|
||||||
checkArgument(numClients >= 0);
|
removeListeners();
|
||||||
if (numClients == 0)
|
}
|
||||||
stopPolling();
|
|
||||||
|
private void addListeners() {
|
||||||
|
user.currentFiatAccountProperty().addListener(bankAccountChangeListener);
|
||||||
|
offerBookService.addListener(offerBookServiceListener);
|
||||||
|
offerBookService.invalidationTimestampProperty().addListener(invalidationListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeListeners() {
|
||||||
|
user.currentFiatAccountProperty().removeListener(bankAccountChangeListener);
|
||||||
|
offerBookService.removeListener(offerBookServiceListener);
|
||||||
|
offerBookService.invalidationTimestampProperty().removeListener(invalidationListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -126,13 +135,13 @@ public class OfferBook {
|
|||||||
// Getter
|
// Getter
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public ObservableList<OfferBookListItem> getOfferBookListItems() {
|
ObservableList<OfferBookListItem> getOfferBookListItems() {
|
||||||
return offerBookListItems;
|
return offerBookListItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Private
|
// Utils
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void setBankAccount(FiatAccount fiatAccount) {
|
private void setBankAccount(FiatAccount fiatAccount) {
|
||||||
@ -149,50 +158,9 @@ public class OfferBook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addListeners() {
|
|
||||||
log.debug("addListeners ");
|
|
||||||
user.currentFiatAccountProperty().addListener(bankAccountChangeListener);
|
|
||||||
offerBookService.addListener(offerBookServiceListener);
|
|
||||||
offerBookService.invalidationTimestampProperty().addListener(invalidationListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeListeners() {
|
|
||||||
log.debug("removeListeners ");
|
|
||||||
user.currentFiatAccountProperty().removeListener(bankAccountChangeListener);
|
|
||||||
offerBookService.removeListener(offerBookServiceListener);
|
|
||||||
offerBookService.invalidationTimestampProperty().removeListener(invalidationListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addOfferToOfferBookListItems(Offer offer) {
|
private void addOfferToOfferBookListItems(Offer offer) {
|
||||||
if (offer != null) {
|
if (offer != null) {
|
||||||
offerBookListItems.add(new OfferBookListItem(offer, country));
|
offerBookListItems.add(new OfferBookListItem(offer, country));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestGetOffers() {
|
|
||||||
offerBookService.getOffers(fiatCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Polling
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// TODO Just temporary, will be removed later when we have a push solution
|
|
||||||
private void startPolling() {
|
|
||||||
addListeners();
|
|
||||||
setBankAccount(user.currentFiatAccountProperty().get());
|
|
||||||
pollingTimer = GUIUtil.setInterval(POLLING_INTERVAL, (animationTimer) -> {
|
|
||||||
offerBookService.requestInvalidationTimeStampFromDHT(fiatCode);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
offerBookService.getOffers(fiatCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopPolling() {
|
|
||||||
pollingTimer.stop();
|
|
||||||
removeListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -54,16 +54,16 @@ import org.slf4j.LoggerFactory;
|
|||||||
class OfferBookDataModel implements Activatable, DataModel {
|
class OfferBookDataModel implements Activatable, DataModel {
|
||||||
private static final Logger log = LoggerFactory.getLogger(OfferBookDataModel.class);
|
private static final Logger log = LoggerFactory.getLogger(OfferBookDataModel.class);
|
||||||
|
|
||||||
|
private final OpenOfferManager openOfferManager;
|
||||||
private final User user;
|
private final User user;
|
||||||
private final OfferBook offerBook;
|
private final OfferBook offerBook;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
private final BSFormatter formatter;
|
private final BSFormatter formatter;
|
||||||
private final OpenOfferManager openOfferManager;
|
|
||||||
|
|
||||||
private final FilteredList<OfferBookListItem> filteredItems;
|
private final FilteredList<OfferBookListItem> filteredItems;
|
||||||
private final SortedList<OfferBookListItem> sortedItems;
|
private final SortedList<OfferBookListItem> sortedItems;
|
||||||
// private OfferBookInfo offerBookInfo;
|
|
||||||
private final ChangeListener<FiatAccount> bankAccountChangeListener;
|
private ChangeListener<FiatAccount> bankAccountChangeListener;
|
||||||
|
|
||||||
private final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
|
private final ObjectProperty<Coin> amountAsCoin = new SimpleObjectProperty<>();
|
||||||
private final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
private final ObjectProperty<Fiat> priceAsFiat = new SimpleObjectProperty<>();
|
||||||
@ -76,6 +76,10 @@ class OfferBookDataModel implements Activatable, DataModel {
|
|||||||
private Offer.Direction direction;
|
private Offer.Direction direction;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public OfferBookDataModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook, Preferences preferences,
|
public OfferBookDataModel(User user, OpenOfferManager openOfferManager, OfferBook offerBook, Preferences preferences,
|
||||||
BSFormatter formatter) {
|
BSFormatter formatter) {
|
||||||
@ -87,63 +91,81 @@ class OfferBookDataModel implements Activatable, DataModel {
|
|||||||
|
|
||||||
this.filteredItems = new FilteredList<>(offerBook.getOfferBookListItems());
|
this.filteredItems = new FilteredList<>(offerBook.getOfferBookListItems());
|
||||||
this.sortedItems = new SortedList<>(filteredItems);
|
this.sortedItems = new SortedList<>(filteredItems);
|
||||||
this.bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue);
|
|
||||||
|
createListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activate() {
|
public void activate() {
|
||||||
|
addBindings();
|
||||||
|
addListeners();
|
||||||
|
/*
|
||||||
amountAsCoin.set(null);
|
amountAsCoin.set(null);
|
||||||
priceAsFiat.set(null);
|
priceAsFiat.set(null);
|
||||||
volumeAsFiat.set(null);
|
volumeAsFiat.set(null);
|
||||||
|
|
||||||
offerBook.addClient();
|
//TODO temp for testing
|
||||||
user.currentFiatAccountProperty().addListener(bankAccountChangeListener);
|
amountAsCoin.set(Coin.COIN);
|
||||||
btcCode.bind(preferences.btcDenominationProperty());
|
priceAsFiat.set(Fiat.valueOf("EUR", 300*10000));
|
||||||
|
// volumeAsFiat.set(Fiat.valueOf("EUR", 300));*/
|
||||||
|
|
||||||
setBankAccount(user.currentFiatAccountProperty().get());
|
setBankAccount(user.currentFiatAccountProperty().get());
|
||||||
applyFilter();
|
applyFilter();
|
||||||
|
|
||||||
|
offerBook.startPolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deactivate() {
|
public void deactivate() {
|
||||||
offerBook.removeClient();
|
removeBindings();
|
||||||
user.currentFiatAccountProperty().removeListener(bankAccountChangeListener);
|
removeListeners();
|
||||||
|
|
||||||
|
offerBook.stopPolling();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBindings() {
|
||||||
|
btcCode.bind(preferences.btcDenominationProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBindings() {
|
||||||
btcCode.unbind();
|
btcCode.unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createListeners() {
|
||||||
|
this.bankAccountChangeListener = (observableValue, oldValue, newValue) -> setBankAccount(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
|
user.currentFiatAccountProperty().addListener(bankAccountChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeListeners() {
|
||||||
|
user.currentFiatAccountProperty().removeListener(bankAccountChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
void setDirection(Offer.Direction direction) {
|
||||||
|
this.direction = direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI actions
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
openOfferManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
|
openOfferManager.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateVolume() {
|
|
||||||
try {
|
|
||||||
if (priceAsFiat.get() != null &&
|
|
||||||
amountAsCoin.get() != null &&
|
|
||||||
!amountAsCoin.get().isZero() &&
|
|
||||||
!priceAsFiat.get().isZero()) {
|
|
||||||
volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
// Should be never reached
|
|
||||||
log.error(t.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void calculateAmount() {
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
try {
|
// Getters
|
||||||
if (volumeAsFiat.get() != null &&
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
priceAsFiat.get() != null &&
|
|
||||||
!volumeAsFiat.get().isZero() &&
|
|
||||||
!priceAsFiat.get().isZero()) {
|
|
||||||
// If we got a btc value with more then 4 decimals we convert it to max 4 decimals
|
|
||||||
amountAsCoin.set(formatter.reduceTo4Decimals(new ExchangeRate(priceAsFiat.get()).fiatToCoin
|
|
||||||
(volumeAsFiat.get())));
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
// Should be never reached
|
|
||||||
log.error(t.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isTradable(Offer offer) {
|
boolean isTradable(Offer offer) {
|
||||||
// if user has not registered yet we display all
|
// if user has not registered yet we display all
|
||||||
@ -180,27 +202,6 @@ class OfferBookDataModel implements Activatable, DataModel {
|
|||||||
return countryResult;
|
return countryResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void setDirection(Offer.Direction direction) {
|
|
||||||
this.direction = direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAmount(Coin amount) {
|
|
||||||
amountAsCoin.set(amount);
|
|
||||||
applyFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPrice(Fiat price) {
|
|
||||||
priceAsFiat.set(price);
|
|
||||||
applyFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setVolume(Fiat volume) {
|
|
||||||
volumeAsFiat.set(volume);
|
|
||||||
applyFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SortedList<OfferBookListItem> getOfferList() {
|
SortedList<OfferBookListItem> getOfferList() {
|
||||||
return sortedItems;
|
return sortedItems;
|
||||||
}
|
}
|
||||||
@ -241,6 +242,57 @@ class OfferBookDataModel implements Activatable, DataModel {
|
|||||||
return direction;
|
return direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void calculateVolume() {
|
||||||
|
try {
|
||||||
|
if (priceAsFiat.get() != null &&
|
||||||
|
amountAsCoin.get() != null &&
|
||||||
|
!amountAsCoin.get().isZero() &&
|
||||||
|
!priceAsFiat.get().isZero()) {
|
||||||
|
volumeAsFiat.set(new ExchangeRate(priceAsFiat.get()).coinToFiat(amountAsCoin.get()));
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Should be never reached
|
||||||
|
log.error(t.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void calculateAmount() {
|
||||||
|
try {
|
||||||
|
if (volumeAsFiat.get() != null &&
|
||||||
|
priceAsFiat.get() != null &&
|
||||||
|
!volumeAsFiat.get().isZero() &&
|
||||||
|
!priceAsFiat.get().isZero()) {
|
||||||
|
// If we got a btc value with more then 4 decimals we convert it to max 4 decimals
|
||||||
|
amountAsCoin.set(formatter.reduceTo4Decimals(new ExchangeRate(priceAsFiat.get()).fiatToCoin
|
||||||
|
(volumeAsFiat.get())));
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Should be never reached
|
||||||
|
log.error(t.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setAmount(Coin amount) {
|
||||||
|
amountAsCoin.set(amount);
|
||||||
|
applyFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPrice(Fiat price) {
|
||||||
|
priceAsFiat.set(price);
|
||||||
|
applyFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVolume(Fiat volume) {
|
||||||
|
volumeAsFiat.set(volume);
|
||||||
|
applyFilter();
|
||||||
|
}
|
||||||
|
|
||||||
private void setBankAccount(FiatAccount fiatAccount) {
|
private void setBankAccount(FiatAccount fiatAccount) {
|
||||||
if (fiatAccount != null) {
|
if (fiatAccount != null) {
|
||||||
fiatCode.set(fiatAccount.currencyCode);
|
fiatCode.set(fiatAccount.currencyCode);
|
||||||
@ -274,5 +326,4 @@ class OfferBookDataModel implements Activatable, DataModel {
|
|||||||
return directionResult && amountResult && priceResult;
|
return directionResult && amountResult && priceResult;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,30 +27,15 @@ public class OfferBookListItem {
|
|||||||
private final Offer offer;
|
private final Offer offer;
|
||||||
private final ObjectProperty<Country> bankAccountCountry = new SimpleObjectProperty<>();
|
private final ObjectProperty<Country> bankAccountCountry = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public OfferBookListItem(Offer offer, Country bankAccountCountry) {
|
public OfferBookListItem(Offer offer, Country bankAccountCountry) {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
setBankAccountCountry(bankAccountCountry);
|
setBankAccountCountry(bankAccountCountry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Setters
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
void setBankAccountCountry(Country bankAccountCountry) {
|
void setBankAccountCountry(Country bankAccountCountry) {
|
||||||
this.bankAccountCountry.set(bankAccountCountry);
|
this.bankAccountCountry.set(bankAccountCountry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Getters
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public Offer getOffer() {
|
public Offer getOffer() {
|
||||||
return offer;
|
return offer;
|
||||||
}
|
}
|
||||||
@ -62,7 +47,5 @@ public class OfferBookListItem {
|
|||||||
ObjectProperty<Country> bankAccountCountryProperty() {
|
ObjectProperty<Country> bankAccountCountryProperty() {
|
||||||
return bankAccountCountry;
|
return bankAccountCountry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import io.bitsquare.gui.util.ImageUtil;
|
|||||||
import io.bitsquare.gui.util.validation.OptionalBtcValidator;
|
import io.bitsquare.gui.util.validation.OptionalBtcValidator;
|
||||||
import io.bitsquare.gui.util.validation.OptionalFiatValidator;
|
import io.bitsquare.gui.util.validation.OptionalFiatValidator;
|
||||||
import io.bitsquare.locale.BSResources;
|
import io.bitsquare.locale.BSResources;
|
||||||
|
import io.bitsquare.locale.Country;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -43,7 +44,7 @@ import java.util.List;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import javafx.beans.property.ReadOnlyObjectWrapper;
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
import javafx.collections.transformation.SortedList;
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
@ -84,6 +85,11 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
private final OptionalFiatValidator optionalFiatValidator;
|
private final OptionalFiatValidator optionalFiatValidator;
|
||||||
private OfferView.OfferActionHandler offerActionHandler;
|
private OfferView.OfferActionHandler offerActionHandler;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
OfferBookView(OfferBookViewModel model,
|
OfferBookView(OfferBookViewModel model,
|
||||||
Navigation navigation,
|
Navigation navigation,
|
||||||
@ -114,7 +120,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
placeholder.setWrapText(true);
|
placeholder.setWrapText(true);
|
||||||
table.setPlaceholder(placeholder);
|
table.setPlaceholder(placeholder);
|
||||||
|
|
||||||
|
|
||||||
setupValidators();
|
setupValidators();
|
||||||
setupComparators();
|
setupComparators();
|
||||||
|
|
||||||
@ -129,24 +134,12 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doActivate() {
|
public void doActivate() {
|
||||||
amountTextField.setText("");
|
addBindings();
|
||||||
priceTextField.setText("");
|
|
||||||
volumeTextField.setText("");
|
|
||||||
|
|
||||||
setupBindings();
|
|
||||||
|
|
||||||
// setOfferBookInfo has been called before
|
// setOfferBookInfo has been called before
|
||||||
SortedList<OfferBookListItem> offerList = model.getOfferList();
|
table.setItems(model.getOfferList());
|
||||||
table.setItems(offerList);
|
priceColumn.setSortType((model.getDirection() == Offer.Direction.BUY) ? TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
|
||||||
offerList.comparatorProperty().bind(table.comparatorProperty());
|
|
||||||
priceColumn.setSortType((model.getDirection() == Offer.Direction.BUY) ?
|
|
||||||
TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
|
|
||||||
table.sort();
|
table.sort();
|
||||||
|
|
||||||
//TODO temp for testing
|
|
||||||
amountTextField.setText("1");
|
|
||||||
priceTextField.setText("300");
|
|
||||||
volumeTextField.setText("300");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -154,6 +147,56 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
removeBindings();
|
removeBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addBindings() {
|
||||||
|
amountTextField.textProperty().bindBidirectional(model.amount);
|
||||||
|
priceTextField.textProperty().bindBidirectional(model.price);
|
||||||
|
volumeTextField.textProperty().bindBidirectional(model.volume);
|
||||||
|
amountBtcLabel.textProperty().bind(model.btcCode);
|
||||||
|
priceFiatLabel.textProperty().bind(model.fiatCode);
|
||||||
|
volumeFiatLabel.textProperty().bind(model.fiatCode);
|
||||||
|
priceDescriptionLabel.textProperty().bind(createStringBinding(() -> BSResources.get("Filter by price in {0}", model.fiatCode.get()), model.fiatCode));
|
||||||
|
volumeDescriptionLabel.textProperty().bind(createStringBinding(() -> BSResources.get("Filter by amount in {0}", model.fiatCode.get()), model.fiatCode));
|
||||||
|
volumeTextField.promptTextProperty().bind(createStringBinding(() -> BSResources.get("Amount in {0}", model.fiatCode.get()), model.fiatCode));
|
||||||
|
|
||||||
|
model.getOfferList().comparatorProperty().bind(table.comparatorProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBindings() {
|
||||||
|
amountTextField.textProperty().unbind();
|
||||||
|
priceTextField.textProperty().unbind();
|
||||||
|
volumeTextField.textProperty().unbind();
|
||||||
|
amountBtcLabel.textProperty().unbind();
|
||||||
|
priceFiatLabel.textProperty().unbind();
|
||||||
|
volumeFiatLabel.textProperty().unbind();
|
||||||
|
priceDescriptionLabel.textProperty().unbind();
|
||||||
|
volumeDescriptionLabel.textProperty().unbind();
|
||||||
|
volumeTextField.promptTextProperty().unbind();
|
||||||
|
|
||||||
|
model.getOfferList().comparatorProperty().unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupValidators() {
|
||||||
|
amountTextField.setValidator(optionalBtcValidator);
|
||||||
|
priceTextField.setValidator(optionalFiatValidator);
|
||||||
|
volumeTextField.setValidator(optionalFiatValidator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupComparators() {
|
||||||
|
priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
|
||||||
|
amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
|
||||||
|
volumeColumn.setComparator((o1, o2) ->
|
||||||
|
o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
|
||||||
|
/* countryColumn.setComparator((o1, o2) -> o1.getOffer().getBankAccountCountry().getName().compareTo(o2
|
||||||
|
.getOffer()
|
||||||
|
.getBankAccountCountry().getName()));*/
|
||||||
|
bankAccountTypeColumn.setComparator((o1, o2) -> o1.getOffer().getFiatAccountType().compareTo(o2.getOffer()
|
||||||
|
.getFiatAccountType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void enableCreateOfferButton() {
|
public void enableCreateOfferButton() {
|
||||||
createOfferButton.setDisable(false);
|
createOfferButton.setDisable(false);
|
||||||
}
|
}
|
||||||
@ -162,6 +205,15 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
model.setDirection(direction);
|
model.setDirection(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOfferActionHandler(OfferView.OfferActionHandler offerActionHandler) {
|
||||||
|
this.offerActionHandler = offerActionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI actions
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void createOffer() {
|
void createOffer() {
|
||||||
if (model.isRegistered()) {
|
if (model.isRegistered()) {
|
||||||
@ -173,6 +225,23 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openSetupScreen() {
|
||||||
|
overlayManager.blurContent();
|
||||||
|
List<Action> actions = new ArrayList<>();
|
||||||
|
actions.add(new AbstractAction(BSResources.get("shared.ok")) {
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent actionEvent) {
|
||||||
|
getProperties().put("type", "OK");
|
||||||
|
Dialog.Actions.OK.handle(actionEvent);
|
||||||
|
navigation.setReturnPath(navigation.getCurrentPath());
|
||||||
|
navigation.navigateTo(MainView.class, AccountView.class, AccountSetupWizard.class);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Popups.openInfoPopup("You don't have setup a trading account.",
|
||||||
|
"You need to setup your trading account before you can trade.",
|
||||||
|
actions);
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onToggleShowAdvancedSettings() {
|
void onToggleShowAdvancedSettings() {
|
||||||
detailsVisible = !detailsVisible;
|
detailsVisible = !detailsVisible;
|
||||||
@ -203,23 +272,6 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
|
Popups.openWarningPopup("Under construction", "This feature is not implemented yet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openSetupScreen() {
|
|
||||||
overlayManager.blurContent();
|
|
||||||
List<Action> actions = new ArrayList<>();
|
|
||||||
actions.add(new AbstractAction(BSResources.get("shared.ok")) {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent actionEvent) {
|
|
||||||
getProperties().put("type", "OK");
|
|
||||||
Dialog.Actions.OK.handle(actionEvent);
|
|
||||||
navigation.setReturnPath(navigation.getCurrentPath());
|
|
||||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSetupWizard.class);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Popups.openInfoPopup("You don't have setup a trading account.",
|
|
||||||
"You need to setup your trading account before you can trade.",
|
|
||||||
actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void takeOffer(Offer offer) {
|
private void takeOffer(Offer offer) {
|
||||||
if (model.isRegistered()) {
|
if (model.isRegistered()) {
|
||||||
if (offer.getDirection() == Offer.Direction.BUY) {
|
if (offer.getDirection() == Offer.Direction.BUY) {
|
||||||
@ -248,36 +300,10 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openRestrictionsWarning(String restrictionsInfo) {
|
|
||||||
overlayManager.blurContent();
|
|
||||||
List<Action> actions = new ArrayList<>();
|
|
||||||
actions.add(new AbstractAction(BSResources.get("shared.yes")) {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent actionEvent) {
|
|
||||||
getProperties().put("type", "YES");
|
|
||||||
Dialog.Actions.YES.handle(actionEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
actions.add(new AbstractAction(BSResources.get("shared.no")) {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent actionEvent) {
|
|
||||||
getProperties().put("type", "NO");
|
|
||||||
Dialog.Actions.NO.handle(actionEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Action response = Popups.openConfirmPopup("Information",
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
"You do not fulfill the requirements for that offer.",
|
// State
|
||||||
restrictionsInfo,
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
actions);
|
|
||||||
|
|
||||||
Popups.removeBlurContent();
|
|
||||||
|
|
||||||
if (Popups.isYes(response))
|
|
||||||
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, RestrictionsView.class);
|
|
||||||
else
|
|
||||||
table.getSelectionModel().clearSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDetailsScreen() {
|
private void showDetailsScreen() {
|
||||||
if (!advancedScreenInited) {
|
if (!advancedScreenInited) {
|
||||||
@ -313,61 +339,46 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
showOnlyMatchingCheckBox.setManaged(visible);
|
showOnlyMatchingCheckBox.setManaged(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupBindings() {
|
|
||||||
amountTextField.textProperty().bindBidirectional(model.amount);
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
priceTextField.textProperty().bindBidirectional(model.price);
|
// Utils
|
||||||
volumeTextField.textProperty().bindBidirectional(model.volume);
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
amountBtcLabel.textProperty().bind(model.btcCode);
|
|
||||||
priceFiatLabel.textProperty().bind(model.fiatCode);
|
private void openRestrictionsWarning(String restrictionsInfo) {
|
||||||
volumeFiatLabel.textProperty().bind(model.fiatCode);
|
overlayManager.blurContent();
|
||||||
priceDescriptionLabel.textProperty().bind(model.fiatCode);
|
List<Action> actions = new ArrayList<>();
|
||||||
volumeDescriptionLabel.textProperty().bind(model.fiatCode);//Price per Bitcoin in EUR
|
actions.add(new AbstractAction(BSResources.get("shared.yes")) {
|
||||||
priceDescriptionLabel.textProperty().bind(createStringBinding(() ->
|
@Override
|
||||||
BSResources.get("Filter by price in {0}", model.fiatCode.get()),
|
public void handle(ActionEvent actionEvent) {
|
||||||
model.fiatCode));
|
getProperties().put("type", "YES");
|
||||||
volumeDescriptionLabel.textProperty().bind(createStringBinding(() ->
|
Dialog.Actions.YES.handle(actionEvent);
|
||||||
BSResources.get("Filter by amount in {0}", model.fiatCode.get()),
|
}
|
||||||
model.fiatCode));
|
});
|
||||||
volumeTextField.promptTextProperty().bind(createStringBinding(() ->
|
actions.add(new AbstractAction(BSResources.get("shared.no")) {
|
||||||
BSResources.get("Amount in {0}", model.fiatCode.get()),
|
@Override
|
||||||
model.fiatCode));
|
public void handle(ActionEvent actionEvent) {
|
||||||
|
getProperties().put("type", "NO");
|
||||||
|
Dialog.Actions.NO.handle(actionEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Action response = Popups.openConfirmPopup("Information",
|
||||||
|
"You do not fulfill the requirements for that offer.",
|
||||||
|
restrictionsInfo,
|
||||||
|
actions);
|
||||||
|
|
||||||
|
Popups.removeBlurContent();
|
||||||
|
|
||||||
|
if (Popups.isYes(response))
|
||||||
|
navigation.navigateTo(MainView.class, AccountView.class, AccountSettingsView.class, RestrictionsView.class);
|
||||||
|
else
|
||||||
|
table.getSelectionModel().clearSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeBindings() {
|
|
||||||
amountTextField.textProperty().unbind();
|
|
||||||
priceTextField.textProperty().unbind();
|
|
||||||
volumeTextField.textProperty().unbind();
|
|
||||||
amountBtcLabel.textProperty().unbind();
|
|
||||||
priceFiatLabel.textProperty().unbind();
|
|
||||||
volumeFiatLabel.textProperty().unbind();
|
|
||||||
priceDescriptionLabel.textProperty().unbind();
|
|
||||||
volumeDescriptionLabel.textProperty().unbind();
|
|
||||||
priceDescriptionLabel.textProperty().unbind();
|
|
||||||
volumeDescriptionLabel.textProperty().unbind();
|
|
||||||
volumeTextField.promptTextProperty().unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupValidators() {
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
amountTextField.setValidator(optionalBtcValidator);
|
// Table
|
||||||
priceTextField.setValidator(optionalFiatValidator);
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
volumeTextField.setValidator(optionalFiatValidator);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupComparators() {
|
|
||||||
priceColumn.setComparator((o1, o2) -> o1.getOffer().getPrice().compareTo(o2.getOffer().getPrice()));
|
|
||||||
amountColumn.setComparator((o1, o2) -> o1.getOffer().getAmount().compareTo(o2.getOffer().getAmount()));
|
|
||||||
volumeColumn.setComparator((o1, o2) ->
|
|
||||||
o1.getOffer().getOfferVolume().compareTo(o2.getOffer().getOfferVolume()));
|
|
||||||
/* countryColumn.setComparator((o1, o2) -> o1.getOffer().getBankAccountCountry().getName().compareTo(o2
|
|
||||||
.getOffer()
|
|
||||||
.getBankAccountCountry().getName()));*/
|
|
||||||
bankAccountTypeColumn.setComparator((o1, o2) -> o1.getOffer().getFiatAccountType().compareTo(o2.getOffer()
|
|
||||||
.getFiatAccountType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOfferActionHandler(OfferView.OfferActionHandler offerActionHandler) {
|
|
||||||
this.offerActionHandler = offerActionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setAmountColumnCellFactory() {
|
private void setAmountColumnCellFactory() {
|
||||||
amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
amountColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
|
||||||
@ -438,6 +449,8 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
return new TableCell<OfferBookListItem, OfferBookListItem>() {
|
return new TableCell<OfferBookListItem, OfferBookListItem>() {
|
||||||
final ImageView iconView = new ImageView();
|
final ImageView iconView = new ImageView();
|
||||||
final Button button = new Button();
|
final Button button = new Button();
|
||||||
|
ChangeListener<Country> countryChangeListener;
|
||||||
|
OfferBookListItem item;
|
||||||
|
|
||||||
{
|
{
|
||||||
button.setGraphic(iconView);
|
button.setGraphic(iconView);
|
||||||
@ -493,14 +506,26 @@ public class OfferBookView extends ActivatableViewAndModel<GridPane, OfferBookVi
|
|||||||
button.setOnAction(event -> takeOffer(item.getOffer()));
|
button.setOnAction(event -> takeOffer(item.getOffer()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO remove listener
|
|
||||||
item.bankAccountCountryProperty().addListener((ov, o, n) -> verifyIfTradable(item));
|
if (countryChangeListener != null && this.item != null)
|
||||||
|
item.bankAccountCountryProperty().removeListener(countryChangeListener);
|
||||||
|
|
||||||
|
this.item = item;
|
||||||
|
countryChangeListener = (ov, o, n) -> verifyIfTradable(this.item);
|
||||||
|
item.bankAccountCountryProperty().addListener(countryChangeListener);
|
||||||
|
|
||||||
|
|
||||||
verifyIfTradable(item);
|
verifyIfTradable(item);
|
||||||
|
|
||||||
button.setText(title);
|
button.setText(title);
|
||||||
setGraphic(button);
|
setGraphic(button);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (this.item != null) {
|
||||||
|
this.item.bankAccountCountryProperty().removeListener(countryChangeListener);
|
||||||
|
this.item = null;
|
||||||
|
countryChangeListener = null;
|
||||||
|
}
|
||||||
setGraphic(null);
|
setGraphic(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ import com.google.inject.Inject;
|
|||||||
|
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
import javafx.collections.transformation.SortedList;
|
import javafx.collections.transformation.SortedList;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -54,6 +55,17 @@ class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> im
|
|||||||
final StringProperty fiatCode = new SimpleStringProperty();
|
final StringProperty fiatCode = new SimpleStringProperty();
|
||||||
final StringProperty restrictionsInfo = new SimpleStringProperty();
|
final StringProperty restrictionsInfo = new SimpleStringProperty();
|
||||||
|
|
||||||
|
private ChangeListener<String> amountListener;
|
||||||
|
private ChangeListener<String> priceListener;
|
||||||
|
private ChangeListener<String> volumeListener;
|
||||||
|
private ChangeListener<Coin> amountAsCoinListener;
|
||||||
|
private ChangeListener<Fiat> priceAsFiatListener;
|
||||||
|
private ChangeListener<Fiat> volumeAsFiatListener;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public OfferBookViewModel(OfferBookDataModel dataModel, OptionalFiatValidator optionalFiatValidator,
|
public OfferBookViewModel(OfferBookDataModel dataModel, OptionalFiatValidator optionalFiatValidator,
|
||||||
@ -64,58 +76,116 @@ class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> im
|
|||||||
this.optionalBtcValidator = optionalBtcValidator;
|
this.optionalBtcValidator = optionalBtcValidator;
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
|
|
||||||
|
createListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doActivate() {
|
||||||
|
addBindings();
|
||||||
|
addListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDeactivate() {
|
||||||
|
removeBindings();
|
||||||
|
removeListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBindings() {
|
||||||
btcCode.bind(dataModel.btcCode);
|
btcCode.bind(dataModel.btcCode);
|
||||||
fiatCode.bind(dataModel.fiatCode);
|
fiatCode.bind(dataModel.fiatCode);
|
||||||
restrictionsInfo.bind(dataModel.restrictionsInfo);
|
restrictionsInfo.bind(dataModel.restrictionsInfo);
|
||||||
|
}
|
||||||
|
|
||||||
// Bidirectional bindings are used for all input fields: amount, price and volume
|
private void removeBindings() {
|
||||||
// We do volume/amount calculation during input, so user has immediate feedback
|
btcCode.unbind();
|
||||||
amount.addListener((ov, oldValue, newValue) -> {
|
fiatCode.unbind();
|
||||||
|
restrictionsInfo.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createListeners() {
|
||||||
|
amountAsCoinListener = (ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue));
|
||||||
|
priceAsFiatListener = (ov, oldValue, newValue) -> price.set(formatter.formatFiat(newValue));
|
||||||
|
volumeAsFiatListener = (ov, oldValue, newValue) -> volume.set(formatter.formatFiat(newValue));
|
||||||
|
|
||||||
|
amountListener = (ov, oldValue, newValue) -> {
|
||||||
if (isBtcInputValid(newValue).isValid) {
|
if (isBtcInputValid(newValue).isValid) {
|
||||||
setAmountToModel();
|
setAmountToModel();
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
dataModel.calculateVolume();
|
dataModel.calculateVolume();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
priceListener = (ov, oldValue, newValue) -> {
|
||||||
price.addListener((ov, oldValue, newValue) -> {
|
|
||||||
if (isFiatInputValid(newValue).isValid) {
|
if (isFiatInputValid(newValue).isValid) {
|
||||||
setAmountToModel();
|
setAmountToModel();
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
dataModel.calculateVolume();
|
dataModel.calculateVolume();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
volumeListener = (ov, oldValue, newValue) -> {
|
||||||
volume.addListener((ov, oldValue, newValue) -> {
|
|
||||||
if (isFiatInputValid(newValue).isValid) {
|
if (isFiatInputValid(newValue).isValid) {
|
||||||
setPriceToModel();
|
setPriceToModel();
|
||||||
setVolumeToModel();
|
setVolumeToModel();
|
||||||
dataModel.calculateAmount();
|
dataModel.calculateAmount();
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||||
dataModel.amountAsCoinProperty().addListener((ov, oldValue, newValue) -> amount.set(formatter.formatCoin
|
dataModel.amountAsCoinProperty().addListener(amountAsCoinListener);
|
||||||
(newValue)));
|
dataModel.priceAsFiatProperty().addListener(priceAsFiatListener);
|
||||||
dataModel.priceAsFiatProperty().addListener((ov, oldValue, newValue) -> price.set(formatter.formatFiat(newValue)));
|
dataModel.volumeAsFiatProperty().addListener(volumeAsFiatListener);
|
||||||
dataModel.volumeAsFiatProperty().addListener((ov, oldValue, newValue) -> volume.set(formatter.formatFiat
|
|
||||||
(newValue)));
|
// Bidirectional bindings are used for all input fields: amount, price and volume
|
||||||
|
// We do volume/amount calculation during input, so user has immediate feedback
|
||||||
|
amount.addListener(amountListener);
|
||||||
|
price.addListener(priceListener);
|
||||||
|
volume.addListener(volumeListener);
|
||||||
|
|
||||||
|
amount.set("1");
|
||||||
|
price.set("300");
|
||||||
|
setAmountToModel();
|
||||||
|
setPriceToModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
private void removeListeners() {
|
||||||
dataModel.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
|
amount.removeListener(amountListener);
|
||||||
|
price.removeListener(priceListener);
|
||||||
|
volume.removeListener(volumeListener);
|
||||||
|
|
||||||
|
dataModel.amountAsCoinProperty().removeListener(amountAsCoinListener);
|
||||||
|
dataModel.priceAsFiatProperty().removeListener(priceAsFiatListener);
|
||||||
|
dataModel.volumeAsFiatProperty().removeListener(volumeAsFiatListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isTradable(Offer offer) {
|
|
||||||
return dataModel.isTradable(offer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void setDirection(Offer.Direction direction) {
|
void setDirection(Offer.Direction direction) {
|
||||||
dataModel.setDirection(direction);
|
dataModel.setDirection(direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI actions
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void onCancelOpenOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
dataModel.onCancelOpenOffer(offer, resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Getters
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
boolean isTradable(Offer offer) {
|
||||||
|
return dataModel.isTradable(offer);
|
||||||
|
}
|
||||||
|
|
||||||
SortedList<OfferBookListItem> getOfferList() {
|
SortedList<OfferBookListItem> getOfferList() {
|
||||||
return dataModel.getOfferList();
|
return dataModel.getOfferList();
|
||||||
}
|
}
|
||||||
@ -162,6 +232,11 @@ class OfferBookViewModel extends ActivatableWithDataModel<OfferBookDataModel> im
|
|||||||
return dataModel.getPriceAsFiat();
|
return dataModel.getPriceAsFiat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
private InputValidator.ValidationResult isBtcInputValid(String input) {
|
||||||
return optionalBtcValidator.validate(input);
|
return optionalBtcValidator.validate(input);
|
||||||
}
|
}
|
||||||
|
@ -46,11 +46,6 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
|
||||||
* Domain for that UI element.
|
|
||||||
* Note that the create offer domain has a deeper scope in the application domain (TradeManager).
|
|
||||||
* That model is just responsible for the domain specific parts displayed needed in that UI element.
|
|
||||||
*/
|
|
||||||
class TakeOfferDataModel implements Activatable, DataModel {
|
class TakeOfferDataModel implements Activatable, DataModel {
|
||||||
private static final Logger log = LoggerFactory.getLogger(TakeOfferDataModel.class);
|
private static final Logger log = LoggerFactory.getLogger(TakeOfferDataModel.class);
|
||||||
|
|
||||||
@ -73,6 +68,13 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
|||||||
final ObjectProperty<Coin> offerFeeAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> offerFeeAsCoin = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Coin> networkFeeAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> networkFeeAsCoin = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
|
private BalanceListener balanceListener;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TakeOfferDataModel(TradeManager tradeManager,
|
public TakeOfferDataModel(TradeManager tradeManager,
|
||||||
WalletService walletService,
|
WalletService walletService,
|
||||||
@ -87,59 +89,90 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activate() {
|
public void activate() {
|
||||||
btcCode.bind(preferences.btcDenominationProperty());
|
addBindings();
|
||||||
|
addListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deactivate() {
|
public void deactivate() {
|
||||||
btcCode.unbind();
|
removeBindings();
|
||||||
|
removeListeners();
|
||||||
tradeManager.onCancelAvailabilityRequest(offer);
|
tradeManager.onCancelAvailabilityRequest(offer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addBindings() {
|
||||||
|
btcCode.bind(preferences.btcDenominationProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBindings() {
|
||||||
|
btcCode.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
|
walletService.addBalanceListener(balanceListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeListeners() {
|
||||||
|
if (addressEntry != null)
|
||||||
|
walletService.removeBalanceListener(balanceListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void initWithData(Coin amount, Offer offer) {
|
void initWithData(Coin amount, Offer offer) {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
securityDepositAsCoin.set(offer.getSecurityDeposit());
|
securityDepositAsCoin.set(offer.getSecurityDeposit());
|
||||||
|
|
||||||
if (amount != null &&
|
if (amount != null && !amount.isGreaterThan(offer.getAmount()) && !offer.getMinAmount().isGreaterThan(amount))
|
||||||
!amount.isGreaterThan(offer.getAmount()) &&
|
|
||||||
!offer.getMinAmount().isGreaterThan(amount)) {
|
|
||||||
amountAsCoin.set(amount);
|
amountAsCoin.set(amount);
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
amountAsCoin.set(offer.getAmount());
|
amountAsCoin.set(offer.getAmount());
|
||||||
}
|
|
||||||
|
|
||||||
calculateVolume();
|
calculateVolume();
|
||||||
calculateTotalToPay();
|
calculateTotalToPay();
|
||||||
|
|
||||||
addressEntry = walletService.getAddressEntry(offer.getId());
|
addressEntry = walletService.getAddressEntry(offer.getId());
|
||||||
walletService.addBalanceListener(new BalanceListener(addressEntry.getAddress()) {
|
assert addressEntry != null;
|
||||||
|
|
||||||
|
balanceListener = new BalanceListener(addressEntry.getAddress()) {
|
||||||
@Override
|
@Override
|
||||||
public void onBalanceChanged(@NotNull Coin balance) {
|
public void onBalanceChanged(@NotNull Coin balance) {
|
||||||
updateBalance(balance);
|
updateBalance(balance);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
updateBalance(walletService.getBalanceForAddress(addressEntry.getAddress()));
|
updateBalance(walletService.getBalanceForAddress(addressEntry.getAddress()));
|
||||||
|
|
||||||
tradeManager.onCheckOfferAvailability(offer);
|
tradeManager.onCheckOfferAvailability(offer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI calls
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void onTakeOffer(TakeOfferResultHandler handler) {
|
void onTakeOffer(TakeOfferResultHandler handler) {
|
||||||
tradeManager.onTakeOffer(amountAsCoin.get(), offer, handler::handleResult);
|
tradeManager.onTakeOffer(amountAsCoin.get(), offer, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onSecurityDepositInfoDisplayed() {
|
||||||
|
preferences.setDisplaySecurityDepositInfo(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void calculateVolume() {
|
void calculateVolume() {
|
||||||
try {
|
|
||||||
if (offer != null &&
|
if (offer != null &&
|
||||||
offer.getPrice() != null &&
|
offer.getPrice() != null &&
|
||||||
amountAsCoin.get() != null &&
|
amountAsCoin.get() != null &&
|
||||||
!amountAsCoin.get().isZero()) {
|
!amountAsCoin.get().isZero()) {
|
||||||
volumeAsFiat.set(new ExchangeRate(offer.getPrice()).coinToFiat(amountAsCoin.get()));
|
volumeAsFiat.set(new ExchangeRate(offer.getPrice()).coinToFiat(amountAsCoin.get()));
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
|
||||||
// Should be never reached
|
|
||||||
log.error(t.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void calculateTotalToPay() {
|
void calculateTotalToPay() {
|
||||||
@ -149,6 +182,15 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
|||||||
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()).add(amountAsCoin.get()));
|
totalToPayAsCoin.set(offerFeeAsCoin.get().add(networkFeeAsCoin.get()).add(securityDepositAsCoin.get()).add(amountAsCoin.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateBalance(@NotNull Coin balance) {
|
||||||
|
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Getters
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
Offer.Direction getDirection() {
|
Offer.Direction getDirection() {
|
||||||
return offer.getDirection();
|
return offer.getDirection();
|
||||||
}
|
}
|
||||||
@ -168,15 +210,10 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean getDisplaySecurityDepositInfo() {
|
boolean getDisplaySecurityDepositInfo() {
|
||||||
return preferences.getDisplaySecurityDepositInfo();
|
return preferences.getDisplaySecurityDepositInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
void securityDepositInfoDisplayed() {
|
|
||||||
preferences.setDisplaySecurityDepositInfo(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
WalletService getWalletService() {
|
WalletService getWalletService() {
|
||||||
return walletService;
|
return walletService;
|
||||||
}
|
}
|
||||||
@ -184,10 +221,4 @@ class TakeOfferDataModel implements Activatable, DataModel {
|
|||||||
AddressEntry getAddressEntry() {
|
AddressEntry getAddressEntry() {
|
||||||
return addressEntry;
|
return addressEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void updateBalance(@NotNull Coin balance) {
|
|
||||||
isWalletFunded.set(totalToPayAsCoin.get() != null && balance.compareTo(totalToPayAsCoin.get()) >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0"
|
||||||
xmlns:fx="http://javafx.com/fxml">
|
xmlns:fx="http://javafx.com/fxml">
|
||||||
|
|
||||||
<ScrollPane fx:id="scrollPane" hbarPolicy="NEVER" vbarPolicy="NEVER" fitToWidth="true" fitToHeight="true"
|
<ScrollPane fx:id="scrollPane" onScroll="#onScroll" hbarPolicy="NEVER" vbarPolicy="NEVER" fitToWidth="true" fitToHeight="true"
|
||||||
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"
|
||||||
AnchorPane.bottomAnchor="0.0">
|
AnchorPane.bottomAnchor="0.0">
|
||||||
|
|
||||||
|
@ -72,6 +72,9 @@ import static javafx.beans.binding.Bindings.createStringBinding;
|
|||||||
@FxmlView
|
@FxmlView
|
||||||
public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOfferViewModel> {
|
public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOfferViewModel> {
|
||||||
|
|
||||||
|
private final Navigation navigation;
|
||||||
|
private final OverlayManager overlayManager;
|
||||||
|
|
||||||
@FXML ScrollPane scrollPane;
|
@FXML ScrollPane scrollPane;
|
||||||
@FXML ImageView imageView;
|
@FXML ImageView imageView;
|
||||||
@FXML InputTextField amountTextField;
|
@FXML InputTextField amountTextField;
|
||||||
@ -94,11 +97,19 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||||||
private ImageView collapse;
|
private ImageView collapse;
|
||||||
private PopOver totalToPayInfoPopover;
|
private PopOver totalToPayInfoPopover;
|
||||||
|
|
||||||
private final Navigation navigation;
|
|
||||||
private final OverlayManager overlayManager;
|
|
||||||
private OfferView.CloseHandler closeHandler;
|
private OfferView.CloseHandler closeHandler;
|
||||||
|
|
||||||
private ChangeListener<String> errorMessageChangeListener;
|
private ChangeListener<String> errorMessageChangeListener;
|
||||||
|
private ChangeListener<Boolean> amountFocusedListener;
|
||||||
|
private ChangeListener<Boolean> isTakeOfferSpinnerVisibleListener;
|
||||||
|
private ChangeListener<TakeOfferViewModel.State> stateListener;
|
||||||
|
private ChangeListener<Boolean> showWarningInvalidBtcDecimalPlacesListener;
|
||||||
|
private ChangeListener<Boolean> showTransactionPublishedScreenListener;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private TakeOfferView(TakeOfferViewModel model, Navigation navigation,
|
private TakeOfferView(TakeOfferViewModel model, Navigation navigation,
|
||||||
@ -107,19 +118,153 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||||||
|
|
||||||
this.navigation = navigation;
|
this.navigation = navigation;
|
||||||
this.overlayManager = overlayManager;
|
this.overlayManager = overlayManager;
|
||||||
|
|
||||||
|
createListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
setupListeners();
|
}
|
||||||
setupBindings();
|
|
||||||
|
@Override
|
||||||
|
protected void doActivate() {
|
||||||
|
addListeners();
|
||||||
|
addBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doDeactivate() {
|
protected void doDeactivate() {
|
||||||
model.errorMessage.removeListener(errorMessageChangeListener);
|
removeBindings();
|
||||||
|
removeListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addBindings() {
|
||||||
|
amountBtcLabel.textProperty().bind(model.btcCode);
|
||||||
|
amountTextField.textProperty().bindBidirectional(model.amount);
|
||||||
|
volumeTextField.textProperty().bindBidirectional(model.volume);
|
||||||
|
totalToPayTextField.textProperty().bind(model.totalToPay);
|
||||||
|
addressTextField.amountAsCoinProperty().bind(model.totalToPayAsCoin);
|
||||||
|
amountDescriptionLabel.textProperty().bind(model.amountDescription);
|
||||||
|
amountTextField.validationResultProperty().bind(model.amountValidationResult);
|
||||||
|
takeOfferButton.disableProperty().bind(model.takeOfferButtonDisabled);
|
||||||
|
takeOfferSpinnerInfoLabel.visibleProperty().bind(model.isTakeOfferSpinnerVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBindings() {
|
||||||
|
amountBtcLabel.textProperty().unbind();
|
||||||
|
amountTextField.textProperty().unbindBidirectional(model.amount);
|
||||||
|
volumeTextField.textProperty().unbindBidirectional(model.volume);
|
||||||
|
totalToPayTextField.textProperty().unbind();
|
||||||
|
addressTextField.amountAsCoinProperty().unbind();
|
||||||
|
amountDescriptionLabel.textProperty().unbind();
|
||||||
|
amountTextField.validationResultProperty().unbind();
|
||||||
|
takeOfferButton.disableProperty().unbind();
|
||||||
|
takeOfferSpinnerInfoLabel.visibleProperty().unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void createListeners() {
|
||||||
|
errorMessageChangeListener = (o, oldValue, newValue) -> {
|
||||||
|
if (newValue != null) {
|
||||||
|
Popups.openErrorPopup(BSResources.get("shared.error"), BSResources.get("takeOffer.error.message", model.errorMessage.get()));
|
||||||
|
Popups.removeBlurContent();
|
||||||
|
Platform.runLater(this::close);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
amountFocusedListener = (o, oldValue, newValue) -> {
|
||||||
|
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
||||||
|
amountTextField.setText(model.amount.get());
|
||||||
|
};
|
||||||
|
isTakeOfferSpinnerVisibleListener = (ov, oldValue, newValue) -> {
|
||||||
|
takeOfferSpinner.setProgress(newValue ? -1 : 0);
|
||||||
|
takeOfferSpinner.setVisible(newValue);
|
||||||
|
};
|
||||||
|
stateListener = (ov, oldValue, newValue) -> {
|
||||||
|
switch (newValue) {
|
||||||
|
case CHECK_AVAILABILITY:
|
||||||
|
showCheckAvailabilityScreen();
|
||||||
|
break;
|
||||||
|
case AMOUNT_SCREEN:
|
||||||
|
showAmountScreen();
|
||||||
|
break;
|
||||||
|
case PAYMENT_SCREEN:
|
||||||
|
setupPaymentScreen();
|
||||||
|
break;
|
||||||
|
case DETAILS_SCREEN:
|
||||||
|
showDetailsScreen();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
showWarningInvalidBtcDecimalPlacesListener = (o, oldValue, newValue) -> {
|
||||||
|
if (newValue) {
|
||||||
|
Popups.openWarningPopup(BSResources.get("shared.warning"),
|
||||||
|
BSResources.get("takeOffer.amountPriceBox.warning.invalidBtcDecimalPlaces"));
|
||||||
|
model.showWarningInvalidBtcDecimalPlaces.set(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
showTransactionPublishedScreenListener = (o, oldValue, newValue) -> {
|
||||||
|
// TODO temp just for testing
|
||||||
|
newValue = false;
|
||||||
|
close();
|
||||||
|
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||||
|
|
||||||
|
if (newValue) {
|
||||||
|
overlayManager.blurContent();
|
||||||
|
|
||||||
|
// Dialogs are a bit limited. There is no callback for the InformationDialog button click, so we added
|
||||||
|
// our own actions.
|
||||||
|
List<Action> actions = new ArrayList<>();
|
||||||
|
/* actions.add(new AbstractAction(BSResources.get("shared.copyTxId")) {
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent actionEvent) {
|
||||||
|
getProperties().put("type", "COPY");
|
||||||
|
Utilities.copyToClipboard(model.transactionId.get());
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent actionEvent) {
|
||||||
|
getProperties().put("type", "CLOSE");
|
||||||
|
try {
|
||||||
|
close();
|
||||||
|
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Dialog.Actions.CLOSE.handle(actionEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Popups.openInfoPopup(BSResources.get("takeOffer.success.headline"),
|
||||||
|
BSResources.get("takeOffer.success.info"),
|
||||||
|
actions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
|
amountTextField.focusedProperty().addListener(amountFocusedListener);
|
||||||
|
model.isTakeOfferSpinnerVisible.addListener(isTakeOfferSpinnerVisibleListener);
|
||||||
|
model.state.addListener(stateListener);
|
||||||
|
model.showWarningInvalidBtcDecimalPlaces.addListener(showWarningInvalidBtcDecimalPlacesListener);
|
||||||
|
model.errorMessage.addListener(errorMessageChangeListener);
|
||||||
|
model.showTransactionPublishedScreen.addListener(showTransactionPublishedScreenListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeListeners() {
|
||||||
|
amountTextField.focusedProperty().removeListener(amountFocusedListener);
|
||||||
|
model.isTakeOfferSpinnerVisible.removeListener(isTakeOfferSpinnerVisibleListener);
|
||||||
|
model.state.removeListener(stateListener);
|
||||||
|
model.showWarningInvalidBtcDecimalPlaces.removeListener(showWarningInvalidBtcDecimalPlacesListener);
|
||||||
|
model.errorMessage.removeListener(errorMessageChangeListener);
|
||||||
|
model.showTransactionPublishedScreen.removeListener(showTransactionPublishedScreenListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void initWithData(Coin amount, Offer offer) {
|
public void initWithData(Coin amount, Offer offer) {
|
||||||
model.initWithData(amount, offer);
|
model.initWithData(amount, offer);
|
||||||
|
|
||||||
@ -156,6 +301,11 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||||||
this.closeHandler = closeHandler;
|
this.closeHandler = closeHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI actions
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onTakeOffer() {
|
void onTakeOffer() {
|
||||||
model.onTakeOffer();
|
model.onTakeOffer();
|
||||||
@ -166,11 +316,15 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||||||
model.onShowPaymentScreen();
|
model.onShowPaymentScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
void onScroll() {
|
||||||
|
InputTextField.hideErrorMessageDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
void onToggleShowAdvancedSettings() {
|
void onToggleShowAdvancedSettings() {
|
||||||
model.detailsVisible = !model.detailsVisible;
|
model.onToggleShowAdvancedSettings();
|
||||||
if (model.detailsVisible) {
|
if (model.isDetailsVisible()) {
|
||||||
showAdvancedSettingsButton.setText(BSResources.get("takeOffer.fundsBox.hideAdvanced"));
|
showAdvancedSettingsButton.setText(BSResources.get("takeOffer.fundsBox.hideAdvanced"));
|
||||||
showAdvancedSettingsButton.setGraphic(collapse);
|
showAdvancedSettingsButton.setGraphic(collapse);
|
||||||
showDetailsScreen();
|
showDetailsScreen();
|
||||||
@ -197,117 +351,10 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||||||
Help.openWindow(HelpId.TAKE_OFFER_ADVANCED);
|
Help.openWindow(HelpId.TAKE_OFFER_ADVANCED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void close() {
|
|
||||||
if (closeHandler != null)
|
|
||||||
closeHandler.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupListeners() {
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
scrollPane.setOnScroll(e -> InputTextField.hideErrorMessageDisplay());
|
// States/screens
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// focus out
|
|
||||||
amountTextField.focusedProperty().addListener((o, oldValue, newValue) -> {
|
|
||||||
model.onFocusOutAmountTextField(oldValue, newValue, amountTextField.getText());
|
|
||||||
amountTextField.setText(model.amount.get());
|
|
||||||
});
|
|
||||||
|
|
||||||
model.state.addListener((ov, oldValue, newValue) -> {
|
|
||||||
switch (newValue) {
|
|
||||||
case CHECK_AVAILABILITY:
|
|
||||||
showCheckAvailabilityScreen();
|
|
||||||
break;
|
|
||||||
case AMOUNT_SCREEN:
|
|
||||||
showAmountScreen();
|
|
||||||
break;
|
|
||||||
case PAYMENT_SCREEN:
|
|
||||||
setupPaymentScreen();
|
|
||||||
break;
|
|
||||||
case DETAILS_SCREEN:
|
|
||||||
showDetailsScreen();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// warnings
|
|
||||||
model.showWarningInvalidBtcDecimalPlaces.addListener((o, oldValue, newValue) -> {
|
|
||||||
if (newValue) {
|
|
||||||
Popups.openWarningPopup(BSResources.get("shared.warning"),
|
|
||||||
BSResources.get("takeOffer.amountPriceBox.warning.invalidBtcDecimalPlaces"));
|
|
||||||
model.showWarningInvalidBtcDecimalPlaces.set(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
errorMessageChangeListener = (o, oldValue, newValue) -> {
|
|
||||||
if (newValue != null) {
|
|
||||||
Popups.openErrorPopup(BSResources.get("shared.error"), BSResources.get("takeOffer.error.message", model.errorMessage.get()));
|
|
||||||
Popups.removeBlurContent();
|
|
||||||
Platform.runLater(this::close);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
model.errorMessage.addListener(errorMessageChangeListener);
|
|
||||||
|
|
||||||
model.showTransactionPublishedScreen.addListener((o, oldValue, newValue) -> {
|
|
||||||
// TODO temp just for testing
|
|
||||||
newValue = false;
|
|
||||||
close();
|
|
||||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
|
||||||
|
|
||||||
if (newValue) {
|
|
||||||
overlayManager.blurContent();
|
|
||||||
|
|
||||||
// Dialogs are a bit limited. There is no callback for the InformationDialog button click, so we added
|
|
||||||
// our own actions.
|
|
||||||
List<Action> actions = new ArrayList<>();
|
|
||||||
/* actions.add(new AbstractAction(BSResources.get("shared.copyTxId")) {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent actionEvent) {
|
|
||||||
getProperties().put("type", "COPY");
|
|
||||||
Utilities.copyToClipboard(model.transactionId.get());
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
actions.add(new AbstractAction(BSResources.get("shared.close")) {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent actionEvent) {
|
|
||||||
getProperties().put("type", "CLOSE");
|
|
||||||
try {
|
|
||||||
close();
|
|
||||||
navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
Dialog.Actions.CLOSE.handle(actionEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Popups.openInfoPopup(BSResources.get("takeOffer.success.headline"),
|
|
||||||
BSResources.get("takeOffer.success.info"),
|
|
||||||
actions);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupBindings() {
|
|
||||||
amountBtcLabel.textProperty().bind(model.btcCode);
|
|
||||||
amountTextField.textProperty().bindBidirectional(model.amount);
|
|
||||||
volumeTextField.textProperty().bindBidirectional(model.volume);
|
|
||||||
totalToPayTextField.textProperty().bind(model.totalToPay);
|
|
||||||
addressTextField.amountAsCoinProperty().bind(model.totalToPayAsCoin);
|
|
||||||
amountDescriptionLabel.textProperty().bind(model.amountDescription);
|
|
||||||
|
|
||||||
|
|
||||||
// Validation
|
|
||||||
amountTextField.validationResultProperty().bind(model.amountValidationResult);
|
|
||||||
|
|
||||||
// buttons
|
|
||||||
takeOfferButton.disableProperty().bind(model.takeOfferButtonDisabled);
|
|
||||||
|
|
||||||
takeOfferSpinnerInfoLabel.visibleProperty().bind(model.isTakeOfferSpinnerVisible);
|
|
||||||
|
|
||||||
model.isTakeOfferSpinnerVisible.addListener((ov, oldValue, newValue) -> {
|
|
||||||
takeOfferSpinner.setProgress(newValue ? -1 : 0);
|
|
||||||
takeOfferSpinner.setVisible(newValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showCheckAvailabilityScreen() {
|
private void showCheckAvailabilityScreen() {
|
||||||
|
|
||||||
@ -376,12 +423,9 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
|
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
|
||||||
scrollPane.layout();
|
scrollPane.layout();
|
||||||
|
|
||||||
model.advancedScreenInited = !model.advancedScreenInited;
|
|
||||||
|
|
||||||
toggleDetailsScreen(true);
|
toggleDetailsScreen(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void hideDetailsScreen() {
|
private void hideDetailsScreen() {
|
||||||
payFundsPane.setActive();
|
payFundsPane.setActive();
|
||||||
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||||
@ -418,6 +462,16 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
|
|||||||
advancedInfoDisplay.setVisible(visible);
|
advancedInfoDisplay.setVisible(visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void close() {
|
||||||
|
if (closeHandler != null)
|
||||||
|
closeHandler.close();
|
||||||
|
}
|
||||||
|
|
||||||
private void setupTotalToPayInfoIconLabel() {
|
private void setupTotalToPayInfoIconLabel() {
|
||||||
totalToPayInfoIconLabel.setId("clickable-icon");
|
totalToPayInfoIconLabel.setId("clickable-icon");
|
||||||
AwesomeDude.setIcon(totalToPayInfoIconLabel, AwesomeIcon.QUESTION_SIGN);
|
AwesomeDude.setIcon(totalToPayInfoIconLabel, AwesomeIcon.QUESTION_SIGN);
|
||||||
|
@ -27,9 +27,11 @@ import io.bitsquare.locale.BSResources;
|
|||||||
import io.bitsquare.locale.CurrencyUtil;
|
import io.bitsquare.locale.CurrencyUtil;
|
||||||
import io.bitsquare.trade.BuyerAsTakerTrade;
|
import io.bitsquare.trade.BuyerAsTakerTrade;
|
||||||
import io.bitsquare.trade.SellerAsTakerTrade;
|
import io.bitsquare.trade.SellerAsTakerTrade;
|
||||||
|
import io.bitsquare.trade.Trade;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.states.BuyerTradeState;
|
import io.bitsquare.trade.states.BuyerTradeState;
|
||||||
import io.bitsquare.trade.states.SellerTradeState;
|
import io.bitsquare.trade.states.SellerTradeState;
|
||||||
|
import io.bitsquare.trade.states.TradeState;
|
||||||
|
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.core.Address;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
@ -42,6 +44,7 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -58,6 +61,12 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
DETAILS_SCREEN
|
DETAILS_SCREEN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final BtcValidator btcValidator;
|
||||||
|
private final BSFormatter formatter;
|
||||||
|
private final String offerFee;
|
||||||
|
private final String networkFee;
|
||||||
|
|
||||||
|
// static fields
|
||||||
private String amountRange;
|
private String amountRange;
|
||||||
private String price;
|
private String price;
|
||||||
private String directionLabel;
|
private String directionLabel;
|
||||||
@ -69,17 +78,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
private String acceptedArbitratorIds;
|
private String acceptedArbitratorIds;
|
||||||
private String addressAsString;
|
private String addressAsString;
|
||||||
private String paymentLabel;
|
private String paymentLabel;
|
||||||
|
private boolean detailsVisible;
|
||||||
|
|
||||||
// Needed for the addressTextField
|
|
||||||
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
|
||||||
final ObjectProperty<State> state = new SimpleObjectProperty<>(State.CHECK_AVAILABILITY);
|
|
||||||
|
|
||||||
private final BtcValidator btcValidator;
|
|
||||||
private final BSFormatter formatter;
|
|
||||||
private final String offerFee;
|
|
||||||
private final String networkFee;
|
|
||||||
boolean detailsVisible;
|
|
||||||
boolean advancedScreenInited;
|
|
||||||
|
|
||||||
final StringProperty amount = new SimpleStringProperty();
|
final StringProperty amount = new SimpleStringProperty();
|
||||||
final StringProperty volume = new SimpleStringProperty();
|
final StringProperty volume = new SimpleStringProperty();
|
||||||
@ -99,12 +99,30 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
|
final BooleanProperty showWarningInvalidBtcDecimalPlaces = new SimpleBooleanProperty();
|
||||||
final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
|
final BooleanProperty showTransactionPublishedScreen = new SimpleBooleanProperty();
|
||||||
|
|
||||||
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
|
|
||||||
|
|
||||||
// Needed for the addressTextField
|
// Needed for the addressTextField
|
||||||
|
final ObjectProperty<Address> address = new SimpleObjectProperty<>();
|
||||||
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
final ObjectProperty<Coin> totalToPayAsCoin = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
|
final ObjectProperty<State> state = new SimpleObjectProperty<>(State.CHECK_AVAILABILITY);
|
||||||
|
final ObjectProperty<InputValidator.ValidationResult> amountValidationResult = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
private boolean takeOfferRequested;
|
private boolean takeOfferRequested;
|
||||||
private boolean isAmountAndPriceValidAndWalletFunded;
|
|
||||||
|
// listeners
|
||||||
|
private ChangeListener<String> amountChangeListener;
|
||||||
|
private ChangeListener<Boolean> isWalletFundedChangeListener;
|
||||||
|
private ChangeListener<Coin> amountAsCoinChangeListener;
|
||||||
|
private ChangeListener<Offer.State> offerStateChangeListener;
|
||||||
|
private ChangeListener<TradeState.ProcessState> tradeStateChangeListener;
|
||||||
|
// Offer and trade are stored only for remove listener at deactivate
|
||||||
|
private Offer offer;
|
||||||
|
private Trade trade;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, lifecycle
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator, BSFormatter formatter) {
|
public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator, BSFormatter formatter) {
|
||||||
@ -116,23 +134,94 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
this.offerFee = formatter.formatCoinWithCode(dataModel.offerFeeAsCoin.get());
|
this.offerFee = formatter.formatCoinWithCode(dataModel.offerFeeAsCoin.get());
|
||||||
this.networkFee = formatter.formatCoinWithCode(dataModel.networkFeeAsCoin.get());
|
this.networkFee = formatter.formatCoinWithCode(dataModel.networkFeeAsCoin.get());
|
||||||
|
|
||||||
setupBindings();
|
createListeners();
|
||||||
setupListeners();
|
|
||||||
applyTakeOfferRequestResult(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setOfferBookFilter is a one time call
|
@Override
|
||||||
|
protected void doActivate() {
|
||||||
|
addBindings();
|
||||||
|
addListeners();
|
||||||
|
isTakeOfferSpinnerVisible.set(false);
|
||||||
|
showTransactionPublishedScreen.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDeactivate() {
|
||||||
|
removeBindings();
|
||||||
|
removeListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addBindings() {
|
||||||
|
volume.bind(createStringBinding(() -> formatter.formatFiatWithCode(dataModel.volumeAsFiat.get()), dataModel.volumeAsFiat));
|
||||||
|
totalToPay.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.totalToPayAsCoin.get()), dataModel.totalToPayAsCoin));
|
||||||
|
securityDeposit.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.securityDepositAsCoin.get()), dataModel.securityDepositAsCoin));
|
||||||
|
totalToPayAsCoin.bind(dataModel.totalToPayAsCoin);
|
||||||
|
btcCode.bind(dataModel.btcCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeBindings() {
|
||||||
|
volume.unbind();
|
||||||
|
totalToPay.unbind();
|
||||||
|
securityDeposit.unbind();
|
||||||
|
totalToPayAsCoin.unbind();
|
||||||
|
btcCode.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createListeners() {
|
||||||
|
amountChangeListener = (ov, oldValue, newValue) -> {
|
||||||
|
if (isBtcInputValid(newValue).isValid) {
|
||||||
|
setAmountToModel();
|
||||||
|
calculateVolume();
|
||||||
|
dataModel.calculateTotalToPay();
|
||||||
|
}
|
||||||
|
evaluateViewState();
|
||||||
|
};
|
||||||
|
isWalletFundedChangeListener = (ov, oldValue, newValue) -> evaluateViewState();
|
||||||
|
amountAsCoinChangeListener = (ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue));
|
||||||
|
offerStateChangeListener = (ov, oldValue, newValue) -> applyOfferState(newValue);
|
||||||
|
tradeStateChangeListener = (ov, oldValue, newValue) -> applyTradeState(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListeners() {
|
||||||
|
// Bidirectional bindings are used for all input fields: amount, price, volume and minAmount
|
||||||
|
// We do volume/amount calculation during input, so user has immediate feedback
|
||||||
|
amount.addListener(amountChangeListener);
|
||||||
|
dataModel.isWalletFunded.addListener(isWalletFundedChangeListener);
|
||||||
|
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
||||||
|
dataModel.amountAsCoin.addListener(amountAsCoinChangeListener);
|
||||||
|
|
||||||
|
amountChangeListener.changed(null, null, amount.get());
|
||||||
|
isWalletFundedChangeListener.changed(null, null, dataModel.isWalletFunded.get());
|
||||||
|
amountAsCoinChangeListener.changed(null, null, dataModel.amountAsCoin.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeListeners() {
|
||||||
|
amount.removeListener(amountChangeListener);
|
||||||
|
dataModel.isWalletFunded.removeListener(isWalletFundedChangeListener);
|
||||||
|
dataModel.amountAsCoin.removeListener(amountAsCoinChangeListener);
|
||||||
|
|
||||||
|
if (offer != null)
|
||||||
|
offer.stateProperty().removeListener(offerStateChangeListener);
|
||||||
|
|
||||||
|
if (trade != null)
|
||||||
|
trade.processStateProperty().removeListener(tradeStateChangeListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// API
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void initWithData(Coin amount, Offer offer) {
|
void initWithData(Coin amount, Offer offer) {
|
||||||
dataModel.initWithData(amount, offer);
|
dataModel.initWithData(amount, offer);
|
||||||
|
|
||||||
directionLabel = offer.getDirection() == Offer.Direction.SELL ?
|
this.offer = offer;
|
||||||
BSResources.get("shared.buyBitcoin") : BSResources.get("shared.sellBitcoin");
|
|
||||||
|
directionLabel = offer.getDirection() == Offer.Direction.SELL ? BSResources.get("shared.buyBitcoin") : BSResources.get("shared.sellBitcoin");
|
||||||
|
|
||||||
fiatCode.set(offer.getCurrencyCode());
|
fiatCode.set(offer.getCurrencyCode());
|
||||||
if (!dataModel.isMinAmountLessOrEqualAmount()) {
|
if (!dataModel.isMinAmountLessOrEqualAmount())
|
||||||
amountValidationResult.set(new InputValidator.ValidationResult(false,
|
amountValidationResult.set(new InputValidator.ValidationResult(false, BSResources.get("takeOffer.validation.amountSmallerThanMinAmount")));
|
||||||
BSResources.get("takeOffer.validation.amountSmallerThanMinAmount")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
if (dataModel.getDirection() == Offer.Direction.BUY) {
|
||||||
amountDescription.set(BSResources.get("takeOffer.amountPriceBox.buy.amountDescription", offer.getId()));
|
amountDescription.set(BSResources.get("takeOffer.amountPriceBox.buy.amountDescription", offer.getId()));
|
||||||
@ -147,17 +236,13 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
fundsBoxInfoDisplay.set(BSResources.get("takeOffer.fundsBox.sell.info"));
|
fundsBoxInfoDisplay.set(BSResources.get("takeOffer.fundsBox.sell.info"));
|
||||||
}
|
}
|
||||||
|
|
||||||
//model.volumeAsFiat.set(offer.getVolumeByAmount(model.amountAsCoin.get()));
|
amountRange = formatter.formatCoinWithCode(offer.getMinAmount()) + " - " + formatter.formatCoinWithCode(offer.getAmount());
|
||||||
|
|
||||||
amountRange = formatter.formatCoinWithCode(offer.getMinAmount()) + " - " +
|
|
||||||
formatter.formatCoinWithCode(offer.getAmount());
|
|
||||||
price = formatter.formatFiatWithCode(offer.getPrice());
|
price = formatter.formatFiatWithCode(offer.getPrice());
|
||||||
|
|
||||||
paymentLabel = BSResources.get("takeOffer.fundsBox.paymentLabel", offer.getId());
|
paymentLabel = BSResources.get("takeOffer.fundsBox.paymentLabel", offer.getId());
|
||||||
if (dataModel.getAddressEntry() != null) {
|
assert dataModel.getAddressEntry() != null;
|
||||||
addressAsString = dataModel.getAddressEntry().getAddress().toString();
|
addressAsString = dataModel.getAddressEntry().getAddress().toString();
|
||||||
address.set(dataModel.getAddressEntry().getAddress());
|
address.set(dataModel.getAddressEntry().getAddress());
|
||||||
}
|
|
||||||
|
|
||||||
acceptedCountries = formatter.countryLocalesToString(offer.getAcceptedCountries());
|
acceptedCountries = formatter.countryLocalesToString(offer.getAcceptedCountries());
|
||||||
acceptedLanguages = formatter.languageCodesToString(offer.getAcceptedLanguageCodes());
|
acceptedLanguages = formatter.languageCodesToString(offer.getAcceptedLanguageCodes());
|
||||||
@ -166,12 +251,43 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
bankAccountCurrency = BSResources.get(CurrencyUtil.getDisplayName(offer.getCurrencyCode()));
|
bankAccountCurrency = BSResources.get(CurrencyUtil.getDisplayName(offer.getCurrencyCode()));
|
||||||
bankAccountCounty = BSResources.get(offer.getBankAccountCountry().name);
|
bankAccountCounty = BSResources.get(offer.getBankAccountCountry().name);
|
||||||
|
|
||||||
offer.stateProperty().addListener((ov, oldValue, newValue) -> applyOfferState(newValue));
|
offer.stateProperty().addListener(offerStateChangeListener);
|
||||||
applyOfferState(offer.stateProperty().get());
|
applyOfferState(offer.stateProperty().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// UI actions
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void onTakeOffer() {
|
||||||
|
takeOfferRequested = true;
|
||||||
|
applyOnTakeOfferResult(false);
|
||||||
|
|
||||||
|
isTakeOfferSpinnerVisible.set(true);
|
||||||
|
dataModel.onTakeOffer((trade) -> {
|
||||||
|
this.trade = trade;
|
||||||
|
trade.processStateProperty().addListener(tradeStateChangeListener);
|
||||||
|
applyTradeState(trade.processStateProperty().get());
|
||||||
|
evaluateViewState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void onShowPaymentScreen() {
|
||||||
|
state.set(State.PAYMENT_SCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onToggleShowAdvancedSettings() {
|
||||||
|
detailsVisible = !detailsVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// States
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void applyOfferState(Offer.State state) {
|
private void applyOfferState(Offer.State state) {
|
||||||
log.debug("offer state = " + state);
|
log.debug("applyOfferState state = " + state);
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case UNDEFINED:
|
case UNDEFINED:
|
||||||
@ -201,14 +317,20 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
errorMessage.set("You cannot take that offer because the offerer is offline.");
|
errorMessage.set("You cannot take that offer because the offerer is offline.");
|
||||||
takeOfferRequested = false;
|
takeOfferRequested = false;
|
||||||
break;
|
break;
|
||||||
/* case FAULT:
|
case FAULT:
|
||||||
if (takeOfferRequested)
|
if (takeOfferRequested)
|
||||||
errorMessage.set("Take offer request failed.");
|
errorMessage.set("Take offer request failed.");
|
||||||
else
|
else
|
||||||
errorMessage.set("The check for the offer availability failed.");
|
errorMessage.set("The check for the offer availability failed.");
|
||||||
|
|
||||||
takeOfferRequested = false;
|
takeOfferRequested = false;
|
||||||
break;*/
|
break;
|
||||||
|
case TIMEOUT:
|
||||||
|
if (takeOfferRequested)
|
||||||
|
errorMessage.set("Take offer request failed due a timeout.");
|
||||||
|
else
|
||||||
|
errorMessage.set("The check for the offer availability failed due a timeout.");
|
||||||
|
takeOfferRequested = false;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
log.error("Unhandled offer state: " + state);
|
log.error("Unhandled offer state: " + state);
|
||||||
break;
|
break;
|
||||||
@ -218,31 +340,24 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
isTakeOfferSpinnerVisible.set(false);
|
isTakeOfferSpinnerVisible.set(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateState();
|
evaluateViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onTakeOffer() {
|
private void applyTradeState(TradeState.ProcessState state) {
|
||||||
takeOfferRequested = true;
|
log.debug("applyTradeState state = " + state);
|
||||||
applyTakeOfferRequestResult(false);
|
|
||||||
|
|
||||||
isTakeOfferSpinnerVisible.set(true);
|
String msg = "An error occurred.";
|
||||||
|
|
||||||
dataModel.onTakeOffer((trade) -> {
|
|
||||||
trade.processStateProperty().addListener((ov, oldValue, newValue) -> {
|
|
||||||
log.debug("trade state = " + newValue);
|
|
||||||
|
|
||||||
String msg = "";
|
|
||||||
if (trade.getErrorMessage() != null)
|
if (trade.getErrorMessage() != null)
|
||||||
msg = "\nError message: " + trade.getErrorMessage();
|
msg = "Error message: " + trade.getErrorMessage();
|
||||||
|
|
||||||
if (trade instanceof SellerAsTakerTrade) {
|
if (trade instanceof SellerAsTakerTrade) {
|
||||||
switch ((SellerTradeState.ProcessState) newValue) {
|
switch ((SellerTradeState.ProcessState) state) {
|
||||||
case UNDEFINED:
|
case UNDEFINED:
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
|
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
|
||||||
assert trade.getDepositTx() != null;
|
assert trade.getDepositTx() != null;
|
||||||
transactionId.set(trade.getDepositTx().getHashAsString());
|
transactionId.set(trade.getDepositTx().getHashAsString());
|
||||||
applyTakeOfferRequestResult(true);
|
applyOnTakeOfferResult(true);
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_CONFIRMED:
|
case DEPOSIT_CONFIRMED:
|
||||||
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
|
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
|
||||||
@ -252,32 +367,28 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
case PAYOUT_TX_COMMITTED:
|
case PAYOUT_TX_COMMITTED:
|
||||||
case PAYOUT_BROAD_CASTED:
|
case PAYOUT_BROAD_CASTED:
|
||||||
break;
|
break;
|
||||||
/* case TAKE_OFFER_FEE_PUBLISH_FAILED:
|
case TIMEOUT:
|
||||||
errorMessage.set("An error occurred when paying the trade fee." + msg);
|
errorMessage.set("A timeout occurred. Maybe there are connection problems. " +
|
||||||
|
"Please try later again.\n" + msg);
|
||||||
takeOfferRequested = false;
|
takeOfferRequested = false;
|
||||||
break;
|
break;
|
||||||
case MESSAGE_SENDING_FAILED:
|
case FAULT:
|
||||||
errorMessage.set("An error occurred when sending a message to the offerer. Maybe there are connection problems. " +
|
|
||||||
"Please try later again." + msg);
|
|
||||||
takeOfferRequested = false;
|
|
||||||
break;
|
|
||||||
case EXCEPTION:
|
|
||||||
errorMessage.set(msg);
|
errorMessage.set(msg);
|
||||||
takeOfferRequested = false;
|
takeOfferRequested = false;
|
||||||
break;*/
|
break;
|
||||||
default:
|
default:
|
||||||
log.warn("Unhandled trade state: " + newValue);
|
log.warn("Unhandled trade state: " + state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (trade instanceof BuyerAsTakerTrade) {
|
else if (trade instanceof BuyerAsTakerTrade) {
|
||||||
switch ((BuyerTradeState.ProcessState) newValue) {
|
switch ((BuyerTradeState.ProcessState) state) {
|
||||||
case UNDEFINED:
|
case UNDEFINED:
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_PUBLISHED:
|
case DEPOSIT_PUBLISHED:
|
||||||
assert trade.getDepositTx() != null;
|
assert trade.getDepositTx() != null;
|
||||||
transactionId.set(trade.getDepositTx().getHashAsString());
|
transactionId.set(trade.getDepositTx().getHashAsString());
|
||||||
applyTakeOfferRequestResult(true);
|
applyOnTakeOfferResult(true);
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_PUBLISHED_MSG_SENT:
|
case DEPOSIT_PUBLISHED_MSG_SENT:
|
||||||
case DEPOSIT_CONFIRMED:
|
case DEPOSIT_CONFIRMED:
|
||||||
@ -288,55 +399,32 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
case PAYOUT_TX_SENT:
|
case PAYOUT_TX_SENT:
|
||||||
case PAYOUT_BROAD_CASTED:
|
case PAYOUT_BROAD_CASTED:
|
||||||
break;
|
break;
|
||||||
/* case TAKE_OFFER_FEE_PUBLISH_FAILED:
|
|
||||||
errorMessage.set("An error occurred when paying the trade fee." + msg);
|
|
||||||
takeOfferRequested = false;
|
|
||||||
break;
|
|
||||||
case MESSAGE_SENDING_FAILED:
|
|
||||||
errorMessage.set("Sending a message to the offerer failed. Maybe there are connection problems. " +
|
|
||||||
"Please try later again." + msg);
|
|
||||||
takeOfferRequested = false;
|
|
||||||
break;
|
|
||||||
case TIMEOUT:
|
case TIMEOUT:
|
||||||
errorMessage.set("Timeout: We did not received a message from the offerer. Maybe there are connection problems. " +
|
errorMessage.set("A timeout occurred. Maybe there are connection problems. " +
|
||||||
"Please try later again." + msg);
|
"Please try later again.\n" + msg);
|
||||||
takeOfferRequested = false;
|
takeOfferRequested = false;
|
||||||
break;
|
break;
|
||||||
case EXCEPTION:
|
case FAULT:
|
||||||
errorMessage.set(msg);
|
errorMessage.set(msg);
|
||||||
takeOfferRequested = false;
|
takeOfferRequested = false;
|
||||||
break;*/
|
break;
|
||||||
default:
|
default:
|
||||||
log.warn("Unhandled trade state: " + newValue);
|
log.warn("Unhandled trade state: " + state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMessage.get() != null) {
|
if (errorMessage.get() != null)
|
||||||
isAmountAndPriceValidAndWalletFunded = false;
|
|
||||||
isTakeOfferSpinnerVisible.set(false);
|
isTakeOfferSpinnerVisible.set(false);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
evaluateState();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isSeller() {
|
|
||||||
return dataModel.getDirection() == Offer.Direction.BUY;
|
|
||||||
}
|
|
||||||
|
|
||||||
void securityDepositInfoDisplayed() {
|
|
||||||
dataModel.securityDepositInfoDisplayed();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void onShowPaymentScreen() {
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
state.set(State.PAYMENT_SCREEN);
|
// Focus handling
|
||||||
}
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// On focus out we do validation and apply the data to the model
|
// On focus out we do validation and apply the data to the model
|
||||||
void onFocusOutAmountTextField(Boolean oldValue, Boolean newValue, String userInput) {
|
void onFocusOutAmountTextField(boolean oldValue, boolean newValue, String userInput) {
|
||||||
if (oldValue && !newValue) {
|
if (oldValue && !newValue) {
|
||||||
InputValidator.ValidationResult result = isBtcInputValid(amount.get());
|
InputValidator.ValidationResult result = isBtcInputValid(amount.get());
|
||||||
amountValidationResult.set(result);
|
amountValidationResult.set(result);
|
||||||
@ -361,6 +449,18 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Getters
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
boolean isSeller() {
|
||||||
|
return dataModel.getDirection() == Offer.Direction.BUY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void securityDepositInfoDisplayed() {
|
||||||
|
dataModel.onSecurityDepositInfoDisplayed();
|
||||||
|
}
|
||||||
|
|
||||||
WalletService getWalletService() {
|
WalletService getWalletService() {
|
||||||
return dataModel.getWalletService();
|
return dataModel.getWalletService();
|
||||||
}
|
}
|
||||||
@ -429,50 +529,24 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
return paymentLabel;
|
return paymentLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
Boolean getDisplaySecurityDepositInfo() {
|
boolean getDisplaySecurityDepositInfo() {
|
||||||
return dataModel.getDisplaySecurityDepositInfo();
|
return dataModel.getDisplaySecurityDepositInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isDetailsVisible() {
|
||||||
private void setupListeners() {
|
return detailsVisible;
|
||||||
// Bidirectional bindings are used for all input fields: amount, price, volume and minAmount
|
|
||||||
// We do volume/amount calculation during input, so user has immediate feedback
|
|
||||||
amount.addListener((ov, oldValue, newValue) -> {
|
|
||||||
if (isBtcInputValid(newValue).isValid) {
|
|
||||||
setAmountToModel();
|
|
||||||
calculateVolume();
|
|
||||||
dataModel.calculateTotalToPay();
|
|
||||||
}
|
|
||||||
evaluateState();
|
|
||||||
});
|
|
||||||
|
|
||||||
dataModel.isWalletFunded.addListener((ov, oldValue, newValue) -> {
|
|
||||||
evaluateState();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Binding with Bindings.createObjectBinding does not work because of bi-directional binding
|
|
||||||
dataModel.amountAsCoin.addListener((ov, oldValue, newValue) -> amount.set(formatter.formatCoin(newValue)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyTakeOfferRequestResult(boolean success) {
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Utils
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void applyOnTakeOfferResult(boolean success) {
|
||||||
isTakeOfferSpinnerVisible.set(false);
|
isTakeOfferSpinnerVisible.set(false);
|
||||||
showTransactionPublishedScreen.set(success);
|
showTransactionPublishedScreen.set(success);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupBindings() {
|
|
||||||
volume.bind(createStringBinding(() -> formatter.formatFiatWithCode(dataModel.volumeAsFiat.get()),
|
|
||||||
dataModel.volumeAsFiat));
|
|
||||||
totalToPay.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.totalToPayAsCoin.get()),
|
|
||||||
dataModel.totalToPayAsCoin));
|
|
||||||
securityDeposit.bind(createStringBinding(() -> formatter.formatCoinWithCode(dataModel.securityDepositAsCoin
|
|
||||||
.get()),
|
|
||||||
dataModel.securityDepositAsCoin));
|
|
||||||
|
|
||||||
totalToPayAsCoin.bind(dataModel.totalToPayAsCoin);
|
|
||||||
|
|
||||||
btcCode.bind(dataModel.btcCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calculateVolume() {
|
private void calculateVolume() {
|
||||||
setAmountToModel();
|
setAmountToModel();
|
||||||
dataModel.calculateVolume();
|
dataModel.calculateVolume();
|
||||||
@ -482,8 +556,8 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||||||
dataModel.amountAsCoin.set(formatter.parseToCoinWith4Decimals(amount.get()));
|
dataModel.amountAsCoin.set(formatter.parseToCoinWith4Decimals(amount.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void evaluateState() {
|
private void evaluateViewState() {
|
||||||
isAmountAndPriceValidAndWalletFunded = isBtcInputValid(amount.get()).isValid &&
|
boolean isAmountAndPriceValidAndWalletFunded = isBtcInputValid(amount.get()).isValid &&
|
||||||
dataModel.isMinAmountLessOrEqualAmount() &&
|
dataModel.isMinAmountLessOrEqualAmount() &&
|
||||||
!dataModel.isAmountLargerThanOfferAmount() &&
|
!dataModel.isAmountLargerThanOfferAmount() &&
|
||||||
dataModel.isWalletFunded.get();
|
dataModel.isWalletFunded.get();
|
||||||
|
@ -17,10 +17,6 @@
|
|||||||
|
|
||||||
package io.bitsquare.gui.util;
|
package io.bitsquare.gui.util;
|
||||||
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
import javafx.animation.AnimationTimer;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.scene.input.*;
|
import javafx.scene.input.*;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -37,36 +33,4 @@ public class GUIUtil {
|
|||||||
clipboard.setContent(clipboardContent);
|
clipboard.setContent(clipboardContent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AnimationTimer setTimeout(int delay, Function<AnimationTimer, Void> callback) {
|
|
||||||
AnimationTimer animationTimer = new AnimationTimer() {
|
|
||||||
final long lastTimeStamp = System.currentTimeMillis();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(long arg0) {
|
|
||||||
if (System.currentTimeMillis() > delay + lastTimeStamp) {
|
|
||||||
Platform.runLater(() -> callback.apply(this));
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
animationTimer.start();
|
|
||||||
return animationTimer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AnimationTimer setInterval(int delay, Function<AnimationTimer, Void> callback) {
|
|
||||||
AnimationTimer animationTimer = new AnimationTimer() {
|
|
||||||
long lastTimeStamp = System.currentTimeMillis();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(long arg0) {
|
|
||||||
if (System.currentTimeMillis() > delay + lastTimeStamp) {
|
|
||||||
lastTimeStamp = System.currentTimeMillis();
|
|
||||||
callback.apply(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
animationTimer.start();
|
|
||||||
return animationTimer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -24,30 +24,15 @@ import org.bitcoinj.core.AddressFormatException;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
|
||||||
* BtcValidator for validating BTC values.
|
|
||||||
* <p/>
|
|
||||||
* That class implements just what we need for the moment. It is not intended as a general purpose library class.
|
|
||||||
*/
|
|
||||||
public final class BtcAddressValidator extends InputValidator {
|
public final class BtcAddressValidator extends InputValidator {
|
||||||
|
|
||||||
private final BitcoinNetwork bitcoinNetwork;
|
private final BitcoinNetwork bitcoinNetwork;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public BtcAddressValidator(BitcoinNetwork bitcoinNetwork) {
|
public BtcAddressValidator(BitcoinNetwork bitcoinNetwork) {
|
||||||
this.bitcoinNetwork = bitcoinNetwork;
|
this.bitcoinNetwork = bitcoinNetwork;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validate(String input) {
|
public ValidationResult validate(String input) {
|
||||||
|
|
||||||
@ -58,11 +43,6 @@ public final class BtcAddressValidator extends InputValidator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private ValidationResult validateBtcAddress(String input) {
|
private ValidationResult validateBtcAddress(String input) {
|
||||||
try {
|
try {
|
||||||
new Address(bitcoinNetwork.getParameters(), input);
|
new Address(bitcoinNetwork.getParameters(), input);
|
||||||
|
@ -23,16 +23,7 @@ import org.bitcoinj.core.NetworkParameters;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/**
|
public class BtcValidator extends NumberValidator {
|
||||||
* BtcValidator for validating BTC values.
|
|
||||||
* <p/>
|
|
||||||
* That class implements just what we need for the moment. It is not intended as a general purpose library class.
|
|
||||||
*/
|
|
||||||
public final class BtcValidator extends NumberValidator {
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validate(String input) {
|
public ValidationResult validate(String input) {
|
||||||
@ -52,12 +43,7 @@ public final class BtcValidator extends NumberValidator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ValidationResult validateIfNotFractionalBtcValue(String input) {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private ValidationResult validateIfNotFractionalBtcValue(String input) {
|
|
||||||
BigDecimal bd = new BigDecimal(input);
|
BigDecimal bd = new BigDecimal(input);
|
||||||
final BigDecimal satoshis = bd.movePointRight(8);
|
final BigDecimal satoshis = bd.movePointRight(8);
|
||||||
if (satoshis.scale() > 0)
|
if (satoshis.scale() > 0)
|
||||||
@ -66,7 +52,7 @@ public final class BtcValidator extends NumberValidator {
|
|||||||
return new ValidationResult(true);
|
return new ValidationResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValidationResult validateIfNotExceedsMaxBtcValue(String input) {
|
protected ValidationResult validateIfNotExceedsMaxBtcValue(String input) {
|
||||||
BigDecimal bd = new BigDecimal(input);
|
BigDecimal bd = new BigDecimal(input);
|
||||||
final BigDecimal satoshis = bd.movePointRight(8);
|
final BigDecimal satoshis = bd.movePointRight(8);
|
||||||
if (satoshis.longValue() > NetworkParameters.MAX_MONEY.longValue())
|
if (satoshis.longValue() > NetworkParameters.MAX_MONEY.longValue())
|
||||||
|
@ -23,18 +23,12 @@ import io.bitsquare.user.User;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
public class FiatValidator extends NumberValidator {
|
||||||
* FiatNumberValidator for validating fiat values.
|
|
||||||
* <p/>
|
|
||||||
* That class implements just what we need for the moment. It is not intended as a general purpose library class.
|
|
||||||
*/
|
|
||||||
public final class FiatValidator extends NumberValidator {
|
|
||||||
|
|
||||||
//TODO Find appropriate values - depends on currencies
|
//TODO Find appropriate values - depends on currencies
|
||||||
public static final double MIN_FIAT_VALUE = 0.01; // usually a cent is the smallest currency unit
|
public static final double MIN_FIAT_VALUE = 0.01; // usually a cent is the smallest currency unit
|
||||||
public static final double MAX_FIAT_VALUE = 1000000;
|
public static final double MAX_FIAT_VALUE = 1000000;
|
||||||
private String currencyCode = "Fiat";
|
protected String currencyCode = "Fiat";
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public FiatValidator(User user) {
|
public FiatValidator(User user) {
|
||||||
@ -51,11 +45,6 @@ public final class FiatValidator extends NumberValidator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validate(String input) {
|
public ValidationResult validate(String input) {
|
||||||
ValidationResult result = validateIfNotEmpty(input);
|
ValidationResult result = validateIfNotEmpty(input);
|
||||||
@ -74,21 +63,11 @@ public final class FiatValidator extends NumberValidator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Setter
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void setFiatCurrencyCode(String currencyCode) {
|
public void setFiatCurrencyCode(String currencyCode) {
|
||||||
this.currencyCode = currencyCode;
|
this.currencyCode = currencyCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ValidationResult validateIfNotExceedsMinFiatValue(String input) {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private ValidationResult validateIfNotExceedsMinFiatValue(String input) {
|
|
||||||
double d = Double.parseDouble(input);
|
double d = Double.parseDouble(input);
|
||||||
if (d < MIN_FIAT_VALUE)
|
if (d < MIN_FIAT_VALUE)
|
||||||
return new ValidationResult(false, BSResources.get("validation.fiat.toSmall", currencyCode));
|
return new ValidationResult(false, BSResources.get("validation.fiat.toSmall", currencyCode));
|
||||||
@ -96,7 +75,7 @@ public final class FiatValidator extends NumberValidator {
|
|||||||
return new ValidationResult(true);
|
return new ValidationResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ValidationResult validateIfNotExceedsMaxFiatValue(String input) {
|
protected ValidationResult validateIfNotExceedsMaxFiatValue(String input) {
|
||||||
double d = Double.parseDouble(input);
|
double d = Double.parseDouble(input);
|
||||||
if (d > MAX_FIAT_VALUE)
|
if (d > MAX_FIAT_VALUE)
|
||||||
return new ValidationResult(false, BSResources.get("validation.fiat.toLarge", currencyCode));
|
return new ValidationResult(false, BSResources.get("validation.fiat.toLarge", currencyCode));
|
||||||
|
@ -19,25 +19,12 @@ package io.bitsquare.gui.util.validation;
|
|||||||
|
|
||||||
import io.bitsquare.locale.BSResources;
|
import io.bitsquare.locale.BSResources;
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for other specialized validators.
|
|
||||||
* <p/>
|
|
||||||
* That class implements just what we need for the moment. It is not intended as a general purpose library class.
|
|
||||||
*/
|
|
||||||
public class InputValidator {
|
public class InputValidator {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public ValidationResult validate(String input) {
|
public ValidationResult validate(String input) {
|
||||||
return validateIfNotEmpty(input);
|
return validateIfNotEmpty(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Protected methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
protected ValidationResult validateIfNotEmpty(String input) {
|
protected ValidationResult validateIfNotEmpty(String input) {
|
||||||
if (input == null || input.length() == 0)
|
if (input == null || input.length() == 0)
|
||||||
return new ValidationResult(false, BSResources.get("validation.empty"));
|
return new ValidationResult(false, BSResources.get("validation.empty"));
|
||||||
@ -45,10 +32,6 @@ public class InputValidator {
|
|||||||
return new ValidationResult(true);
|
return new ValidationResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// ValidationResult
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public static class ValidationResult {
|
public static class ValidationResult {
|
||||||
public final boolean isValid;
|
public final boolean isValid;
|
||||||
public final String errorMessage;
|
public final String errorMessage;
|
||||||
|
@ -24,15 +24,9 @@ import io.bitsquare.locale.BSResources;
|
|||||||
* Localisation not supported at the moment
|
* Localisation not supported at the moment
|
||||||
* The decimal mark can be either "." or ",". Thousand separators are not supported yet,
|
* The decimal mark can be either "." or ",". Thousand separators are not supported yet,
|
||||||
* but might be added alter with Local support.
|
* but might be added alter with Local support.
|
||||||
* <p/>
|
|
||||||
* That class implements just what we need for the moment. It is not intended as a general purpose library class.
|
|
||||||
*/
|
*/
|
||||||
public abstract class NumberValidator extends InputValidator {
|
public abstract class NumberValidator extends InputValidator {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Protected methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
protected String cleanInput(String input) {
|
protected String cleanInput(String input) {
|
||||||
return input.replace(",", ".").trim();
|
return input.replace(",", ".").trim();
|
||||||
}
|
}
|
||||||
|
@ -17,23 +17,10 @@
|
|||||||
|
|
||||||
package io.bitsquare.gui.util.validation;
|
package io.bitsquare.gui.util.validation;
|
||||||
|
|
||||||
import io.bitsquare.locale.BSResources;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BtcValidator for validating BTC values.
|
* That validator accepts empty inputs
|
||||||
* <p/>
|
|
||||||
* That class implements just what we need for the moment. It is not intended as a general purpose library class.
|
|
||||||
*/
|
*/
|
||||||
public final class OptionalBtcValidator extends NumberValidator {
|
public class OptionalBtcValidator extends BtcValidator {
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validate(String input) {
|
public ValidationResult validate(String input) {
|
||||||
@ -63,27 +50,4 @@ public final class OptionalBtcValidator extends NumberValidator {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private ValidationResult validateIfNotFractionalBtcValue(String input) {
|
|
||||||
BigDecimal bd = new BigDecimal(input);
|
|
||||||
final BigDecimal satoshis = bd.movePointRight(8);
|
|
||||||
if (satoshis.scale() > 0)
|
|
||||||
return new ValidationResult(false, BSResources.get("validation.btc.toSmall"));
|
|
||||||
else
|
|
||||||
return new ValidationResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ValidationResult validateIfNotExceedsMaxBtcValue(String input) {
|
|
||||||
BigDecimal bd = new BigDecimal(input);
|
|
||||||
final BigDecimal satoshis = bd.movePointRight(8);
|
|
||||||
if (satoshis.longValue() > NetworkParameters.MAX_MONEY.longValue())
|
|
||||||
return new ValidationResult(false, BSResources.get("validation.btc.toLarge"));
|
|
||||||
else
|
|
||||||
return new ValidationResult(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,44 +17,19 @@
|
|||||||
|
|
||||||
package io.bitsquare.gui.util.validation;
|
package io.bitsquare.gui.util.validation;
|
||||||
|
|
||||||
import io.bitsquare.locale.BSResources;
|
|
||||||
import io.bitsquare.locale.CurrencyUtil;
|
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FiatNumberValidator for validating fiat values.
|
* That validator accepts empty inputs
|
||||||
* <p/>
|
|
||||||
* That class implements just what we need for the moment. It is not intended as a general purpose library class.
|
|
||||||
*/
|
*/
|
||||||
public final class OptionalFiatValidator extends NumberValidator {
|
public class OptionalFiatValidator extends FiatValidator {
|
||||||
|
|
||||||
//TODO Find appropriate values - depends on currencies
|
|
||||||
public static final double MIN_FIAT_VALUE = 0.01; // usually a cent is the smallest currency unit
|
|
||||||
public static final double MAX_FIAT_VALUE = 1000000;
|
|
||||||
private String currencyCode = "Fiat";
|
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public OptionalFiatValidator(User user) {
|
public OptionalFiatValidator(User user) {
|
||||||
if (user != null) {
|
super(user);
|
||||||
if (user.currentFiatAccountProperty().get() == null)
|
|
||||||
setFiatCurrencyCode(CurrencyUtil.getDefaultCurrencyAsCode());
|
|
||||||
else if (user.currentFiatAccountProperty().get() != null)
|
|
||||||
setFiatCurrencyCode(user.currentFiatAccountProperty().get().currencyCode);
|
|
||||||
|
|
||||||
user.currentFiatAccountProperty().addListener((ov, oldValue, newValue) -> {
|
|
||||||
if (newValue != null)
|
|
||||||
setFiatCurrencyCode(newValue.currencyCode);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validate(String input) {
|
public ValidationResult validate(String input) {
|
||||||
@ -84,34 +59,4 @@ public final class OptionalFiatValidator extends NumberValidator {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Setter
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void setFiatCurrencyCode(String currencyCode) {
|
|
||||||
this.currencyCode = currencyCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private ValidationResult validateIfNotExceedsMinFiatValue(String input) {
|
|
||||||
double d = Double.parseDouble(input);
|
|
||||||
if (d < MIN_FIAT_VALUE)
|
|
||||||
return new ValidationResult(false, BSResources.get("validation.fiat.toSmall", currencyCode));
|
|
||||||
else
|
|
||||||
return new ValidationResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ValidationResult validateIfNotExceedsMaxFiatValue(String input) {
|
|
||||||
double d = Double.parseDouble(input);
|
|
||||||
if (d > MAX_FIAT_VALUE)
|
|
||||||
return new ValidationResult(false, BSResources.get("validation.fiat.toLarge", currencyCode));
|
|
||||||
else
|
|
||||||
return new ValidationResult(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,6 @@ import io.bitsquare.locale.BSResources;
|
|||||||
|
|
||||||
public final class PasswordValidator extends InputValidator {
|
public final class PasswordValidator extends InputValidator {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult validate(String input) {
|
public ValidationResult validate(String input) {
|
||||||
ValidationResult result = validateIfNotEmpty(input);
|
ValidationResult result = validateIfNotEmpty(input);
|
||||||
@ -33,11 +29,6 @@ public final class PasswordValidator extends InputValidator {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private ValidationResult validateMinLength(String input) {
|
private ValidationResult validateMinLength(String input) {
|
||||||
if (input.length() > 7)
|
if (input.length() > 7)
|
||||||
return new ValidationResult(true);
|
return new ValidationResult(true);
|
||||||
|
@ -44,7 +44,7 @@ public class CreateOfferViewModelTest {
|
|||||||
BSFormatter formatter = new BSFormatter(new User(), null);
|
BSFormatter formatter = new BSFormatter(new User(), null);
|
||||||
formatter.setLocale(Locale.US);
|
formatter.setLocale(Locale.US);
|
||||||
formatter.setFiatCurrencyCode("USD");
|
formatter.setFiatCurrencyCode("USD");
|
||||||
model = new CreateOfferDataModel(null, null, null, null, null, null, formatter);
|
model = new CreateOfferDataModel(null, null, null, null, null, formatter);
|
||||||
|
|
||||||
presenter = new CreateOfferViewModel(model, new FiatValidator(null), new BtcValidator(), formatter);
|
presenter = new CreateOfferViewModel(model, new FiatValidator(null), new BtcValidator(), formatter);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user