Add Notification and Instructions popups. Cleanup MainViewModel

This commit is contained in:
Manfred Karrer 2016-02-14 16:45:22 +01:00
parent cdccd57968
commit 6bf2adae7f
23 changed files with 637 additions and 514 deletions

View file

@ -48,6 +48,7 @@ public class Dispute implements Serializable {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private final String tradeId; private final String tradeId;
private final String id;
private final int traderId; private final int traderId;
private final boolean disputeOpenerIsBuyer; private final boolean disputeOpenerIsBuyer;
private final boolean disputeOpenerIsOfferer; private final boolean disputeOpenerIsOfferer;
@ -123,6 +124,8 @@ public class Dispute implements Serializable {
this.arbitratorPubKeyRing = arbitratorPubKeyRing; this.arbitratorPubKeyRing = arbitratorPubKeyRing;
this.isSupportTicket = isSupportTicket; this.isSupportTicket = isSupportTicket;
this.openingDate = new Date().getTime(); this.openingDate = new Date().getTime();
id = tradeId + "_" + traderId;
} }
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
@ -177,6 +180,10 @@ public class Dispute implements Serializable {
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public String getId() {
return id;
}
public String getTradeId() { public String getTradeId() {
return tradeId; return tradeId;
} }

View file

@ -381,8 +381,12 @@ public class TradeManager {
return offer.isMyOffer(keyRing); return offer.isMyOffer(keyRing);
} }
public boolean isMyOfferInBtcBuyerRole(Offer offer) { public boolean isBuyer(Offer offer) {
return !(isMyOffer(offer) ^ offer.getDirection() == Offer.Direction.BUY); // If I am the offerer, the offer direction is taken, otherwise the mirrored direction
if (isMyOffer(offer))
return offer.getDirection() == Offer.Direction.BUY;
else
return offer.getDirection() == Offer.Direction.SELL;
} }
public Optional<Trade> getTradeById(String tradeId) { public Optional<Trade> getTradeById(String tradeId) {

View file

@ -66,8 +66,7 @@ public class SetupDepositBalanceListener extends TradeTask {
|| newValue == Trade.State.DEPOSIT_SEEN_IN_NETWORK) { || newValue == Trade.State.DEPOSIT_SEEN_IN_NETWORK) {
walletService.removeBalanceListener(balanceListener); walletService.removeBalanceListener(balanceListener);
log.debug(" UserThread.execute(this::unSubscribe);"); // hack to remove tradeStateSubscription at callback
// TODO is that allowed?
UserThread.execute(this::unSubscribe); UserThread.execute(this::unSubscribe);
} }
}); });
@ -81,8 +80,6 @@ public class SetupDepositBalanceListener extends TradeTask {
} }
private void unSubscribe() { private void unSubscribe() {
//TODO investigate, seems to not get called sometimes
log.debug("unSubscribe tradeStateSubscription");
tradeStateSubscription.unsubscribe(); tradeStateSubscription.unsubscribe();
} }

View file

@ -96,8 +96,8 @@ public class Preferences implements Serializable {
private final ArrayList<TradeCurrency> tradeCurrencies; private final ArrayList<TradeCurrency> tradeCurrencies;
private BlockChainExplorer blockChainExplorerMainNet; private BlockChainExplorer blockChainExplorerMainNet;
private BlockChainExplorer blockChainExplorerTestNet; private BlockChainExplorer blockChainExplorerTestNet;
private boolean showPlaceOfferConfirmation; private boolean showNotifications = true;
private boolean showTakeOfferConfirmation; private boolean showInstructions = true;
private String backupDirectory; private String backupDirectory;
private boolean autoSelectArbitrators = true; private boolean autoSelectArbitrators = true;
private final Map<String, Boolean> showAgainMap; private final Map<String, Boolean> showAgainMap;
@ -140,8 +140,8 @@ public class Preferences implements Serializable {
if (blockChainExplorerMainNet == null) if (blockChainExplorerMainNet == null)
setBlockChainExplorerTestNet(blockChainExplorersMainNet.get(0)); setBlockChainExplorerTestNet(blockChainExplorersMainNet.get(0));
showPlaceOfferConfirmation = persisted.getShowPlaceOfferConfirmation(); showNotifications = persisted.getShowNotifications();
showTakeOfferConfirmation = persisted.getShowTakeOfferConfirmation(); showInstructions = persisted.getShowInstructions();
backupDirectory = persisted.getBackupDirectory(); backupDirectory = persisted.getBackupDirectory();
autoSelectArbitrators = persisted.getAutoSelectArbitrators(); autoSelectArbitrators = persisted.getAutoSelectArbitrators();
showAgainMap = persisted.getShowAgainMap(); showAgainMap = persisted.getShowAgainMap();
@ -163,8 +163,6 @@ public class Preferences implements Serializable {
tradeCurrencies = new ArrayList<>(tradeCurrenciesAsObservable); tradeCurrencies = new ArrayList<>(tradeCurrenciesAsObservable);
setBlockChainExplorerTestNet(blockChainExplorersTestNet.get(0)); setBlockChainExplorerTestNet(blockChainExplorersTestNet.get(0));
setBlockChainExplorerMainNet(blockChainExplorersMainNet.get(0)); setBlockChainExplorerMainNet(blockChainExplorersMainNet.get(0));
showPlaceOfferConfirmation = true;
showTakeOfferConfirmation = true;
showAgainMap = new HashMap<>(); showAgainMap = new HashMap<>();
showAgainMap.put(PopupId.TRADE_WALLET, true); showAgainMap.put(PopupId.TRADE_WALLET, true);
@ -253,13 +251,13 @@ public class Preferences implements Serializable {
setBlockChainExplorerTestNet(blockChainExplorer); setBlockChainExplorerTestNet(blockChainExplorer);
} }
public void setShowPlaceOfferConfirmation(boolean showPlaceOfferConfirmation) { public void setShowNotifications(boolean showNotifications) {
this.showPlaceOfferConfirmation = showPlaceOfferConfirmation; this.showNotifications = showNotifications;
storage.queueUpForSave(2000); storage.queueUpForSave(2000);
} }
public void setShowTakeOfferConfirmation(boolean showTakeOfferConfirmation) { public void setShowInstructions(boolean showInstructions) {
this.showTakeOfferConfirmation = showTakeOfferConfirmation; this.showInstructions = showInstructions;
storage.queueUpForSave(2000); storage.queueUpForSave(2000);
} }
@ -356,13 +354,12 @@ public class Preferences implements Serializable {
return blockChainExplorersTestNet; return blockChainExplorersTestNet;
} }
public boolean getShowPlaceOfferConfirmation() { public boolean getShowNotifications() {
return showPlaceOfferConfirmation; return showNotifications;
} }
public boolean getShowInstructions() {
public boolean getShowTakeOfferConfirmation() { return showInstructions;
return showTakeOfferConfirmation;
} }
public String getBackupDirectory() { public String getBackupDirectory() {

View file

@ -26,6 +26,7 @@ import io.bitsquare.common.crypto.KeyStorage;
import io.bitsquare.crypto.EncryptionServiceModule; import io.bitsquare.crypto.EncryptionServiceModule;
import io.bitsquare.gui.GuiModule; import io.bitsquare.gui.GuiModule;
import io.bitsquare.gui.common.view.CachingViewLoader; import io.bitsquare.gui.common.view.CachingViewLoader;
import io.bitsquare.gui.main.intructions.InstructionCenter;
import io.bitsquare.gui.main.notifications.NotificationCenter; import io.bitsquare.gui.main.notifications.NotificationCenter;
import io.bitsquare.p2p.P2PModule; import io.bitsquare.p2p.P2PModule;
import io.bitsquare.storage.Storage; import io.bitsquare.storage.Storage;
@ -60,6 +61,7 @@ class BitsquareAppModule extends AppModule {
bind(User.class).in(Singleton.class); bind(User.class).in(Singleton.class);
bind(Preferences.class).in(Singleton.class); bind(Preferences.class).in(Singleton.class);
bind(NotificationCenter.class).in(Singleton.class); bind(NotificationCenter.class).in(Singleton.class);
bind(InstructionCenter.class).in(Singleton.class);
File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY)); File storageDir = new File(env.getRequiredProperty(Storage.DIR_KEY));
bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir); bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir);

View file

