From 6bf2adae7f4a911ac774004fb45f58329b91037d Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 14 Feb 2016 16:45:22 +0100 Subject: [PATCH] Add Notification and Instructions popups. Cleanup MainViewModel --- .../io/bitsquare/arbitration/Dispute.java | 7 + .../java/io/bitsquare/trade/TradeManager.java | 8 +- .../offerer/SetupDepositBalanceListener.java | 5 +- .../java/io/bitsquare/user/Preferences.java | 27 +- .../io/bitsquare/app/BitsquareAppModule.java | 2 + .../io/bitsquare/gui/main/MainViewModel.java | 526 +++++++----------- .../disputes/trader/TraderDisputeView.java | 4 +- .../gui/main/intructions/Instruction.java | 15 + .../main/intructions/InstructionCenter.java | 49 ++ .../gui/main/notifications/Notification.java | 43 ++ .../notifications/NotificationCenter.java | 185 +++++- .../createoffer/CreateOfferDataModel.java | 8 - .../offer/createoffer/CreateOfferView.java | 12 +- .../createoffer/CreateOfferViewModel.java | 9 - .../offer/takeoffer/TakeOfferDataModel.java | 8 - .../main/offer/takeoffer/TakeOfferView.java | 13 +- .../offer/takeoffer/TakeOfferViewModel.java | 4 - .../gui/main/popups/OfferDetailsPopup.java | 23 +- .../io/bitsquare/gui/main/popups/Popup.java | 2 +- .../bitsquare/gui/main/popups/TacPopup.java | 63 +++ .../pendingtrades/PendingTradesView.java | 8 +- .../settings/preferences/PreferencesView.java | 65 +-- .../preferences/PreferencesViewModel.java | 65 +-- 23 files changed, 637 insertions(+), 514 deletions(-) create mode 100644 gui/src/main/java/io/bitsquare/gui/main/intructions/Instruction.java create mode 100644 gui/src/main/java/io/bitsquare/gui/main/intructions/InstructionCenter.java create mode 100644 gui/src/main/java/io/bitsquare/gui/main/notifications/Notification.java create mode 100644 gui/src/main/java/io/bitsquare/gui/main/popups/TacPopup.java diff --git a/core/src/main/java/io/bitsquare/arbitration/Dispute.java b/core/src/main/java/io/bitsquare/arbitration/Dispute.java index a0bb07d13f..584fbb751d 100644 --- a/core/src/main/java/io/bitsquare/arbitration/Dispute.java +++ b/core/src/main/java/io/bitsquare/arbitration/Dispute.java @@ -48,6 +48,7 @@ public class Dispute implements Serializable { /////////////////////////////////////////////////////////////////////////////////////////// private final String tradeId; + private final String id; private final int traderId; private final boolean disputeOpenerIsBuyer; private final boolean disputeOpenerIsOfferer; @@ -123,6 +124,8 @@ public class Dispute implements Serializable { this.arbitratorPubKeyRing = arbitratorPubKeyRing; this.isSupportTicket = isSupportTicket; this.openingDate = new Date().getTime(); + + id = tradeId + "_" + traderId; } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { @@ -177,6 +180,10 @@ public class Dispute implements Serializable { // Getters /////////////////////////////////////////////////////////////////////////////////////////// + public String getId() { + return id; + } + public String getTradeId() { return tradeId; } diff --git a/core/src/main/java/io/bitsquare/trade/TradeManager.java b/core/src/main/java/io/bitsquare/trade/TradeManager.java index 3d61a8ba3e..7967d20874 100644 --- a/core/src/main/java/io/bitsquare/trade/TradeManager.java +++ b/core/src/main/java/io/bitsquare/trade/TradeManager.java @@ -381,8 +381,12 @@ public class TradeManager { return offer.isMyOffer(keyRing); } - public boolean isMyOfferInBtcBuyerRole(Offer offer) { - return !(isMyOffer(offer) ^ offer.getDirection() == Offer.Direction.BUY); + public boolean isBuyer(Offer offer) { + // 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 getTradeById(String tradeId) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SetupDepositBalanceListener.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SetupDepositBalanceListener.java index 189732bee5..040384120e 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SetupDepositBalanceListener.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/offerer/SetupDepositBalanceListener.java @@ -66,8 +66,7 @@ public class SetupDepositBalanceListener extends TradeTask { || newValue == Trade.State.DEPOSIT_SEEN_IN_NETWORK) { walletService.removeBalanceListener(balanceListener); - log.debug(" UserThread.execute(this::unSubscribe);"); - // TODO is that allowed? + // hack to remove tradeStateSubscription at callback UserThread.execute(this::unSubscribe); } }); @@ -81,8 +80,6 @@ public class SetupDepositBalanceListener extends TradeTask { } private void unSubscribe() { - //TODO investigate, seems to not get called sometimes - log.debug("unSubscribe tradeStateSubscription"); tradeStateSubscription.unsubscribe(); } diff --git a/core/src/main/java/io/bitsquare/user/Preferences.java b/core/src/main/java/io/bitsquare/user/Preferences.java index 269720f1b1..1d41128d47 100644 --- a/core/src/main/java/io/bitsquare/user/Preferences.java +++ b/core/src/main/java/io/bitsquare/user/Preferences.java @@ -96,8 +96,8 @@ public class Preferences implements Serializable { private final ArrayList tradeCurrencies; private BlockChainExplorer blockChainExplorerMainNet; private BlockChainExplorer blockChainExplorerTestNet; - private boolean showPlaceOfferConfirmation; - private boolean showTakeOfferConfirmation; + private boolean showNotifications = true; + private boolean showInstructions = true; private String backupDirectory; private boolean autoSelectArbitrators = true; private final Map showAgainMap; @@ -140,8 +140,8 @@ public class Preferences implements Serializable { if (blockChainExplorerMainNet == null) setBlockChainExplorerTestNet(blockChainExplorersMainNet.get(0)); - showPlaceOfferConfirmation = persisted.getShowPlaceOfferConfirmation(); - showTakeOfferConfirmation = persisted.getShowTakeOfferConfirmation(); + showNotifications = persisted.getShowNotifications(); + showInstructions = persisted.getShowInstructions(); backupDirectory = persisted.getBackupDirectory(); autoSelectArbitrators = persisted.getAutoSelectArbitrators(); showAgainMap = persisted.getShowAgainMap(); @@ -163,8 +163,6 @@ public class Preferences implements Serializable { tradeCurrencies = new ArrayList<>(tradeCurrenciesAsObservable); setBlockChainExplorerTestNet(blockChainExplorersTestNet.get(0)); setBlockChainExplorerMainNet(blockChainExplorersMainNet.get(0)); - showPlaceOfferConfirmation = true; - showTakeOfferConfirmation = true; showAgainMap = new HashMap<>(); showAgainMap.put(PopupId.TRADE_WALLET, true); @@ -253,13 +251,13 @@ public class Preferences implements Serializable { setBlockChainExplorerTestNet(blockChainExplorer); } - public void setShowPlaceOfferConfirmation(boolean showPlaceOfferConfirmation) { - this.showPlaceOfferConfirmation = showPlaceOfferConfirmation; + public void setShowNotifications(boolean showNotifications) { + this.showNotifications = showNotifications; storage.queueUpForSave(2000); } - public void setShowTakeOfferConfirmation(boolean showTakeOfferConfirmation) { - this.showTakeOfferConfirmation = showTakeOfferConfirmation; + public void setShowInstructions(boolean showInstructions) { + this.showInstructions = showInstructions; storage.queueUpForSave(2000); } @@ -356,13 +354,12 @@ public class Preferences implements Serializable { return blockChainExplorersTestNet; } - public boolean getShowPlaceOfferConfirmation() { - return showPlaceOfferConfirmation; + public boolean getShowNotifications() { + return showNotifications; } - - public boolean getShowTakeOfferConfirmation() { - return showTakeOfferConfirmation; + public boolean getShowInstructions() { + return showInstructions; } public String getBackupDirectory() { diff --git a/gui/src/main/java/io/bitsquare/app/BitsquareAppModule.java b/gui/src/main/java/io/bitsquare/app/BitsquareAppModule.java index 3336bd50e8..abf37a84e9 100644 --- a/gui/src/main/java/io/bitsquare/app/BitsquareAppModule.java +++ b/gui/src/main/java/io/bitsquare/app/BitsquareAppModule.java @@ -26,6 +26,7 @@ import io.bitsquare.common.crypto.KeyStorage; import io.bitsquare.crypto.EncryptionServiceModule; import io.bitsquare.gui.GuiModule; import io.bitsquare.gui.common.view.CachingViewLoader; +import io.bitsquare.gui.main.intructions.InstructionCenter; import io.bitsquare.gui.main.notifications.NotificationCenter; import io.bitsquare.p2p.P2PModule; import io.bitsquare.storage.Storage; @@ -60,6 +61,7 @@ class BitsquareAppModule extends AppModule { bind(User.class).in(Singleton.class); bind(Preferences.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)); bind(File.class).annotatedWith(named(Storage.DIR_KEY)).toInstance(storageDir); diff --git a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java index ab9e7f34c3..dd2d586859 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/MainViewModel.java @@ -26,21 +26,23 @@ import io.bitsquare.app.Version; import io.bitsquare.arbitration.ArbitratorManager; import io.bitsquare.arbitration.Dispute; 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.pricefeed.MarketPriceFeed; import io.bitsquare.common.UserThread; import io.bitsquare.gui.Navigation; 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.BalanceWithConfirmationTextField; 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.Popup; +import io.bitsquare.gui.main.popups.TacPopup; 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.locale.CountryUtil; import io.bitsquare.locale.CurrencyUtil; @@ -69,9 +71,9 @@ import org.reactfx.util.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.time.Duration; import java.util.*; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -89,6 +91,8 @@ public class MainViewModel implements ViewModel { private final Preferences preferences; private final AlertManager alertManager; private final WalletPasswordPopup walletPasswordPopup; + private NotificationCenter notificationCenter; + private TacPopup tacPopup; private Navigation navigation; private final BSFormatter formatter; @@ -133,8 +137,7 @@ public class MainViewModel implements ViewModel { private java.util.Timer numberofBtcPeersTimer; private java.util.Timer numberofP2PNetworkPeersTimer; private Timer startupTimeout; - private Set tradeStateSubscriptions = new HashSet<>(); - private Set disputeStateSubscriptions = new HashSet<>(); + private final Map disputeIsClosedSubscriptionsMap = new HashMap<>(); /////////////////////////////////////////////////////////////////////////////////////////// @@ -147,6 +150,7 @@ public class MainViewModel implements ViewModel { ArbitratorManager arbitratorManager, P2PService p2PService, TradeManager tradeManager, OpenOfferManager openOfferManager, DisputeManager disputeManager, Preferences preferences, User user, AlertManager alertManager, WalletPasswordPopup walletPasswordPopup, + NotificationCenter notificationCenter, TacPopup tacPopup, Navigation navigation, BSFormatter formatter) { this.marketPriceFeed = marketPriceFeed; this.user = user; @@ -160,6 +164,8 @@ public class MainViewModel implements ViewModel { this.preferences = preferences; this.alertManager = alertManager; this.walletPasswordPopup = walletPasswordPopup; + this.notificationCenter = notificationCenter; + this.tacPopup = tacPopup; this.navigation = navigation; this.formatter = formatter; @@ -172,8 +178,6 @@ public class MainViewModel implements ViewModel { BalanceWithConfirmationTextField.setWalletService(walletService); if (BitsquareApp.DEV_MODE) { - preferences.setShowPlaceOfferConfirmation(false); - preferences.setShowTakeOfferConfirmation(false); preferences.setUseAnimations(false); preferences.setUseEffects(false); } @@ -221,6 +225,16 @@ public class MainViewModel implements ViewModel { } + + /////////////////////////////////////////////////////////////////////////////////////////// + // UI handlers + /////////////////////////////////////////////////////////////////////////////////////////// + + void onSplashScreenRemoved() { + isSplashScreenRemoved.set(true); + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Initialisation /////////////////////////////////////////////////////////////////////////////////////////// @@ -360,33 +374,23 @@ public class MainViewModel implements ViewModel { startupTimeout.stop(); // disputeManager + disputeManager.onAllServicesInitialized(); disputeManager.getDisputesAsObservableList().addListener((ListChangeListener) change -> { change.next(); - addDisputeClosedChangeListener(change.getAddedSubList()); - updateDisputeStates(); + onDisputesChangeListener(change.getAddedSubList(), change.getRemoved()); }); - addDisputeClosedChangeListener(disputeManager.getDisputesAsObservableList()); - updateDisputeStates(); - disputeManager.onAllServicesInitialized(); - + onDisputesChangeListener(disputeManager.getDisputesAsObservableList(), null); // tradeManager tradeManager.getTrades().addListener((ListChangeListener) c -> updateBalance()); - - tradeManager.getTrades().addListener((ListChangeListener) change -> { - change.next(); - setDisputeStateSubscriptions(); - setTradeStateSubscriptions(); - pendingTradesChanged(); + tradeManager.getTrades().addListener((ListChangeListener) change -> onTradesChanged()); + onTradesChanged(); + // 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(); }); - pendingTradesChanged(); - setDisputeStateSubscriptions(); - setTradeStateSubscriptions(); - - - // arbitratorManager - arbitratorManager.onAllServicesInitialized(); - // 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 @@ -398,77 +402,120 @@ public class MainViewModel implements ViewModel { || disputeManager.getDisputesAsObservableList().size() > 0)) { 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() { @Override public void onBalanceChanged(Coin balance, Transaction tx) { updateBalance(); } }); - updateBalance(); setBitcoinNetworkSyncProgress(walletService.downloadPercentageProperty().get()); checkPeriodicallyForBtcSyncState(); - // openOfferManager openOfferManager.getOpenOffers().addListener((ListChangeListener) c -> updateBalance()); openOfferManager.onAllServicesInitialized(); - - - // alertManager + arbitratorManager.onAllServicesInitialized(); alertManager.alertMessageProperty().addListener((observable, oldValue, newValue) -> displayAlertIfPresent(newValue)); displayAlertIfPresent(alertManager.alertMessageProperty().get()); + setupP2PPeersInfo(); + updateBalance(); + setupDevDummyPaymentAccount(); + setupMarketPriceFeed(); - // tac - // TODO add link: https://bitsquare.io/arbitration_system.pdf - 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(); - } + tacPopup.showIfNeeded(); + notificationCenter.onAllServicesInitialized(); - // 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 oldBlocks, List 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) -> { if ((int) oldValue > 0 && (int) newValue == 0) { // give a bit of tolerance @@ -492,19 +539,10 @@ public class MainViewModel implements ViewModel { updateP2pNetworkInfoWithPeersChanged((int) newValue); }; 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) marketPriceFeed.setCurrencyCode(preferences.getPreferredTradeCurrency().getCode()); if (marketPriceFeed.getType() == null) @@ -605,130 +643,39 @@ public class MainViewModel implements ViewModel { } - /////////////////////////////////////////////////////////////////////////////////////////// - // UI callbacks - /////////////////////////////////////////////////////////////////////////////////////////// - - void onSplashScreenRemoved() { - isSplashScreenRemoved.set(true); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // 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 oldBlocks, List 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()); + private void onDisputesChangeListener(List addedList, @Nullable List removedList) { + if (removedList != null) { + removedList.stream().forEach(dispute -> { + String id = dispute.getId(); + if (disputeIsClosedSubscriptionsMap.containsKey(id)) { + disputeIsClosedSubscriptionsMap.get(id).unsubscribe(); + disputeIsClosedSubscriptionsMap.remove(id); + } + }); } - } + 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() { - 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; + showOpenDisputesNotification.set(openDisputes > 0); + }); + disputeIsClosedSubscriptionsMap.put(id, disputeStateSubscription); } }); } - private void addDisputeClosedChangeListener(List list) { - 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() { + private void onTradesChanged() { long numPendingTrades = tradeManager.getTrades().size(); if (numPendingTrades > 0) numPendingTradesAsString.set(String.valueOf(numPendingTrades)); @@ -738,132 +685,6 @@ public class MainViewModel implements ViewModel { 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) { btcSyncProgress.set(value); 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() { if (checkForBtcSyncStateTimer != null) { checkForBtcSyncStateTimer.stop(); 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); + } + } } diff --git a/gui/src/main/java/io/bitsquare/gui/main/disputes/trader/TraderDisputeView.java b/gui/src/main/java/io/bitsquare/gui/main/disputes/trader/TraderDisputeView.java index ede884f52c..5d5469f96c 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/disputes/trader/TraderDisputeView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/disputes/trader/TraderDisputeView.java @@ -575,7 +575,7 @@ public class TraderDisputeView extends ActivatableView { // TODO There are still some cell rendering issues on updates setGraphic(messageAnchorPane); } else { - if (sendMsgProgressIndicator != null) + if (sendMsgProgressIndicator != null && sendMsgProgressIndicatorListener != null) sendMsgProgressIndicator.progressProperty().removeListener(sendMsgProgressIndicatorListener); messageAnchorPane.prefWidthProperty().unbind(); @@ -796,7 +796,7 @@ public class TraderDisputeView extends ActivatableView { } else { if (closedProperty != null) closedProperty.removeListener(listener); - + setText(""); } } diff --git a/gui/src/main/java/io/bitsquare/gui/main/intructions/Instruction.java b/gui/src/main/java/io/bitsquare/gui/main/intructions/Instruction.java new file mode 100644 index 0000000000..13f9b0f993 --- /dev/null +++ b/gui/src/main/java/io/bitsquare/gui/main/intructions/Instruction.java @@ -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() { + + } +} diff --git a/gui/src/main/java/io/bitsquare/gui/main/intructions/InstructionCenter.java b/gui/src/main/java/io/bitsquare/gui/main/intructions/InstructionCenter.java new file mode 100644 index 0000000000..c3a1515e7b --- /dev/null +++ b/gui/src/main/java/io/bitsquare/gui/main/intructions/InstructionCenter.java @@ -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 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(); + } + } + } +} diff --git a/gui/src/main/java/io/bitsquare/gui/main/notifications/Notification.java b/gui/src/main/java/io/bitsquare/gui/main/notifications/Notification.java new file mode 100644 index 0000000000..40fddf7dfd --- /dev/null +++ b/gui/src/main/java/io/bitsquare/gui/main/notifications/Notification.java @@ -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; + } +} diff --git a/gui/src/main/java/io/bitsquare/gui/main/notifications/NotificationCenter.java b/gui/src/main/java/io/bitsquare/gui/main/notifications/NotificationCenter.java index 63a488a237..b5817c0511 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/notifications/NotificationCenter.java +++ b/gui/src/main/java/io/bitsquare/gui/main/notifications/NotificationCenter.java @@ -1,14 +1,195 @@ package io.bitsquare.gui.main.notifications; 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.LoggerFactory; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class NotificationCenter { private static final Logger log = LoggerFactory.getLogger(NotificationCenter.class); - @Inject - public NotificationCenter() { + /////////////////////////////////////////////////////////////////////////////////////////// + // Static + /////////////////////////////////////////////////////////////////////////////////////////// + + private final static List notifications = new ArrayList<>(); + + static void add(Notification notification) { + notifications.add(notification); } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Instance fields + /////////////////////////////////////////////////////////////////////////////////////////// + + private TradeManager tradeManager; + private DisputeManager disputeManager; + + private final Map disputeStateSubscriptionsMap = new HashMap<>(); + private final Map 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) 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(); + } + } diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java index c97104b491..7cf78e9db1 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java @@ -391,14 +391,6 @@ class CreateOfferDataModel extends ActivatableDataModel { return user.getAcceptedArbitrators(); } - public void setShowPlaceOfferConfirmation(boolean selected) { - preferences.setShowPlaceOfferConfirmation(selected); - } - - public boolean getShowPlaceOfferConfirmation() { - return preferences.getShowPlaceOfferConfirmation(); - } - public Preferences getPreferences() { return preferences; } diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java index b031d04013..2838f276ca 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java @@ -47,6 +47,7 @@ import io.bitsquare.locale.BSResources; import io.bitsquare.locale.TradeCurrency; import io.bitsquare.payment.PaymentAccount; import io.bitsquare.trade.offer.Offer; +import io.bitsquare.user.Preferences; import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; import javafx.event.ActionEvent; @@ -109,6 +110,7 @@ public class CreateOfferView extends ActivatableViewAndModel currencyComboBoxSelectionHandler; private int gridRow = 0; + private Preferences preferences; /////////////////////////////////////////////////////////////////////////////////////////// @@ -116,11 +118,12 @@ public class CreateOfferView extends ActivatableViewAndModel model.onPlaceOffer(o)).show(offer); + String id = "CreatOfferConfirmation"; + if (preferences.showAgain(id)) { + offerDetailsPopup.onPlaceOffer(model::onPlaceOffer) + .dontShowAgainId(id) + .show(offer); } else { if (model.hasAcceptedArbitrators()) { model.onPlaceOffer(offer); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java index 7e3cc03aef..dc6321531b 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferViewModel.java @@ -577,13 +577,4 @@ class CreateOfferViewModel extends ActivatableWithDataModel getArbitrators() { return dataModel.getArbitrators(); } - - public void setShowPlaceOfferConfirmation(boolean selected) { - dataModel.setShowPlaceOfferConfirmation(selected); - } - - public boolean getShowPlaceOfferConfirmation() { - return dataModel.getShowPlaceOfferConfirmation(); - } - } diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java index 4296e0ea59..1b4a94a5ce 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java @@ -377,14 +377,6 @@ class TakeOfferDataModel extends ActivatableDataModel { return addressEntry; } - public boolean getShowTakeOfferConfirmation() { - return preferences.getShowTakeOfferConfirmation(); - } - - public void setShowTakeOfferConfirmation(boolean selected) { - preferences.setShowTakeOfferConfirmation(selected); - } - public List getArbitrators() { return user.getAcceptedArbitrators(); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java index d2cc34959d..341d0bd680 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java @@ -43,6 +43,7 @@ import io.bitsquare.gui.util.Layout; import io.bitsquare.locale.BSResources; import io.bitsquare.payment.PaymentAccount; import io.bitsquare.trade.offer.Offer; +import io.bitsquare.user.Preferences; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.geometry.*; @@ -71,6 +72,7 @@ public class TakeOfferView extends ActivatableViewAndModel model.onTakeOffer()).show(offer, model.dataModel.amountAsCoin.get()); + String id = "TakeOfferConfirmation"; + if (preferences.showAgain(id)) { + offerDetailsPopup.onTakeOffer(() -> model.onTakeOffer()) + .dontShowAgainId(id) + .show(offer, model.dataModel.amountAsCoin.get()); } else { if (model.hasAcceptedArbitrators()) { model.onTakeOffer(); diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java index bbd5f5e08c..ecd6645bd6 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java @@ -543,10 +543,6 @@ class TakeOfferViewModel extends ActivatableWithDataModel im return dataModel.getTradeCurrency(); } - public boolean getShowTakeOfferConfirmation() { - return dataModel.getShowTakeOfferConfirmation(); - } - public List getArbitrators() { return dataModel.getArbitrators(); } diff --git a/gui/src/main/java/io/bitsquare/gui/main/popups/OfferDetailsPopup.java b/gui/src/main/java/io/bitsquare/gui/main/popups/OfferDetailsPopup.java index b87e1f641e..20cc2cc9a8 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/popups/OfferDetailsPopup.java +++ b/gui/src/main/java/io/bitsquare/gui/main/popups/OfferDetailsPopup.java @@ -33,7 +33,6 @@ import io.bitsquare.user.Preferences; import io.bitsquare.user.User; import javafx.geometry.Insets; import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; import org.bitcoinj.core.Coin; @@ -51,7 +50,7 @@ public class OfferDetailsPopup extends Popup { protected static final Logger log = LoggerFactory.getLogger(OfferDetailsPopup.class); private final BSFormatter formatter; - private final Preferences preferences; + protected final Preferences preferences; private final User user; private KeyRing keyRing; private final Navigation navigation; @@ -97,6 +96,11 @@ public class OfferDetailsPopup extends Popup { return this; } + public OfferDetailsPopup dontShowAgainId(String dontShowAgainId) { + this.dontShowAgainId = dontShowAgainId; + return this; + } + public OfferDetailsPopup onPlaceOffer(Consumer placeOfferHandler) { this.placeOfferHandlerOptional = Optional.of(placeOfferHandler); 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); Button cancelButton = addConfirmButton(true); - addCancelButton(cancelButton, true); + addCancelButton(cancelButton); } else if (takeOfferHandlerOptional.isPresent()) { addTitledGroupBg(gridPane, ++rowIndex, 1, "Contract", Layout.GROUP_DISTANCE); addLabelTextField(gridPane, rowIndex, "Terms and conditions:", Offer.TAC_TAKER, Layout.FIRST_ROW_AND_GROUP_DISTANCE); Button cancelButton = addConfirmButton(false); - addCancelButton(cancelButton, false); + addCancelButton(cancelButton); } else { Button cancelButton = addButtonAfterGroup(gridPane, ++rowIndex, "Close"); cancelButton.setOnAction(e -> { @@ -229,19 +233,10 @@ public class OfferDetailsPopup extends Popup { return tuple.second; } - private void addCancelButton(Button cancelButton, boolean isPlaceOffer) { + private void addCancelButton(Button cancelButton) { cancelButton.setOnAction(e -> { closeHandlerOptional.ifPresent(closeHandler -> closeHandler.run()); 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())); - } } } diff --git a/gui/src/main/java/io/bitsquare/gui/main/popups/Popup.java b/gui/src/main/java/io/bitsquare/gui/main/popups/Popup.java index 839cab2b12..9e1a66fbaa 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/popups/Popup.java +++ b/gui/src/main/java/io/bitsquare/gui/main/popups/Popup.java @@ -70,7 +70,7 @@ public class Popup { private boolean showProgressIndicator; private Button actionButton; protected Label headLineLabel; - private String dontShowAgainId; + protected String dontShowAgainId; private Preferences preferences; private ChangeListener positionListener; private Timer centerTime; diff --git a/gui/src/main/java/io/bitsquare/gui/main/popups/TacPopup.java b/gui/src/main/java/io/bitsquare/gui/main/popups/TacPopup.java new file mode 100644 index 0000000000..c0bf6cdca0 --- /dev/null +++ b/gui/src/main/java/io/bitsquare/gui/main/popups/TacPopup.java @@ -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(); + } + } +} diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java index bda80d732d..a97cc44d19 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java @@ -127,12 +127,8 @@ public class PendingTradesView extends ActivatableViewAndModel btcDenominationComboBox; - private ComboBox blockExplorerComboBox; + private ComboBox blockChainExplorerComboBox; private ComboBox languageComboBox; - private ComboBox tradeCurrencyComboBox; + private ComboBox preferredTradeCurrencyComboBox; - private CheckBox useAnimationsCheckBox, useEffectsCheckBox, showPlaceOfferConfirmationCheckBox, showTakeOfferConfirmationCheckBox, + private CheckBox useAnimationsCheckBox, useEffectsCheckBox, showNotificationsCheckBox, showInstructionsCheckBox, autoSelectArbitratorsCheckBox; private int gridRow = 0; //private InputTextField transactionFeeInputTextField; private ChangeListener transactionFeeFocusedListener; + private Preferences preferences; @Inject - public PreferencesView(PreferencesViewModel model) { + public PreferencesView(PreferencesViewModel model, Preferences preferences) { super(model); + this.preferences = preferences; } @Override public void initialize() { 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; // 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; - + // 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; transactionFeeFocusedListener = (o, oldValue, newValue) -> { @@ -71,8 +74,8 @@ public class PreferencesView extends ActivatableViewAndModel model.onSelectBtcDenomination(btcDenominationComboBox.getSelectionModel().getSelectedItem()));*/ - tradeCurrencyComboBox.setItems(model.tradeCurrencies); - tradeCurrencyComboBox.getSelectionModel().select(model.getTradeCurrency()); - tradeCurrencyComboBox.setConverter(new StringConverter() { + preferredTradeCurrencyComboBox.setItems(model.tradeCurrencies); + preferredTradeCurrencyComboBox.getSelectionModel().select(preferences.getPreferredTradeCurrency()); + preferredTradeCurrencyComboBox.setConverter(new StringConverter() { @Override public String toString(TradeCurrency tradeCurrency) { return tradeCurrency.getNameAndCode(); @@ -95,7 +98,7 @@ public class PreferencesView extends ActivatableViewAndModel model.onSelectTradeCurrency(tradeCurrencyComboBox.getSelectionModel().getSelectedItem())); + preferredTradeCurrencyComboBox.setOnAction(e -> preferences.setPreferredTradeCurrency(preferredTradeCurrencyComboBox.getSelectionModel().getSelectedItem())); languageComboBox.setItems(model.languageCodes); languageComboBox.getSelectionModel().select(model.getLanguageCode()); @@ -113,9 +116,9 @@ public class PreferencesView extends ActivatableViewAndModel model.onSelectLanguageCode(languageComboBox.getSelectionModel().getSelectedItem())); - blockExplorerComboBox.setItems(model.blockExplorers); - blockExplorerComboBox.getSelectionModel().select(model.getBlockExplorer()); - blockExplorerComboBox.setConverter(new StringConverter() { + blockChainExplorerComboBox.setItems(model.blockExplorers); + blockChainExplorerComboBox.getSelectionModel().select(preferences.getBlockChainExplorer()); + blockChainExplorerComboBox.setConverter(new StringConverter() { @Override public String toString(BlockChainExplorer blockChainExplorer) { return blockChainExplorer.name; @@ -126,39 +129,39 @@ public class PreferencesView extends ActivatableViewAndModel model.onSelectBlockExplorer(blockExplorerComboBox.getSelectionModel().getSelectedItem())); + blockChainExplorerComboBox.setOnAction(e -> preferences.setBlockChainExplorer(blockChainExplorerComboBox.getSelectionModel().getSelectedItem())); // transactionFeeInputTextField.textProperty().bindBidirectional(model.transactionFeePerByte); // transactionFeeInputTextField.focusedProperty().addListener(transactionFeeFocusedListener); - useAnimationsCheckBox.setSelected(model.getUseAnimations()); - useAnimationsCheckBox.setOnAction(e -> model.onSelectUseAnimations(useAnimationsCheckBox.isSelected())); + useAnimationsCheckBox.setSelected(preferences.getUseAnimations()); + useAnimationsCheckBox.setOnAction(e -> preferences.setUseAnimations(useAnimationsCheckBox.isSelected())); - useEffectsCheckBox.setSelected(model.getUseEffects()); - useEffectsCheckBox.setOnAction(e -> model.onSelectUseEffects(useEffectsCheckBox.isSelected())); + useEffectsCheckBox.setSelected(preferences.getUseEffects()); + useEffectsCheckBox.setOnAction(e -> preferences.setUseEffects(useEffectsCheckBox.isSelected())); - showPlaceOfferConfirmationCheckBox.setSelected(model.getShowPlaceOfferConfirmation()); - showPlaceOfferConfirmationCheckBox.setOnAction(e -> model.onSelectShowPlaceOfferConfirmation(showPlaceOfferConfirmationCheckBox.isSelected())); + showNotificationsCheckBox.setSelected(preferences.getShowNotifications()); + showNotificationsCheckBox.setOnAction(e -> preferences.setShowNotifications(showNotificationsCheckBox.isSelected())); - showTakeOfferConfirmationCheckBox.setSelected(model.getShowTakeOfferConfirmation()); - showTakeOfferConfirmationCheckBox.setOnAction(e -> model.onSelectShowTakeOfferConfirmation(showTakeOfferConfirmationCheckBox.isSelected())); + showInstructionsCheckBox.setSelected(preferences.getShowInstructions()); + showInstructionsCheckBox.setOnAction(e -> preferences.setShowInstructions(showInstructionsCheckBox.isSelected())); - autoSelectArbitratorsCheckBox.setSelected(model.getAutoSelectArbitrators()); - autoSelectArbitratorsCheckBox.setOnAction(e -> model.onSelectAutoSelectArbitratorsCheckBox(autoSelectArbitratorsCheckBox.isSelected())); + autoSelectArbitratorsCheckBox.setSelected(preferences.getAutoSelectArbitrators()); + autoSelectArbitratorsCheckBox.setOnAction(e -> preferences.setAutoSelectArbitrators(autoSelectArbitratorsCheckBox.isSelected())); } @Override protected void deactivate() { //btcDenominationComboBox.setOnAction(null); languageComboBox.setOnAction(null); - tradeCurrencyComboBox.setOnAction(null); - blockExplorerComboBox.setOnAction(null); + preferredTradeCurrencyComboBox.setOnAction(null); + blockChainExplorerComboBox.setOnAction(null); + showNotificationsCheckBox.setOnAction(null); + showInstructionsCheckBox.setOnAction(null); // transactionFeeInputTextField.textProperty().unbind(); /// transactionFeeInputTextField.focusedProperty().removeListener(transactionFeeFocusedListener); useAnimationsCheckBox.setOnAction(null); useEffectsCheckBox.setOnAction(null); - showPlaceOfferConfirmationCheckBox.setOnAction(null); - showTakeOfferConfirmationCheckBox.setOnAction(null); autoSelectArbitratorsCheckBox.setOnAction(null); } } diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesViewModel.java b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesViewModel.java index 44a13dd4a4..ac451a652e 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesViewModel.java +++ b/gui/src/main/java/io/bitsquare/gui/main/settings/preferences/PreferencesViewModel.java @@ -65,42 +65,11 @@ class PreferencesViewModel extends ActivatableViewModel { protected void deactivate() { } + /////////////////////////////////////////////////////////////////////////////////////////// // 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) { preferences.setPreferredLocale(new Locale(code, preferences.getPreferredLocale().getCountry())); } @@ -124,40 +93,8 @@ class PreferencesViewModel extends ActivatableViewModel { // 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() { return preferences.getPreferredLocale().getLanguage(); } - public TradeCurrency getTradeCurrency() { - return preferences.getPreferredTradeCurrency(); - } - }