@ -26,21 +26,23 @@ import io.bitsquare.app.Version;
import io.bitsquare.arbitration.ArbitratorManager; import io.bitsquare.arbitration.ArbitratorManager;
import io.bitsquare.arbitration.Dispute; import io.bitsquare.arbitration.Dispute;
import io.bitsquare.arbitration.DisputeManager; import io.bitsquare.arbitration.DisputeManager;
import io.bitsquare.btc.*; import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.btc.pricefeed.MarketPriceFeed; import io.bitsquare.btc.pricefeed.MarketPriceFeed;
import io.bitsquare.common.UserThread; import io.bitsquare.common.UserThread;
import io.bitsquare.gui.Navigation; import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.ViewModel; import io.bitsquare.gui.common.model.ViewModel;
import io.bitsquare.gui.common.view.ViewPath;
import io.bitsquare.gui.components.BalanceTextField; import io.bitsquare.gui.components.BalanceTextField;
import io.bitsquare.gui.components.BalanceWithConfirmationTextField; import io.bitsquare.gui.components.BalanceWithConfirmationTextField;
import io.bitsquare.gui.components.TxIdTextField; import io.bitsquare.gui.components.TxIdTextField;
import io.bitsquare.gui.main.notifications.NotificationCenter;
import io.bitsquare.gui.main.popups.DisplayAlertMessagePopup; import io.bitsquare.gui.main.popups.DisplayAlertMessagePopup;
import io.bitsquare.gui.main.popups.Popup; import io.bitsquare.gui.main.popups.Popup;
import io.bitsquare.gui.main.popups.TacPopup;
import io.bitsquare.gui.main.popups.WalletPasswordPopup; import io.bitsquare.gui.main.popups.WalletPasswordPopup;
import io.bitsquare.gui.main.portfolio.PortfolioView;
import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesView;
import io.bitsquare.gui.util.BSFormatter; import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.CountryUtil; import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.CurrencyUtil; import io.bitsquare.locale.CurrencyUtil;
@ -69,9 +71,9 @@ import org.reactfx.util.Timer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.time.Duration; import java.time.Duration;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -89,6 +91,8 @@ public class MainViewModel implements ViewModel {
private final Preferences preferences; private final Preferences preferences;
private final AlertManager alertManager; private final AlertManager alertManager;
private final WalletPasswordPopup walletPasswordPopup; private final WalletPasswordPopup walletPasswordPopup;
private NotificationCenter notificationCenter;
private TacPopup tacPopup;
private Navigation navigation; private Navigation navigation;
private final BSFormatter formatter; private final BSFormatter formatter;
@ -133,8 +137,7 @@ public class MainViewModel implements ViewModel {
private java.util.Timer numberofBtcPeersTimer; private java.util.Timer numberofBtcPeersTimer;
private java.util.Timer numberofP2PNetworkPeersTimer; private java.util.Timer numberofP2PNetworkPeersTimer;
private Timer startupTimeout; private Timer startupTimeout;
private Set<Subscription> tradeStateSubscriptions = new HashSet<>(); private final Map<String, Subscription> disputeIsClosedSubscriptionsMap = new HashMap<>();
private Set<Subscription> disputeStateSubscriptions = new HashSet<>();
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -147,6 +150,7 @@ public class MainViewModel implements ViewModel {
ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager, ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager,
OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences, OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences,
User user, AlertManager alertManager, WalletPasswordPopup walletPasswordPopup, User user, AlertManager alertManager, WalletPasswordPopup walletPasswordPopup,
NotificationCenter notificationCenter, TacPopup tacPopup,
Navigation navigation, BSFormatter formatter) { Navigation navigation, BSFormatter formatter) {
this.marketPriceFeed = marketPriceFeed; this.marketPriceFeed = marketPriceFeed;
this.user = user; this.user = user;
@ -160,6 +164,8 @@ public class MainViewModel implements ViewModel {
this.preferences = preferences; this.preferences = preferences;
this.alertManager = alertManager; this.alertManager = alertManager;
this.walletPasswordPopup = walletPasswordPopup; this.walletPasswordPopup = walletPasswordPopup;
this.notificationCenter = notificationCenter;
this.tacPopup = tacPopup;
this.navigation = navigation; this.navigation = navigation;
this.formatter = formatter; this.formatter = formatter;
@ -172,8 +178,6 @@ public class MainViewModel implements ViewModel {
BalanceWithConfirmationTextField.setWalletService(walletService); BalanceWithConfirmationTextField.setWalletService(walletService);
if (BitsquareApp.DEV_MODE) { if (BitsquareApp.DEV_MODE) {
preferences.setShowPlaceOfferConfirmation(false);
preferences.setShowTakeOfferConfirmation(false);
preferences.setUseAnimations(false); preferences.setUseAnimations(false);
preferences.setUseEffects(false); preferences.setUseEffects(false);
} }
@ -221,6 +225,16 @@ public class MainViewModel implements ViewModel {
} }
///////////////////////////////////////////////////////////////////////////////////////////
// UI handlers
///////////////////////////////////////////////////////////////////////////////////////////
void onSplashScreenRemoved() {
isSplashScreenRemoved.set(true);
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Initialisation // Initialisation
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -360,33 +374,23 @@ public class MainViewModel implements ViewModel {
startupTimeout.stop(); startupTimeout.stop();
// disputeManager // disputeManager
disputeManager.onAllServicesInitialized();
disputeManager.getDisputesAsObservableList().addListener((ListChangeListener<Dispute>) change -> { disputeManager.getDisputesAsObservableList().addListener((ListChangeListener<Dispute>) change -> {
change.next(); change.next();
addDisputeClosedChangeListener(change.getAddedSubList()); onDisputesChangeListener(change.getAddedSubList(), change.getRemoved());
updateDisputeStates();
}); });
addDisputeClosedChangeListener(disputeManager.getDisputesAsObservableList()); onDisputesChangeListener(disputeManager.getDisputesAsObservableList(), null);
updateDisputeStates();
disputeManager.onAllServicesInitialized();
// tradeManager // tradeManager
tradeManager.getTrades().addListener((ListChangeListener<Trade>) c -> updateBalance()); tradeManager.getTrades().addListener((ListChangeListener<Trade>) c -> updateBalance());
tradeManager.getTrades().addListener((ListChangeListener<Trade>) change -> onTradesChanged());
tradeManager.getTrades().addListener((ListChangeListener<Trade>) change -> { onTradesChanged();
change.next(); // We handle the trade period here as we display a global popup if we reached dispute time
setDisputeStateSubscriptions(); tradesAndUIReady = EasyBind.combine(isSplashScreenRemoved, tradeManager.pendingTradesInitializedProperty(), (a, b) -> a && b);
setTradeStateSubscriptions(); tradesAndUIReady.subscribe((observable, oldValue, newValue) -> {
pendingTradesChanged(); if (newValue)
applyTradePeriodState();
}); });
pendingTradesChanged();
setDisputeStateSubscriptions();
setTradeStateSubscriptions();
// arbitratorManager
arbitratorManager.onAllServicesInitialized();
// walletService // walletService
// In case we have any offers open or a pending trade we need to unlock our trading wallet so a trade can be executed automatically // In case we have any offers open or a pending trade we need to unlock our trading wallet so a trade can be executed automatically
@ -398,77 +402,120 @@ public class MainViewModel implements ViewModel {
|| disputeManager.getDisputesAsObservableList().size() > 0)) { || disputeManager.getDisputesAsObservableList().size() > 0)) {
walletPasswordPopup.onAesKey(aesKey -> tradeWalletService.setAesKey(aesKey)).show(); walletPasswordPopup.onAesKey(aesKey -> tradeWalletService.setAesKey(aesKey)).show();
} }
// We handle the trade period here as we display a global popup if we reached dispute time
tradesAndUIReady = EasyBind.combine(isSplashScreenRemoved, tradeManager.pendingTradesInitializedProperty(), (a, b) -> a && b);
tradesAndUIReady.subscribe((observable, oldValue, newValue) -> {
if (newValue)
applyTradePeriodState();
});
walletService.addBalanceListener(new BalanceListener() { walletService.addBalanceListener(new BalanceListener() {
@Override @Override
public void onBalanceChanged(Coin balance, Transaction tx) { public void onBalanceChanged(Coin balance, Transaction tx) {
updateBalance(); updateBalance();
} }
}); });
updateBalance();
setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get()); setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get());
checkPeriodicallyForBtcSyncState(); checkPeriodicallyForBtcSyncState();
// openOfferManager
openOfferManager.getOpenOffers().addListener((ListChangeListener<OpenOffer>) c -> updateBalance()); openOfferManager.getOpenOffers().addListener((ListChangeListener<OpenOffer>) c -> updateBalance());
openOfferManager.onAllServicesInitialized(); openOfferManager.onAllServicesInitialized();
arbitratorManager.onAllServicesInitialized();
// alertManager
alertManager.alertMessageProperty().addListener((observable, oldValue, newValue) -> displayAlertIfPresent(newValue)); alertManager.alertMessageProperty().addListener((observable, oldValue, newValue) -> displayAlertIfPresent(newValue));
displayAlertIfPresent(alertManager.alertMessageProperty().get()); displayAlertIfPresent(alertManager.alertMessageProperty().get());
setupP2PPeersInfo();
updateBalance();
setupDevDummyPaymentAccount();
setupMarketPriceFeed();
// tac tacPopup.showIfNeeded();
// TODO add link: https://bitsquare.io/arbitration_system.pdf notificationCenter.onAllServicesInitialized();
String text = "1. This software is experimental and provided \"as is\", without warranty of any kind, " +
"express or implied, including but not limited to the warranties of " +
"merchantability, fitness for a particular purpose and non-infringement.\n" +
"In no event shall the authors or copyright holders be liable for any claim, damages or other " +
"liability, whether in an action of contract, tort or otherwise, " +
"arising from, out of or in connection with the software or the use or other dealings in the software.\n\n" +
"2. The user is responsible to use the software in compliance with local laws.\n\n" +
"3. The user confirms that he has read and agreed to the rules defined in our " +
"Wiki regrading the dispute process\n" +
"(https://github.com/bitsquare/bitsquare/wiki/Arbitration-system).";
if (!preferences.getTacAccepted() && !BitsquareApp.DEV_MODE) {
new Popup().headLine("USER AGREEMENT")
.message(text)
.actionButtonText("I agree")
.closeButtonText("I disagree and quit")
.onAction(() -> {
preferences.setTacAccepted(true);
if (preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET)
UserThread.runAfter(() -> new Popup()
.warning("This software is still in alpha version.\n" +
"Please be aware that using Mainnet comes with the risk to lose funds " +
"in case of software bugs.\n" +
"To limit the possible losses the maximum allowed trading amount and the " +
"security deposit have been reduced to 0.01 BTC for the alpha version " +
"when using Mainnet.")
.headLine("Important information!")
.actionButtonText("I understand and want to use Mainnet")
.closeButtonText("Restart and use Testnet")
.onClose(() -> {
UserThread.execute(() -> preferences.setBitcoinNetwork(BitcoinNetwork.TESTNET));
UserThread.runAfter(BitsquareApp.shutDownHandler::run, 300, TimeUnit.MILLISECONDS);
})
.width(600)
.show(), 300, TimeUnit.MILLISECONDS);
})
.onClose(BitsquareApp.shutDownHandler::run)
.show();
}
// update nr of peers in footer // now show app
showAppScreen.set(true);
}
///////////////////////////////////////////////////////////////////////////////////////////
// States
///////////////////////////////////////////////////////////////////////////////////////////
private void applyTradePeriodState() {
updateTradePeriodState();
tradeWalletService.addBlockChainListener(new BlockChainListener() {
@Override
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
updateTradePeriodState();
}
@Override
public void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks)
throws VerificationException {
}
@Override
public boolean isTransactionRelevant(Transaction tx) throws ScriptException {
return false;
}
@Override
public void receiveFromBlock(Transaction tx, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset)
throws VerificationException {
}
@Override
public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, AbstractBlockChain.NewBlockType blockType,
int relativityOffset) throws VerificationException {
return false;
}
});
}
private void updateTradePeriodState() {
tradeManager.getTrades().stream().forEach(trade -> {
int bestChainHeight = tradeWalletService.getBestChainHeight();
if (trade.getOpenDisputeTimeAsBlockHeight() > 0 && bestChainHeight >= trade.getOpenDisputeTimeAsBlockHeight())
trade.setTradePeriodState(Trade.TradePeriodState.TRADE_PERIOD_OVER);
else if (trade.getCheckPaymentTimeAsBlockHeight() > 0 && bestChainHeight >= trade.getCheckPaymentTimeAsBlockHeight())
trade.setTradePeriodState(Trade.TradePeriodState.HALF_REACHED);
String id;
String limitDate = formatter.addBlocksToNowDateFormatted(trade.getOpenDisputeTimeAsBlockHeight() - tradeWalletService.getBestChainHeight());
switch (trade.getTradePeriodState()) {
case NORMAL:
break;
case HALF_REACHED:
id = "displayHalfTradePeriodOver" + trade.getId();
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
preferences.dontShowAgain(id);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the half of the max. allowed trading period and " +
"is still not completed.\n\n" +
"The trade period ends on " + limitDate + "\n\n" +
"Please check your trade state at \"Portfolio/Open trades\" for further information.")
.show();
}
break;
case TRADE_PERIOD_OVER:
id = "displayTradePeriodOver" + trade.getId();
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
preferences.dontShowAgain(id);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the max. allowed trading period and is " +
"not completed.\n\n" +
"The trade period ended on " + limitDate + "\n\n" +
"Please check your trade at \"Portfolio/Open trades\" for contacting " +
"the arbitrator.")
.show();
}
break;
}
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void setupP2PPeersInfo() {
numConnectedPeersListener = (observable, oldValue, newValue) -> { numConnectedPeersListener = (observable, oldValue, newValue) -> {
if ((int) oldValue > 0 && (int) newValue == 0) { if ((int) oldValue > 0 && (int) newValue == 0) {
// give a bit of tolerance // give a bit of tolerance
@ -492,19 +539,10 @@ public class MainViewModel implements ViewModel {
updateP2pNetworkInfoWithPeersChanged((int) newValue); updateP2pNetworkInfoWithPeersChanged((int) newValue);
}; };
p2PService.getNumConnectedPeers().addListener(numConnectedPeersListener); p2PService.getNumConnectedPeers().addListener(numConnectedPeersListener);
}
// now show app
showAppScreen.set(true);
if (BitsquareApp.DEV_MODE && user.getPaymentAccounts().isEmpty()) {
OKPayAccount okPayAccount = new OKPayAccount();
okPayAccount.setAccountNr("dummy");
okPayAccount.setAccountName("OKPay dummy");
okPayAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency());
okPayAccount.setCountry(CountryUtil.getDefaultCountry());
user.addPaymentAccount(okPayAccount);
}
private void setupMarketPriceFeed() {
if (marketPriceFeed.getCurrencyCode() == null) if (marketPriceFeed.getCurrencyCode() == null)
marketPriceFeed.setCurrencyCode(preferences.getPreferredTradeCurrency().getCode()); marketPriceFeed.setCurrencyCode(preferences.getPreferredTradeCurrency().getCode());
if (marketPriceFeed.getType() == null) if (marketPriceFeed.getType() == null)
@ -605,130 +643,39 @@ public class MainViewModel implements ViewModel {
} }
/////////////////////////////////////////////////////////////////////////////////////////// private void onDisputesChangeListener(List<? extends Dispute> addedList, @Nullable List<? extends Dispute> removedList) {
// UI callbacks if (removedList != null) {
/////////////////////////////////////////////////////////////////////////////////////////// removedList.stream().forEach(dispute -> {
String id = dispute.getId();
void onSplashScreenRemoved() { if (disputeIsClosedSubscriptionsMap.containsKey(id)) {
isSplashScreenRemoved.set(true); disputeIsClosedSubscriptionsMap.get(id).unsubscribe();
} disputeIsClosedSubscriptionsMap.remove(id);
}
});
///////////////////////////////////////////////////////////////////////////////////////////
// Apply states
///////////////////////////////////////////////////////////////////////////////////////////
private void applyTradePeriodState() {
updateTradePeriodState();
tradeWalletService.addBlockChainListener(new BlockChainListener() {
@Override
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
updateTradePeriodState();
}
@Override
public void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks)
throws VerificationException {
}
@Override
public boolean isTransactionRelevant(Transaction tx) throws ScriptException {
return false;
}
@Override
public void receiveFromBlock(Transaction tx, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset)
throws VerificationException {
}
@Override
public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, AbstractBlockChain.NewBlockType blockType,
int relativityOffset) throws VerificationException {
return false;
}
});
}
private void setWalletServiceException(Throwable error) {
setBitcoinNetworkSyncProgress(0);
btcSplashInfo.set("Nr. of Bitcoin network peers: " + numBTCPeers + " / connecting to " + btcNetworkAsString + " failed");
btcFooterInfo.set(btcSplashInfo.get());
if (error instanceof TimeoutException) {
walletServiceErrorMsg.set("Connecting to the bitcoin network failed because of a timeout.");
} else if (error.getCause() instanceof BlockStoreException) {
new Popup().warning("Bitsquare is already running. You cannot run 2 instances of Bitsquare.")
.closeButtonText("Shut down")
.onClose(BitsquareApp.shutDownHandler::run)
.show();
} else if (error.getMessage() != null) {
walletServiceErrorMsg.set("Connection to the bitcoin network failed because of an error:" + error.getMessage());
} else {
walletServiceErrorMsg.set("Connection to the bitcoin network failed because of an error:" + error.toString());
} }
} addedList.stream().forEach(dispute -> {
String id = dispute.getId();
if (disputeIsClosedSubscriptionsMap.containsKey(id)) {
log.warn("We have already an entry in disputeStateSubscriptionsMap. That should never happen.");
} else {
Subscription disputeStateSubscription = EasyBind.subscribe(dispute.isClosedProperty(),
disputeState -> {
int openDisputes = disputeManager.getDisputesAsObservableList().stream()
.filter(e -> !e.isClosed())
.collect(Collectors.toList()).size();
if (openDisputes > 0)
numOpenDisputesAsString.set(String.valueOf(openDisputes));
if (openDisputes > 9)
numOpenDisputesAsString.set("?");
private void updateTradePeriodState() { showOpenDisputesNotification.set(openDisputes > 0);
tradeManager.getTrades().stream().forEach(trade -> { });
int bestChainHeight = tradeWalletService.getBestChainHeight(); disputeIsClosedSubscriptionsMap.put(id, disputeStateSubscription);
if (trade.getOpenDisputeTimeAsBlockHeight() > 0 && bestChainHeight >= trade.getOpenDisputeTimeAsBlockHeight())
trade.setTradePeriodState(Trade.TradePeriodState.TRADE_PERIOD_OVER);
else if (trade.getCheckPaymentTimeAsBlockHeight() > 0 && bestChainHeight >= trade.getCheckPaymentTimeAsBlockHeight())
trade.setTradePeriodState(Trade.TradePeriodState.HALF_REACHED);
String id;
String limitDate = formatter.addBlocksToNowDateFormatted(trade.getOpenDisputeTimeAsBlockHeight() - tradeWalletService.getBestChainHeight());
switch (trade.getTradePeriodState()) {
case NORMAL:
break;
case HALF_REACHED:
id = "displayHalfTradePeriodOver" + trade.getId();
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
preferences.dontShowAgain(id);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the half of the max. allowed trading period and " +
"is still not completed.\n\n" +
"The trade period ends on " + limitDate + "\n\n" +
"Please check your trade state at \"Portfolio/Open trades\" for further information.")
.show();
}
break;
case TRADE_PERIOD_OVER:
id = "displayTradePeriodOver" + trade.getId();
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE) {
preferences.dontShowAgain(id);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the max. allowed trading period and is " +
"not completed.\n\n" +
"The trade period ended on " + limitDate + "\n\n" +
"Please check your trade at \"Portfolio/Open trades\" for contacting " +
"the arbitrator.")
.show();
}
break;
} }
}); });
} }
private void addDisputeClosedChangeListener(List<? extends Dispute> list) { private void onTradesChanged() {
list.stream().forEach(e -> e.isClosedProperty().addListener((observable, oldValue, newValue) -> {
if (newValue)
updateDisputeStates();
}));
}
private void updateDisputeStates() {
int openDisputes = disputeManager.getDisputesAsObservableList().stream().filter(e -> !e.isClosed()).collect(Collectors.toList()).size();
if (openDisputes > 0)
numOpenDisputesAsString.set(String.valueOf(openDisputes));
if (openDisputes > 9)
numOpenDisputesAsString.set("?");
showOpenDisputesNotification.set(openDisputes > 0);
}
private void pendingTradesChanged() {
long numPendingTrades = tradeManager.getTrades().size(); long numPendingTrades = tradeManager.getTrades().size();
if (numPendingTrades > 0) if (numPendingTrades > 0)
numPendingTradesAsString.set(String.valueOf(numPendingTrades)); numPendingTradesAsString.set(String.valueOf(numPendingTrades));
@ -738,132 +685,6 @@ public class MainViewModel implements ViewModel {
showPendingTradesNotification.set(numPendingTrades > 0); showPendingTradesNotification.set(numPendingTrades > 0);
} }
private void setTradeStateSubscriptions() {
tradeStateSubscriptions.stream().forEach(Subscription::unsubscribe);
tradeStateSubscriptions.clear();
tradeManager.getTrades().stream().forEach(trade -> {
Subscription tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
if (newValue != null) {
applyTradeState(trade);
}
});
tradeStateSubscriptions.add(tradeStateSubscription);
});
}
private void applyTradeState(Trade trade) {
Trade.State state = trade.getState();
log.debug("addTradeStateListeners " + state);
boolean isBtcBuyer = tradeManager.isMyOfferInBtcBuyerRole(trade.getOffer());
String headLine = "Notification for trade with ID " + trade.getShortId();
String message = null;
String id = "notificationPopup_" + state + trade.getId();
if (isBtcBuyer) {
switch (state) {
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
message = "Your offer has been accepted by a seller.\n" +
"You need to wait for one blockchain confirmation before starting the payment.";
break;
case DEPOSIT_CONFIRMED:
message = "The deposit transaction of your trade has got the first blockchain confirmation.\n" +
"You have to start the payment to the bitcoin seller now.";
break;
/* case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED:
case PAYOUT_TX_COMMITTED:
case PAYOUT_TX_SENT:*/
case PAYOUT_BROAD_CASTED:
message = "The bitcoin seller has confirmed the receipt of your payment and the payout transaction has been published.\n" +
"The trade is now completed and you can withdraw your funds.";
break;
}
} else {
switch (state) {
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
message = "Your offer has been accepted by a buyer.\n" +
"You need to wait for one blockchain confirmation before starting the payment.";
break;
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
message = "The bitcoin buyer has started the payment.\n" +
"Please check your payment account if you have received his payment.";
break;
/* case FIAT_PAYMENT_RECEIPT_MSG_SENT:
case PAYOUT_TX_RECEIVED:
case PAYOUT_TX_COMMITTED:*/
case PAYOUT_BROAD_CASTED:
message = "The payout transaction has been published.\n" +
"The trade is now completed and you can withdraw your funds.";
}
}
ViewPath currentPath = navigation.getCurrentPath();
boolean isPendingTradesViewCurrentView = currentPath != null &&
currentPath.size() == 3 &&
currentPath.get(2).equals(PendingTradesView.class);
if (message != null) {
//TODO we get that called initially before the navigation is inited
if (isPendingTradesViewCurrentView || currentPath == null) {
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE)
new Popup().headLine(headLine)
.message(message)
.show();
preferences.dontShowAgain(id);
} else {
if (preferences.showAgain(id) && !BitsquareApp.DEV_MODE)
new Popup().headLine(headLine)
.message(message)
.actionButtonText("Go to \"Portfolio/Open trades\"")
.onAction(() -> {
FxTimer.runLater(Duration.ofMillis(100),
() -> navigation.navigateTo(MainView.class, PortfolioView.class, PendingTradesView.class)
);
})
.show();
preferences.dontShowAgain(id);
}
}
}
private void setDisputeStateSubscriptions() {
disputeStateSubscriptions.stream().forEach(Subscription::unsubscribe);
disputeStateSubscriptions.clear();
tradeManager.getTrades().stream().forEach(trade -> {
Subscription disputeStateSubscription = EasyBind.subscribe(trade.disputeStateProperty(), disputeState -> {
if (disputeState != null) {
applyDisputeState(trade, disputeState);
}
});
disputeStateSubscriptions.add(disputeStateSubscription);
});
}
private void applyDisputeState(Trade trade, Trade.DisputeState disputeState) {
switch (disputeState) {
case NONE:
break;
case DISPUTE_REQUESTED:
break;
case DISPUTE_STARTED_BY_PEER:
disputeManager.findOwnDispute(trade.getId()).ifPresent(dispute -> {
String msg;
if (dispute.isSupportTicket())
msg = "Your trading peer has encountered technical problems and requested support for trade with ID " + trade.getShortId() + ".\n" +
"Please await further instructions from the arbitrator.\n" +
"Your funds are safe and will be refunded as soon the problem is resolved.";
else
msg = "Your trading peer has requested a dispute for trade with ID " + trade.getShortId() + ".";
new Popup().information(msg).show();
});
break;
case DISPUTE_CLOSED:
new Popup().information("A support ticket for trade with ID " + trade.getShortId() + " has been closed.").show();
break;
}
}
private void setBitcoinNetworkSyncProgress(double value) { private void setBitcoinNetworkSyncProgress(double value) {
btcSyncProgress.set(value); btcSyncProgress.set(value);
String numPeers = "Nr. of Bitcoin network peers: " + numBTCPeers; String numPeers = "Nr. of Bitcoin network peers: " + numBTCPeers;
@ -885,10 +706,39 @@ public class MainViewModel implements ViewModel {
} }
} }
private void setWalletServiceException(Throwable error) {
setBitcoinNetworkSyncProgress(0);
btcSplashInfo.set("Nr. of Bitcoin network peers: " + numBTCPeers + " / connecting to " + btcNetworkAsString + " failed");
btcFooterInfo.set(btcSplashInfo.get());
if (error instanceof TimeoutException) {
walletServiceErrorMsg.set("Connecting to the bitcoin network failed because of a timeout.");
} else if (error.getCause() instanceof BlockStoreException) {
new Popup().warning("Bitsquare is already running. You cannot run 2 instances of Bitsquare.")
.closeButtonText("Shut down")
.onClose(BitsquareApp.shutDownHandler::run)
.show();
} else if (error.getMessage() != null) {
walletServiceErrorMsg.set("Connection to the bitcoin network failed because of an error:" + error.getMessage());
} else {
walletServiceErrorMsg.set("Connection to the bitcoin network failed because of an error:" + error.toString());
}
}
private void stopCheckForBtcSyncStateTimer() { private void stopCheckForBtcSyncStateTimer() {
if (checkForBtcSyncStateTimer != null) { if (checkForBtcSyncStateTimer != null) {
checkForBtcSyncStateTimer.stop(); checkForBtcSyncStateTimer.stop();
checkForBtcSyncStateTimer = null; checkForBtcSyncStateTimer = null;
} }
} }
private void setupDevDummyPaymentAccount() {
if (BitsquareApp.DEV_MODE && user.getPaymentAccounts().isEmpty()) {
OKPayAccount okPayAccount = new OKPayAccount();
okPayAccount.setAccountNr("dummy");
okPayAccount.setAccountName("OKPay dummy");
okPayAccount.setSelectedTradeCurrency(CurrencyUtil.getDefaultTradeCurrency());
okPayAccount.setCountry(CountryUtil.getDefaultCountry());
user.addPaymentAccount(okPayAccount);
}
}
} }

View file

@ -575,7 +575,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
// TODO There are still some cell rendering issues on updates // TODO There are still some cell rendering issues on updates
setGraphic(messageAnchorPane); setGraphic(messageAnchorPane);
} else { } else {
if (sendMsgProgressIndicator != null) if (sendMsgProgressIndicator != null && sendMsgProgressIndicatorListener != null)
sendMsgProgressIndicator.progressProperty().removeListener(sendMsgProgressIndicatorListener); sendMsgProgressIndicator.progressProperty().removeListener(sendMsgProgressIndicatorListener);
messageAnchorPane.prefWidthProperty().unbind(); messageAnchorPane.prefWidthProperty().unbind();
@ -796,7 +796,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
} else { } else {
if (closedProperty != null) if (closedProperty != null)
closedProperty.removeListener(listener); closedProperty.removeListener(listener);
setText(""); setText("");
} }
} }

View file

@ -0,0 +1,15 @@
package io.bitsquare.gui.main.intructions;
import com.google.inject.Inject;
import io.bitsquare.gui.main.popups.Popup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Instruction extends Popup {
private static final Logger log = LoggerFactory.getLogger(Instruction.class);
@Inject
public Instruction() {
}
}

View file

@ -0,0 +1,49 @@
package io.bitsquare.gui.main.intructions;
import com.google.inject.Inject;
import io.bitsquare.gui.main.notifications.Notification;
import io.bitsquare.trade.TradeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
public class InstructionCenter {
private final Logger log = LoggerFactory.getLogger(InstructionCenter.class);
private Queue<io.bitsquare.gui.main.notifications.Notification> notifications = new LinkedBlockingQueue<>(3);
private io.bitsquare.gui.main.notifications.Notification displayedNotification;
private TradeManager tradeManager;
@Inject
public InstructionCenter(TradeManager tradeManager) {
this.tradeManager = tradeManager;
}
void queueForDisplay(io.bitsquare.gui.main.notifications.Notification notification) {
boolean result = notifications.offer(notification);
if (!result)
log.warn("The capacity is full with popups in the queue.\n\t" +
"Not added new notification=" + notification);
displayNext();
}
void isHidden(Notification notification) {
if (displayedNotification == null || displayedNotification == notification) {
displayedNotification = null;
displayNext();
} else {
log.warn("We got a isHidden called with a wrong notification.\n\t" +
"notification (argument)=" + notification + "\n\tdisplayedPopup=" + displayedNotification);
}
}
private void displayNext() {
if (displayedNotification == null) {
if (!notifications.isEmpty()) {
displayedNotification = notifications.poll();
displayedNotification.display();
}
}
}
}

View file

@ -0,0 +1,43 @@
package io.bitsquare.gui.main.notifications;
import io.bitsquare.gui.main.popups.Popup;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Notification extends Popup {
private static final Logger log = LoggerFactory.getLogger(Notification.class);
private boolean hasBeenDisplayed;
public Notification() {
NotificationCenter.add(this);
}
public Notification headLine(String headLine) {
return (Notification) super.headLine(headLine);
}
public Notification tradeHeadLine(String tradeId) {
return headLine("Notification for trade with ID " + tradeId);
}
public Notification disputeHeadLine(String tradeId) {
return headLine("Support ticket for trade with ID " + tradeId);
}
public Notification message(String message) {
return (Notification) super.message(message);
}
public void show() {
super.show();
hasBeenDisplayed = true;
}
public void hide() {
super.hide();
}
public boolean isHasBeenDisplayed() {
return hasBeenDisplayed;
}
}

View file

@ -1,14 +1,195 @@
package io.bitsquare.gui.main.notifications; package io.bitsquare.gui.main.notifications;
import com.google.inject.Inject; import com.google.inject.Inject;
import io.bitsquare.app.Log;
import io.bitsquare.arbitration.DisputeManager;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import javafx.collections.ListChangeListener;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class NotificationCenter { public class NotificationCenter {
private static final Logger log = LoggerFactory.getLogger(NotificationCenter.class); private static final Logger log = LoggerFactory.getLogger(NotificationCenter.class);
@Inject
public NotificationCenter() {
///////////////////////////////////////////////////////////////////////////////////////////
// Static
///////////////////////////////////////////////////////////////////////////////////////////
private final static List<Notification> notifications = new ArrayList<>();
static void add(Notification notification) {
notifications.add(notification);
} }
///////////////////////////////////////////////////////////////////////////////////////////
// Instance fields
///////////////////////////////////////////////////////////////////////////////////////////
private TradeManager tradeManager;
private DisputeManager disputeManager;
private final Map<String, Subscription> disputeStateSubscriptionsMap = new HashMap<>();
private final Map<String, Subscription> tradeStateSubscriptionsMap = new HashMap<>();
private String selectedTradeId;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, initialisation
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public NotificationCenter(TradeManager tradeManager, DisputeManager disputeManager) {
this.tradeManager = tradeManager;
this.disputeManager = disputeManager;
}
public void onAllServicesInitialized() {
tradeManager.getTrades().addListener((ListChangeListener<Trade>) change -> {
change.next();
log.error("change getRemoved " + change.getRemoved());
log.error("change getAddedSubList " + change.getAddedSubList());
if (change.wasRemoved()) {
change.getRemoved().stream().forEach(trade -> {
String tradeId = trade.getId();
if (disputeStateSubscriptionsMap.containsKey(tradeId)) {
disputeStateSubscriptionsMap.get(tradeId).unsubscribe();
disputeStateSubscriptionsMap.remove(tradeId);
}
if (tradeStateSubscriptionsMap.containsKey(tradeId)) {
tradeStateSubscriptionsMap.get(tradeId).unsubscribe();
tradeStateSubscriptionsMap.remove(tradeId);
}
});
}
if (change.wasAdded()) {
change.getAddedSubList().stream().forEach(trade -> {
String tradeId = trade.getId();
if (disputeStateSubscriptionsMap.containsKey(tradeId)) {
log.warn("We have already an entry in disputeStateSubscriptionsMap. That should never happen.");
} else {
Subscription disputeStateSubscription = EasyBind.subscribe(trade.disputeStateProperty(), disputeState -> onDisputeStateChanged(trade, disputeState));
disputeStateSubscriptionsMap.put(tradeId, disputeStateSubscription);
}
if (tradeStateSubscriptionsMap.containsKey(tradeId)) {
log.warn("We have already an entry in tradeStateSubscriptionsMap. That should never happen.");
} else {
Subscription tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), tradeState -> onTradeStateChanged(trade, tradeState));
tradeStateSubscriptionsMap.put(tradeId, tradeStateSubscription);
}
});
}
});
tradeManager.getTrades().stream()
.forEach(trade -> {
String tradeId = trade.getId();
Subscription disputeStateSubscription = EasyBind.subscribe(trade.disputeStateProperty(), disputeState -> onDisputeStateChanged(trade, disputeState));
disputeStateSubscriptionsMap.put(tradeId, disputeStateSubscription);
Subscription tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), tradeState -> onTradeStateChanged(trade, tradeState));
tradeStateSubscriptionsMap.put(tradeId, tradeStateSubscription);
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Setter/Getter
///////////////////////////////////////////////////////////////////////////////////////////
public String getSelectedTradeId() {
return selectedTradeId;
}
public void setSelectedTradeId(String selectedTradeId) {
this.selectedTradeId = selectedTradeId;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void onTradeStateChanged(Trade trade, Trade.State tradeState) {
Log.traceCall(tradeState.toString());
String message = null;
if (tradeManager.isBuyer(trade.getOffer())) {
switch (tradeState) {
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
message = "Your offer has been accepted by a seller.\n" +
"You need to wait for one blockchain confirmation before starting the payment.";
break;
case DEPOSIT_CONFIRMED:
message = "The deposit transaction of your trade has got the first blockchain confirmation.\n" +
"You have to start the payment to the bitcoin seller now.";
break;
/* case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED:
case PAYOUT_TX_COMMITTED:
case PAYOUT_TX_SENT:*/
case PAYOUT_BROAD_CASTED:
message = "The bitcoin seller has confirmed the receipt of your payment and the payout transaction has been published.\n" +
"The trade is now completed and you can withdraw your funds.";
break;
}
} else {
switch (tradeState) {
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
message = "Your offer has been accepted by a buyer.\n" +
"You need to wait for one blockchain confirmation before starting the payment.";
break;
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
message = "The bitcoin buyer has started the payment.\n" +
"Please check your payment account if you have received his payment.";
break;
/* case FIAT_PAYMENT_RECEIPT_MSG_SENT:
case PAYOUT_TX_RECEIVED:
case PAYOUT_TX_COMMITTED:*/
case PAYOUT_BROAD_CASTED:
message = "The payout transaction has been published.\n" +
"The trade is now completed and you can withdraw your funds.";
}
}
if (message != null && !trade.getId().equals(selectedTradeId))
new Notification().tradeHeadLine(trade.getShortId()).message(message).show();
}
private void onDisputeStateChanged(Trade trade, Trade.DisputeState disputeState) {
Log.traceCall(disputeState.toString());
String message = null;
switch (disputeState) {
case NONE:
break;
case DISPUTE_REQUESTED:
break;
case DISPUTE_STARTED_BY_PEER:
if (disputeManager.findOwnDispute(trade.getId()).isPresent()) {
if (disputeManager.findOwnDispute(trade.getId()).get().isSupportTicket())
message = "Your trading peer has encountered technical problems and requested support for trade with ID " + trade.getShortId() + ".\n" +
"Please await further instructions from the arbitrator.\n" +
"Your funds are safe and will be refunded as soon the problem is resolved.";
else
message = "Your trading peer has requested a dispute for trade with ID " + trade.getShortId() + ".";
}
break;
case DISPUTE_CLOSED:
message = "A support ticket for trade with ID " + trade.getShortId() + " has been closed.";
break;
}
if (message != null)
new Notification().tradeHeadLine(trade.getShortId()).message(message).show();
}
} }

View file

@ -391,14 +391,6 @@ class CreateOfferDataModel extends ActivatableDataModel {
return user.getAcceptedArbitrators(); return user.getAcceptedArbitrators();
} }
public void setShowPlaceOfferConfirmation(boolean selected) {
preferences.setShowPlaceOfferConfirmation(selected);
}
public boolean getShowPlaceOfferConfirmation() {
return preferences.getShowPlaceOfferConfirmation();
}
public Preferences getPreferences() { public Preferences getPreferences() {
return preferences; return preferences;
} }

View file

@ -47,6 +47,7 @@ import io.bitsquare.locale.BSResources;
import io.bitsquare.locale.TradeCurrency; import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.payment.PaymentAccount; import io.bitsquare.payment.PaymentAccount;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
@ -109,6 +110,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private EventHandler<ActionEvent> currencyComboBoxSelectionHandler; private EventHandler<ActionEvent> currencyComboBoxSelectionHandler;
private int gridRow = 0; private int gridRow = 0;
private Preferences preferences;
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -116,11 +118,12 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
private CreateOfferView(CreateOfferViewModel model, Navigation navigation, OfferDetailsPopup offerDetailsPopup) { private CreateOfferView(CreateOfferViewModel model, Navigation navigation, OfferDetailsPopup offerDetailsPopup, Preferences preferences) {
super(model); super(model);
this.navigation = navigation; this.navigation = navigation;
this.offerDetailsPopup = offerDetailsPopup; this.offerDetailsPopup = offerDetailsPopup;
this.preferences = preferences;
} }
@Override @Override
@ -210,8 +213,11 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
private void onPlaceOffer() { private void onPlaceOffer() {
if (model.isBootstrapped()) { if (model.isBootstrapped()) {
Offer offer = model.createAndGetOffer(); Offer offer = model.createAndGetOffer();
if (model.getShowPlaceOfferConfirmation()) { String id = "CreatOfferConfirmation";
offerDetailsPopup.onPlaceOffer(o -> model.onPlaceOffer(o)).show(offer); if (preferences.showAgain(id)) {
offerDetailsPopup.onPlaceOffer(model::onPlaceOffer)
.dontShowAgainId(id)
.show(offer);
} else { } else {
if (model.hasAcceptedArbitrators()) { if (model.hasAcceptedArbitrators()) {
model.onPlaceOffer(offer); model.onPlaceOffer(offer);

View file

@ -577,13 +577,4 @@ class CreateOfferViewModel extends ActivatableWithDataModel<CreateOfferDataModel
public List<Arbitrator> getArbitrators() { public List<Arbitrator> getArbitrators() {
return dataModel.getArbitrators(); return dataModel.getArbitrators();
} }
public void setShowPlaceOfferConfirmation(boolean selected) {
dataModel.setShowPlaceOfferConfirmation(selected);
}
public boolean getShowPlaceOfferConfirmation() {
return dataModel.getShowPlaceOfferConfirmation();
}
} }

View file

@ -377,14 +377,6 @@ class TakeOfferDataModel extends ActivatableDataModel {
return addressEntry; return addressEntry;
} }
public boolean getShowTakeOfferConfirmation() {
return preferences.getShowTakeOfferConfirmation();
}
public void setShowTakeOfferConfirmation(boolean selected) {
preferences.setShowTakeOfferConfirmation(selected);
}
public List<Arbitrator> getArbitrators() { public List<Arbitrator> getArbitrators() {
return user.getAcceptedArbitrators(); return user.getAcceptedArbitrators();
} }

View file

@ -43,6 +43,7 @@ import io.bitsquare.gui.util.Layout;
import io.bitsquare.locale.BSResources; import io.bitsquare.locale.BSResources;
import io.bitsquare.payment.PaymentAccount; import io.bitsquare.payment.PaymentAccount;
import io.bitsquare.trade.offer.Offer; import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.geometry.*; import javafx.geometry.*;
@ -71,6 +72,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private final Navigation navigation; private final Navigation navigation;
private final BSFormatter formatter; private final BSFormatter formatter;
private final OfferDetailsPopup offerDetailsPopup; private final OfferDetailsPopup offerDetailsPopup;
private Preferences preferences;
private ScrollPane scrollPane; private ScrollPane scrollPane;
private GridPane gridPane; private GridPane gridPane;
private ImageView imageView; private ImageView imageView;
@ -108,12 +110,14 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@Inject @Inject
private TakeOfferView(TakeOfferViewModel model, Navigation navigation, BSFormatter formatter, OfferDetailsPopup offerDetailsPopup) { private TakeOfferView(TakeOfferViewModel model, Navigation navigation, BSFormatter formatter,
OfferDetailsPopup offerDetailsPopup, Preferences preferences) {
super(model); super(model);
this.navigation = navigation; this.navigation = navigation;
this.formatter = formatter; this.formatter = formatter;
this.offerDetailsPopup = offerDetailsPopup; this.offerDetailsPopup = offerDetailsPopup;
this.preferences = preferences;
} }
@Override @Override
@ -349,8 +353,11 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
private void onTakeOffer() { private void onTakeOffer() {
Offer offer = model.getOffer(); Offer offer = model.getOffer();
if (model.getShowTakeOfferConfirmation()) { String id = "TakeOfferConfirmation";
offerDetailsPopup.onTakeOffer(() -> model.onTakeOffer()).show(offer, model.dataModel.amountAsCoin.get()); if (preferences.showAgain(id)) {
offerDetailsPopup.onTakeOffer(() -> model.onTakeOffer())
.dontShowAgainId(id)
.show(offer, model.dataModel.amountAsCoin.get());
} else { } else {
if (model.hasAcceptedArbitrators()) { if (model.hasAcceptedArbitrators()) {
model.onTakeOffer(); model.onTakeOffer();

View file

@ -543,10 +543,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
return dataModel.getTradeCurrency(); return dataModel.getTradeCurrency();
} }
public boolean getShowTakeOfferConfirmation() {
return dataModel.getShowTakeOfferConfirmation();
}
public List<Arbitrator> getArbitrators() { public List<Arbitrator> getArbitrators() {
return dataModel.getArbitrators(); return dataModel.getArbitrators();
} }

View file

@ -33,7 +33,6 @@ import io.bitsquare.user.Preferences;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
@ -51,7 +50,7 @@ public class OfferDetailsPopup extends Popup {
protected static final Logger log = LoggerFactory.getLogger(OfferDetailsPopup.class); protected static final Logger log = LoggerFactory.getLogger(OfferDetailsPopup.class);
private final BSFormatter formatter; private final BSFormatter formatter;
private final Preferences preferences; protected final Preferences preferences;
private final User user; private final User user;
private KeyRing keyRing; private KeyRing keyRing;
private final Navigation navigation; private final Navigation navigation;
@ -97,6 +96,11 @@ public class OfferDetailsPopup extends Popup {
return this; return this;
} }
public OfferDetailsPopup dontShowAgainId(String dontShowAgainId) {
this.dontShowAgainId = dontShowAgainId;
return this;
}
public OfferDetailsPopup onPlaceOffer(Consumer<Offer> placeOfferHandler) { public OfferDetailsPopup onPlaceOffer(Consumer<Offer> placeOfferHandler) {
this.placeOfferHandlerOptional = Optional.of(placeOfferHandler); this.placeOfferHandlerOptional = Optional.of(placeOfferHandler);
return this; return this;
@ -189,13 +193,13 @@ public class OfferDetailsPopup extends Popup {
addLabelTextField(gridPane, rowIndex, "Please note:", Offer.TAC_OFFERER, Layout.FIRST_ROW_AND_GROUP_DISTANCE); addLabelTextField(gridPane, rowIndex, "Please note:", Offer.TAC_OFFERER, Layout.FIRST_ROW_AND_GROUP_DISTANCE);
Button cancelButton = addConfirmButton(true); Button cancelButton = addConfirmButton(true);
addCancelButton(cancelButton, true); addCancelButton(cancelButton);
} else if (takeOfferHandlerOptional.isPresent()) { } else if (takeOfferHandlerOptional.isPresent()) {
addTitledGroupBg(gridPane, ++rowIndex, 1, "Contract", Layout.GROUP_DISTANCE); addTitledGroupBg(gridPane, ++rowIndex, 1, "Contract", Layout.GROUP_DISTANCE);
addLabelTextField(gridPane, rowIndex, "Terms and conditions:", Offer.TAC_TAKER, Layout.FIRST_ROW_AND_GROUP_DISTANCE); addLabelTextField(gridPane, rowIndex, "Terms and conditions:", Offer.TAC_TAKER, Layout.FIRST_ROW_AND_GROUP_DISTANCE);
Button cancelButton = addConfirmButton(false); Button cancelButton = addConfirmButton(false);
addCancelButton(cancelButton, false); addCancelButton(cancelButton);
} else { } else {
Button cancelButton = addButtonAfterGroup(gridPane, ++rowIndex, "Close"); Button cancelButton = addButtonAfterGroup(gridPane, ++rowIndex, "Close");
cancelButton.setOnAction(e -> { cancelButton.setOnAction(e -> {
@ -229,19 +233,10 @@ public class OfferDetailsPopup extends Popup {
return tuple.second; return tuple.second;
} }
private void addCancelButton(Button cancelButton, boolean isPlaceOffer) { private void addCancelButton(Button cancelButton) {
cancelButton.setOnAction(e -> { cancelButton.setOnAction(e -> {
closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run()); closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run());
hide(); hide();
}); });
CheckBox checkBox = addCheckBox(gridPane, ++rowIndex, "Don't show again", 5);
if (isPlaceOffer) {
checkBox.setSelected(!preferences.getShowPlaceOfferConfirmation());
checkBox.setOnAction(e -> preferences.setShowPlaceOfferConfirmation(!checkBox.isSelected()));
} else {
checkBox.setSelected(!preferences.getShowTakeOfferConfirmation());
checkBox.setOnAction(e -> preferences.setShowTakeOfferConfirmation(!checkBox.isSelected()));
}
} }
} }

View file

@ -70,7 +70,7 @@ public class Popup {
private boolean showProgressIndicator; private boolean showProgressIndicator;
private Button actionButton; private Button actionButton;
protected Label headLineLabel; protected Label headLineLabel;
private String dontShowAgainId; protected String dontShowAgainId;
private Preferences preferences; private Preferences preferences;
private ChangeListener<Number> positionListener; private ChangeListener<Number> positionListener;
private Timer centerTime; private Timer centerTime;

View file

@ -0,0 +1,63 @@
package io.bitsquare.gui.main.popups;
import com.google.inject.Inject;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.btc.BitcoinNetwork;
import io.bitsquare.common.UserThread;
import io.bitsquare.user.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.TimeUnit;
public class TacPopup extends Popup {
private static final Logger log = LoggerFactory.getLogger(TacPopup.class);
private Preferences preferences;
@Inject
public TacPopup(Preferences preferences) {
this.preferences = preferences;
}
public void showIfNeeded() {
if (!preferences.getTacAccepted() && !BitsquareApp.DEV_MODE) {
// TODO add link: https://bitsquare.io/arbitration_system.pdf
headLine("USER AGREEMENT");
String text = "1. This software is experimental and provided \"as is\", without warranty of any kind, " +
"express or implied, including but not limited to the warranties of " +
"merchantability, fitness for a particular purpose and non-infringement.\n" +
"In no event shall the authors or copyright holders be liable for any claim, damages or other " +
"liability, whether in an action of contract, tort or otherwise, " +
"arising from, out of or in connection with the software or the use or other dealings in the software.\n\n" +
"2. The user is responsible to use the software in compliance with local laws.\n\n" +
"3. The user confirms that he has read and agreed to the rules defined in our " +
"Wiki regrading the dispute process\n" +
"(https://github.com/bitsquare/bitsquare/wiki/Arbitration-system).";
message(text);
actionButtonText("I agree");
closeButtonText("I disagree and quit");
onAction(() -> {
preferences.setTacAccepted(true);
if (preferences.getBitcoinNetwork() == BitcoinNetwork.MAINNET)
UserThread.runAfter(() -> new Popup()
.warning("This software is still in alpha version.\n" +
"Please be aware that using Mainnet comes with the risk to lose funds " +
"in case of software bugs.\n" +
"To limit the possible losses the maximum allowed trading amount and the " +
"security deposit have been reduced to 0.01 BTC for the alpha version " +
"when using Mainnet.")
.headLine("Important information!")
.actionButtonText("I understand and want to use Mainnet")
.closeButtonText("Restart and use Testnet")
.onClose(() -> {
UserThread.execute(() -> preferences.setBitcoinNetwork(BitcoinNetwork.TESTNET));
UserThread.runAfter(BitsquareApp.shutDownHandler::run, 300, TimeUnit.MILLISECONDS);
})
.width(600)
.show(), 300, TimeUnit.MILLISECONDS);
});
onClose(BitsquareApp.shutDownHandler::run);
super.show();
}
}
}

View file

@ -127,12 +127,8 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
selectedSubView.deactivate(); selectedSubView.deactivate();
if (selectedItem.getTrade() != null) { if (selectedItem.getTrade() != null) {
// If we are the offerer the direction is like expected selectedSubView = model.dataModel.tradeManager.isBuyer(model.dataModel.getOffer()) ?
// If we are the taker the direction is mirrored new BuyerSubView(model) : new SellerSubView(model);
if (model.dataModel.isOfferer())
selectedSubView = model.dataModel.isBuyOffer() ? new BuyerSubView(model) : new SellerSubView(model);
else
selectedSubView = model.dataModel.isBuyOffer() ? new SellerSubView(model) : new BuyerSubView(model);
selectedSubView.setMinHeight(430); selectedSubView.setMinHeight(430);
VBox.setVgrow(selectedSubView, Priority.ALWAYS); VBox.setVgrow(selectedSubView, Priority.ALWAYS);

View file

@ -23,6 +23,7 @@ import io.bitsquare.gui.util.Layout;
import io.bitsquare.locale.LanguageUtil; import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.locale.TradeCurrency; import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.user.BlockChainExplorer; import io.bitsquare.user.BlockChainExplorer;
import io.bitsquare.user.Preferences;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.scene.control.CheckBox; import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBox;
@ -38,30 +39,32 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
// not supported yet // not supported yet
//private ComboBox<String> btcDenominationComboBox; //private ComboBox<String> btcDenominationComboBox;
private ComboBox<BlockChainExplorer> blockExplorerComboBox; private ComboBox<BlockChainExplorer> blockChainExplorerComboBox;
private ComboBox<String> languageComboBox; private ComboBox<String> languageComboBox;
private ComboBox<TradeCurrency> tradeCurrencyComboBox; private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox;
private CheckBox useAnimationsCheckBox, useEffectsCheckBox, showPlaceOfferConfirmationCheckBox, showTakeOfferConfirmationCheckBox, private CheckBox useAnimationsCheckBox, useEffectsCheckBox, showNotificationsCheckBox, showInstructionsCheckBox,
autoSelectArbitratorsCheckBox; autoSelectArbitratorsCheckBox;
private int gridRow = 0; private int gridRow = 0;
//private InputTextField transactionFeeInputTextField; //private InputTextField transactionFeeInputTextField;
private ChangeListener<Boolean> transactionFeeFocusedListener; private ChangeListener<Boolean> transactionFeeFocusedListener;
private Preferences preferences;
@Inject @Inject
public PreferencesView(PreferencesViewModel model) { public PreferencesView(PreferencesViewModel model, Preferences preferences) {
super(model); super(model);
this.preferences = preferences;
} }
@Override @Override
public void initialize() { public void initialize() {
addTitledGroupBg(root, gridRow, 4, "Preferences"); addTitledGroupBg(root, gridRow, 4, "Preferences");
tradeCurrencyComboBox = addLabelComboBox(root, gridRow, "Preferred currency:", Layout.FIRST_ROW_DISTANCE).second; preferredTradeCurrencyComboBox = addLabelComboBox(root, gridRow, "Preferred currency:", Layout.FIRST_ROW_DISTANCE).second;
languageComboBox = addLabelComboBox(root, ++gridRow, "Language:").second; languageComboBox = addLabelComboBox(root, ++gridRow, "Language:").second;
// btcDenominationComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin denomination:").second; // btcDenominationComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin denomination:").second;
blockExplorerComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin block explorer:").second; blockChainExplorerComboBox = addLabelComboBox(root, ++gridRow, "Bitcoin block explorer:").second;
autoSelectArbitratorsCheckBox = addLabelCheckBox(root, ++gridRow, "Auto select arbitrators by language:", "").second; autoSelectArbitratorsCheckBox = addLabelCheckBox(root, ++gridRow, "Auto select arbitrators by language:", "").second;
// TODO need a bit extra work to separate trade and non trade tx fees before it can be used // TODO need a bit extra work to separate trade and non trade tx fees before it can be used
/*transactionFeeInputTextField = addLabelInputTextField(root, ++gridRow, "Transaction fee (satoshi/byte):").second; /*transactionFeeInputTextField = addLabelInputTextField(root, ++gridRow, "Transaction fee (satoshi/byte):").second;
transactionFeeFocusedListener = (o, oldValue, newValue) -> { transactionFeeFocusedListener = (o, oldValue, newValue) -> {
@ -71,8 +74,8 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
addTitledGroupBg(root, ++gridRow, 4, "Display options", Layout.GROUP_DISTANCE); addTitledGroupBg(root, ++gridRow, 4, "Display options", Layout.GROUP_DISTANCE);
useAnimationsCheckBox = addLabelCheckBox(root, gridRow, "Use animations:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second; useAnimationsCheckBox = addLabelCheckBox(root, gridRow, "Use animations:", "", Layout.FIRST_ROW_AND_GROUP_DISTANCE).second;
useEffectsCheckBox = addLabelCheckBox(root, ++gridRow, "Use effects:", "").second; useEffectsCheckBox = addLabelCheckBox(root, ++gridRow, "Use effects:", "").second;
showPlaceOfferConfirmationCheckBox = addLabelCheckBox(root, ++gridRow, "Show confirmation at place offer:", "").second; showNotificationsCheckBox = addLabelCheckBox(root, ++gridRow, "Show notifications:", "").second;
showTakeOfferConfirmationCheckBox = addLabelCheckBox(root, ++gridRow, "Show confirmation at take offer:", "").second; showInstructionsCheckBox = addLabelCheckBox(root, ++gridRow, "Show instruction popups:", "").second;
} }
@Override @Override
@ -82,9 +85,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
btcDenominationComboBox.getSelectionModel().select(model.getBtcDenomination()); btcDenominationComboBox.getSelectionModel().select(model.getBtcDenomination());
btcDenominationComboBox.setOnAction(e -> model.onSelectBtcDenomination(btcDenominationComboBox.getSelectionModel().getSelectedItem()));*/ btcDenominationComboBox.setOnAction(e -> model.onSelectBtcDenomination(btcDenominationComboBox.getSelectionModel().getSelectedItem()));*/
tradeCurrencyComboBox.setItems(model.tradeCurrencies); preferredTradeCurrencyComboBox.setItems(model.tradeCurrencies);
tradeCurrencyComboBox.getSelectionModel().select(model.getTradeCurrency()); preferredTradeCurrencyComboBox.getSelectionModel().select(preferences.getPreferredTradeCurrency());
tradeCurrencyComboBox.setConverter(new StringConverter<TradeCurrency>() { preferredTradeCurrencyComboBox.setConverter(new StringConverter<TradeCurrency>() {
@Override @Override
public String toString(TradeCurrency tradeCurrency) { public String toString(TradeCurrency tradeCurrency) {
return tradeCurrency.getNameAndCode(); return tradeCurrency.getNameAndCode();
@ -95,7 +98,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
return null; return null;
} }
}); });
tradeCurrencyComboBox.setOnAction(e -> model.onSelectTradeCurrency(tradeCurrencyComboBox.getSelectionModel().getSelectedItem())); preferredTradeCurrencyComboBox.setOnAction(e -> preferences.setPreferredTradeCurrency(preferredTradeCurrencyComboBox.getSelectionModel().getSelectedItem()));
languageComboBox.setItems(model.languageCodes); languageComboBox.setItems(model.languageCodes);
languageComboBox.getSelectionModel().select(model.getLanguageCode()); languageComboBox.getSelectionModel().select(model.getLanguageCode());
@ -113,9 +116,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
languageComboBox.setOnAction(e -> model.onSelectLanguageCode(languageComboBox.getSelectionModel().getSelectedItem())); languageComboBox.setOnAction(e -> model.onSelectLanguageCode(languageComboBox.getSelectionModel().getSelectedItem()));
blockExplorerComboBox.setItems(model.blockExplorers); blockChainExplorerComboBox.setItems(model.blockExplorers);
blockExplorerComboBox.getSelectionModel().select(model.getBlockExplorer()); blockChainExplorerComboBox.getSelectionModel().select(preferences.getBlockChainExplorer());
blockExplorerComboBox.setConverter(new StringConverter<BlockChainExplorer>() { blockChainExplorerComboBox.setConverter(new StringConverter<BlockChainExplorer>() {
@Override @Override
public String toString(BlockChainExplorer blockChainExplorer) { public String toString(BlockChainExplorer blockChainExplorer) {
return blockChainExplorer.name; return blockChainExplorer.name;
@ -126,39 +129,39 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
return null; return null;
} }
}); });
blockExplorerComboBox.setOnAction(e -> model.onSelectBlockExplorer(blockExplorerComboBox.getSelectionModel().getSelectedItem())); blockChainExplorerComboBox.setOnAction(e -> preferences.setBlockChainExplorer(blockChainExplorerComboBox.getSelectionModel().getSelectedItem()));
// transactionFeeInputTextField.textProperty().bindBidirectional(model.transactionFeePerByte); // transactionFeeInputTextField.textProperty().bindBidirectional(model.transactionFeePerByte);
// transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener); // transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener);
useAnimationsCheckBox.setSelected(model.getUseAnimations()); useAnimationsCheckBox.setSelected(preferences.getUseAnimations());
useAnimationsCheckBox.setOnAction(e -> model.onSelectUseAnimations(useAnimationsCheckBox.isSelected())); useAnimationsCheckBox.setOnAction(e -> preferences.setUseAnimations(useAnimationsCheckBox.isSelected()));
useEffectsCheckBox.setSelected(model.getUseEffects()); useEffectsCheckBox.setSelected(preferences.getUseEffects());
useEffectsCheckBox.setOnAction(e -> model.onSelectUseEffects(useEffectsCheckBox.isSelected())); useEffectsCheckBox.setOnAction(e -> preferences.setUseEffects(useEffectsCheckBox.isSelected()));
showPlaceOfferConfirmationCheckBox.setSelected(model.getShowPlaceOfferConfirmation()); showNotificationsCheckBox.setSelected(preferences.getShowNotifications());
showPlaceOfferConfirmationCheckBox.setOnAction(e -> model.onSelectShowPlaceOfferConfirmation(showPlaceOfferConfirmationCheckBox.isSelected())); showNotificationsCheckBox.setOnAction(e -> preferences.setShowNotifications(showNotificationsCheckBox.isSelected()));
showTakeOfferConfirmationCheckBox.setSelected(model.getShowTakeOfferConfirmation()); showInstructionsCheckBox.setSelected(preferences.getShowInstructions());
showTakeOfferConfirmationCheckBox.setOnAction(e -> model.onSelectShowTakeOfferConfirmation(showTakeOfferConfirmationCheckBox.isSelected())); showInstructionsCheckBox.setOnAction(e -> preferences.setShowInstructions(showInstructionsCheckBox.isSelected()));
autoSelectArbitratorsCheckBox.setSelected(model.getAutoSelectArbitrators()); autoSelectArbitratorsCheckBox.setSelected(preferences.getAutoSelectArbitrators());
autoSelectArbitratorsCheckBox.setOnAction(e -> model.onSelectAutoSelectArbitratorsCheckBox(autoSelectArbitratorsCheckBox.isSelected())); autoSelectArbitratorsCheckBox.setOnAction(e -> preferences.setAutoSelectArbitrators(autoSelectArbitratorsCheckBox.isSelected()));
} }
@Override @Override
protected void deactivate() { protected void deactivate() {
//btcDenominationComboBox.setOnAction(null); //btcDenominationComboBox.setOnAction(null);
languageComboBox.setOnAction(null); languageComboBox.setOnAction(null);
tradeCurrencyComboBox.setOnAction(null); preferredTradeCurrencyComboBox.setOnAction(null);
blockExplorerComboBox.setOnAction(null); blockChainExplorerComboBox.setOnAction(null);
showNotificationsCheckBox.setOnAction(null);
showInstructionsCheckBox.setOnAction(null);
// transactionFeeInputTextField.textProperty().unbind(); // transactionFeeInputTextField.textProperty().unbind();
/// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener); /// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener);
useAnimationsCheckBox.setOnAction(null); useAnimationsCheckBox.setOnAction(null);
useEffectsCheckBox.setOnAction(null); useEffectsCheckBox.setOnAction(null);
showPlaceOfferConfirmationCheckBox.setOnAction(null);
showTakeOfferConfirmationCheckBox.setOnAction(null);
autoSelectArbitratorsCheckBox.setOnAction(null); autoSelectArbitratorsCheckBox.setOnAction(null);
} }
} }

View file

@ -65,42 +65,11 @@ class PreferencesViewModel extends ActivatableViewModel {
protected void deactivate() { protected void deactivate() {
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// UI actions // UI actions
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void onSelectBtcDenomination(String selectedItem) {
preferences.setBtcDenomination(selectedItem);
}
public void onSelectUseEffects(boolean selected) {
preferences.setUseEffects(selected);
}
public void onSelectUseAnimations(boolean selected) {
preferences.setUseAnimations(selected);
}
public void onSelectShowPlaceOfferConfirmation(boolean selected) {
preferences.setShowPlaceOfferConfirmation(selected);
}
public void onSelectShowTakeOfferConfirmation(boolean selected) {
preferences.setShowTakeOfferConfirmation(selected);
}
public void onSelectAutoSelectArbitratorsCheckBox(boolean selected) {
preferences.setAutoSelectArbitrators(selected);
}
public void onSelectBlockExplorer(BlockChainExplorer selectedItem) {
preferences.setBlockChainExplorer(selectedItem);
}
public void onSelectTradeCurrency(TradeCurrency selectedItem) {
preferences.setPreferredTradeCurrency(selectedItem);
}
public void onSelectLanguageCode(String code) { public void onSelectLanguageCode(String code) {
preferences.setPreferredLocale(new Locale(code, preferences.getPreferredLocale().getCountry())); preferences.setPreferredLocale(new Locale(code, preferences.getPreferredLocale().getCountry()));
} }
@ -124,40 +93,8 @@ class PreferencesViewModel extends ActivatableViewModel {
// Getters // Getters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public String getBtcDenomination() {
return preferences.getBtcDenomination();
}
public boolean getUseAnimations() {
return preferences.getUseAnimations();
}
public boolean getUseEffects() {
return preferences.getUseEffects();
}
public boolean getShowPlaceOfferConfirmation() {
return preferences.getShowPlaceOfferConfirmation();
}
public boolean getShowTakeOfferConfirmation() {
return preferences.getShowTakeOfferConfirmation();
}
public boolean getAutoSelectArbitrators() {
return preferences.getAutoSelectArbitrators();
}
public BlockChainExplorer getBlockExplorer() {
return preferences.getBlockChainExplorer();
}
public String getLanguageCode() { public String getLanguageCode() {
return preferences.getPreferredLocale().getLanguage(); return preferences.getPreferredLocale().getLanguage();
} }
public TradeCurrency getTradeCurrency() {
return preferences.getPreferredTradeCurrency();
}
} }