diff --git a/core/src/main/java/bisq/core/app/BisqExecutable.java b/core/src/main/java/bisq/core/app/BisqExecutable.java index 67270b5c6de..5e4863c818f 100644 --- a/core/src/main/java/bisq/core/app/BisqExecutable.java +++ b/core/src/main/java/bisq/core/app/BisqExecutable.java @@ -21,6 +21,7 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.DaoSetup; +import bisq.core.dao.node.full.RpcService; import bisq.core.offer.OpenOfferManager; import bisq.core.setup.CorePersistedDataHost; import bisq.core.setup.CoreSetup; @@ -229,6 +230,7 @@ public void gracefulShutDown(ResultHandler resultHandler) { injector.getInstance(ArbitratorManager.class).shutDown(); injector.getInstance(TradeStatisticsManager.class).shutDown(); injector.getInstance(XmrTxProofService.class).shutDown(); + injector.getInstance(RpcService.class).shutDown(); injector.getInstance(DaoSetup.class).shutDown(); injector.getInstance(AvoidStandbyModeService.class).shutDown(); injector.getInstance(OpenOfferManager.class).shutDown(() -> { diff --git a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java index 9ab2eeb7381..0cb4006deb5 100644 --- a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java @@ -22,6 +22,7 @@ import bisq.core.btc.wallet.BsqWalletService; import bisq.core.btc.wallet.BtcWalletService; import bisq.core.dao.DaoSetup; +import bisq.core.dao.node.full.RpcService; import bisq.core.offer.OpenOfferManager; import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager; @@ -86,6 +87,7 @@ public void gracefulShutDown(ResultHandler resultHandler) { try { if (injector != null) { JsonFileManager.shutDownAllInstances(); + injector.getInstance(RpcService.class).shutDown(); injector.getInstance(DaoSetup.class).shutDown(); injector.getInstance(ArbitratorManager.class).shutDown(); injector.getInstance(OpenOfferManager.class).shutDown(() -> injector.getInstance(P2PService.class).shutDown(() -> { diff --git a/core/src/main/java/bisq/core/dao/node/full/RpcService.java b/core/src/main/java/bisq/core/dao/node/full/RpcService.java index d694cee91c2..1357288a998 100644 --- a/core/src/main/java/bisq/core/dao/node/full/RpcService.java +++ b/core/src/main/java/bisq/core/dao/node/full/RpcService.java @@ -121,6 +121,20 @@ public RpcService(Preferences preferences, // API /////////////////////////////////////////////////////////////////////////////////////////// + public void shutDown() { + if (daemon != null) { + daemon.shutdown(); + log.info("daemon shut down"); + } + + if (client != null) { + client.close(); + log.info("client closed"); + } + + executor.shutdown(); + } + void setup(ResultHandler resultHandler, Consumer errorHandler) { ListenableFuture future = executor.submit(() -> { try { diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 818ef8e5989..148bd0f25cb 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -71,6 +71,7 @@ shared.amountWithCur=Amount in {0} shared.volumeWithCur=Volume in {0} shared.currency=Currency shared.market=Market +shared.deviation=Deviation shared.paymentMethod=Payment method shared.tradeCurrency=Trade currency shared.offerType=Offer type @@ -560,6 +561,8 @@ portfolio.tab.history=History portfolio.tab.failed=Failed portfolio.tab.editOpenOffer=Edit offer +portfolio.closedTrades.deviation.help=Percentage price deviation from market + portfolio.pending.invalidDelayedPayoutTx=There is an issue with a missing or invalid transaction.\n\n\ Please do NOT send the fiat or altcoin payment. Contact Bisq \ developers on Keybase [HYPERLINK:https://keybase.io/team/bisq] or on the \ @@ -1281,7 +1284,7 @@ settings.net.inbound=inbound settings.net.outbound=outbound settings.net.reSyncSPVChainLabel=Resync SPV chain settings.net.reSyncSPVChainButton=Delete SPV file and resync -settings.net.reSyncSPVSuccess=The SPV chain file will be deleted on the next startup. You need to restart your application now.\n\n\ +settings.net.reSyncSPVSuccess=Are you sure you want to do an SPV resync? If you proceed, the SPV chain file will be deleted on the next startup.\n\n\ After the restart it can take a while to resync with the network and you will only see all transactions once the resync is completed.\n\n\ Depending on the number of transactions and the age of your wallet the resync can take up to a few hours and consumes 100% of CPU. \ Do not interrupt the process otherwise you have to repeat it. diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml index 25900ef61af..4395e2e5481 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.fxml @@ -34,17 +34,18 @@ - - - + + + - - + + + - + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java index c282e86db0d..87407bdbc94 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesView.java @@ -21,6 +21,7 @@ import bisq.desktop.common.view.FxmlView; import bisq.desktop.components.AutoTooltipButton; import bisq.desktop.components.AutoTooltipLabel; +import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.components.InputTextField; import bisq.desktop.components.PeerInfoIcon; @@ -79,10 +80,37 @@ public class ClosedTradesView extends ActivatableViewAndModel { private final boolean useDevPrivilegeKeys; + private enum ColumnNames { + TRADE_ID(Res.get("shared.tradeId")), + DATE(Res.get("shared.dateTime")), + MARKET(Res.get("shared.market")), + PRICE(Res.get("shared.price")), + DEVIATION(Res.get("shared.deviation")), + AMOUNT(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode())), + VOLUME(Res.get("shared.amount")), + TX_FEE(Res.get("shared.txFee")), + TRADE_FEE(Res.get("shared.tradeFee")), + BUYER_SEC(Res.get("shared.buyerSecurityDeposit")), + SELLER_SEC(Res.get("shared.sellerSecurityDeposit")), + OFFER_TYPE(Res.get("shared.offerType")), + STATUS(Res.get("shared.state")); + + private final String text; + + ColumnNames(String text) { + this.text = text; + } + @Override + public String toString() { + return text; + } + } + @FXML TableView tableView; @FXML - TableColumn priceColumn, amountColumn, volumeColumn, txFeeColumn, tradeFeeColumn, buyerSecurityDepositColumn, sellerSecurityDepositColumn, + TableColumn priceColumn, deviationColumn, amountColumn, volumeColumn, + txFeeColumn, tradeFeeColumn, buyerSecurityDepositColumn, sellerSecurityDepositColumn, marketColumn, directionColumn, dateColumn, tradeIdColumn, stateColumn, avatarColumn; @FXML HBox footerBox; @@ -120,18 +148,20 @@ public ClosedTradesView(ClosedTradesViewModel model, @Override public void initialize() { - txFeeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.txFee"))); - tradeFeeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.tradeFee"))); - buyerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.buyerSecurityDeposit"))); - sellerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.sellerSecurityDeposit"))); - priceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.price"))); - amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountWithCur", Res.getBaseCurrencyCode()))); - volumeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amount"))); - marketColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.market"))); - directionColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.offerType"))); - dateColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.dateTime"))); - tradeIdColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.tradeId"))); - stateColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.state"))); + txFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TX_FEE.toString())); + tradeFeeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_FEE.toString())); + buyerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.BUYER_SEC.toString())); + sellerSecurityDepositColumn.setGraphic(new AutoTooltipLabel(ColumnNames.SELLER_SEC.toString())); + priceColumn.setGraphic(new AutoTooltipLabel(ColumnNames.PRICE.toString())); + deviationColumn.setGraphic(new AutoTooltipTableColumn<>(ColumnNames.DEVIATION.toString(), + Res.get("portfolio.closedTrades.deviation.help")).getGraphic()); + amountColumn.setGraphic(new AutoTooltipLabel(ColumnNames.AMOUNT.toString())); + volumeColumn.setGraphic(new AutoTooltipLabel(ColumnNames.VOLUME.toString())); + marketColumn.setGraphic(new AutoTooltipLabel(ColumnNames.MARKET.toString())); + directionColumn.setGraphic(new AutoTooltipLabel(ColumnNames.OFFER_TYPE.toString())); + dateColumn.setGraphic(new AutoTooltipLabel(ColumnNames.DATE.toString())); + tradeIdColumn.setGraphic(new AutoTooltipLabel(ColumnNames.TRADE_ID.toString())); + stateColumn.setGraphic(new AutoTooltipLabel(ColumnNames.STATUS.toString())); avatarColumn.setText(""); tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); @@ -145,6 +175,7 @@ public void initialize() { setBuyerSecurityDepositColumnCellFactory(); setSellerSecurityDepositColumnCellFactory(); setPriceColumnCellFactory(); + setDeviationColumnCellFactory(); setVolumeColumnCellFactory(); setDateColumnCellFactory(); setMarketColumnCellFactory(); @@ -159,6 +190,9 @@ public void initialize() { priceColumn.setComparator(nullsFirstComparing(o -> o instanceof Trade ? ((Trade) o).getTradePrice() : o.getOffer().getPrice() )); + deviationColumn.setComparator(Comparator.comparing(o -> + o.getTradable().getOffer().isUseMarketBasedPrice() ? o.getTradable().getOffer().getMarketPriceMargin() : 1, + Comparator.nullsFirst(Comparator.naturalOrder()))); volumeColumn.setComparator(nullsFirstComparingAsTrade(Trade::getTradeVolume)); amountColumn.setComparator(nullsFirstComparingAsTrade(Trade::getTradeAmount)); avatarColumn.setComparator(nullsFirstComparingAsTrade(o -> @@ -217,25 +251,27 @@ protected void activate() { exportButton.setOnAction(event -> { final ObservableList> tableColumns = tableView.getColumns(); CSVEntryConverter headerConverter = transactionsListItem -> { - String[] columns = new String[12]; - for (int i = 0; i < columns.length; i++) - columns[i] = ((AutoTooltipLabel) tableColumns.get(i).getGraphic()).getText(); + String[] columns = new String[ColumnNames.values().length]; + for (ColumnNames m : ColumnNames.values()) { + columns[m.ordinal()] = m.toString(); + } return columns; }; CSVEntryConverter contentConverter = item -> { - String[] columns = new String[12]; - columns[0] = model.getTradeId(item); - columns[1] = model.getDate(item); - columns[2] = model.getMarketLabel(item); - columns[3] = model.getPrice(item); - columns[4] = model.getAmount(item); - columns[5] = model.getVolume(item); - columns[6] = model.getTxFee(item); - columns[7] = model.getMakerFee(item); - columns[8] = model.getBuyerSecurityDeposit(item); - columns[9] = model.getSellerSecurityDeposit(item); - columns[10] = model.getDirectionLabel(item); - columns[11] = model.getState(item); + String[] columns = new String[ColumnNames.values().length]; + columns[ColumnNames.TRADE_ID.ordinal()] = model.getTradeId(item); + columns[ColumnNames.DATE.ordinal()] = model.getDate(item); + columns[ColumnNames.MARKET.ordinal()] = model.getMarketLabel(item); + columns[ColumnNames.PRICE.ordinal()] = model.getPrice(item); + columns[ColumnNames.DEVIATION.ordinal()] = model.getPriceDeviation(item); + columns[ColumnNames.AMOUNT.ordinal()] = model.getAmount(item); + columns[ColumnNames.VOLUME.ordinal()] = model.getVolume(item); + columns[ColumnNames.TX_FEE.ordinal()] = model.getTxFee(item); + columns[ColumnNames.TRADE_FEE.ordinal()] = model.getMakerFee(item); + columns[ColumnNames.BUYER_SEC.ordinal()] = model.getBuyerSecurityDeposit(item); + columns[ColumnNames.SELLER_SEC.ordinal()] = model.getSellerSecurityDeposit(item); + columns[ColumnNames.OFFER_TYPE.ordinal()] = model.getDirectionLabel(item); + columns[ColumnNames.STATUS.ordinal()] = model.getState(item); return columns; }; @@ -461,6 +497,24 @@ public void updateItem(final ClosedTradableListItem item, boolean empty) { }); } + private void setDeviationColumnCellFactory() { + deviationColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + deviationColumn.setCellFactory( + new Callback<>() { + @Override + public TableCell call( + TableColumn column) { + return new TableCell<>() { + @Override + public void updateItem(final ClosedTradableListItem item, boolean empty) { + super.updateItem(item, empty); + setGraphic(new AutoTooltipLabel(model.getPriceDeviation(item))); + } + }; + } + }); + } + private void setVolumeColumnCellFactory() { volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); volumeColumn.setCellFactory( diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java index 6e023f00368..23487d6650e 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/closedtrades/ClosedTradesViewModel.java @@ -78,6 +78,17 @@ String getPrice(ClosedTradableListItem item) { return FormattingUtils.formatPrice(tradable.getOffer().getPrice()); } + String getPriceDeviation(ClosedTradableListItem item) { + if (item == null) + return ""; + Tradable tradable = item.getTradable(); + if (tradable.getOffer().isUseMarketBasedPrice()) { + return FormattingUtils.formatPercentagePrice(tradable.getOffer().getMarketPriceMargin()); + } else { + return Res.get("shared.na"); + } + } + String getVolume(ClosedTradableListItem item) { if (item != null && item.getTradable() instanceof Trade) return DisplayUtils.formatVolumeWithCode(((Trade) item.getTradable()).getTradeVolume()); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.fxml b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.fxml index 250cc4d8073..71deb94aaa1 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.fxml +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.fxml @@ -29,14 +29,15 @@ - - + + - - - + + + + - + diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java index ecdee3ca5e2..8dc987efaf4 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersView.java @@ -22,6 +22,7 @@ import bisq.desktop.common.view.FxmlView; import bisq.desktop.components.AutoTooltipCheckBox; import bisq.desktop.components.AutoTooltipLabel; +import bisq.desktop.components.AutoTooltipTableColumn; import bisq.desktop.components.HyperlinkWithIcon; import bisq.desktop.main.MainView; import bisq.desktop.main.funds.FundsView; @@ -67,7 +68,7 @@ public class OpenOffersView extends ActivatableViewAndModel tableView; @FXML - TableColumn priceColumn, amountColumn, volumeColumn, + TableColumn priceColumn, deviationColumn, amountColumn, volumeColumn, marketColumn, directionColumn, dateColumn, offerIdColumn, deactivateItemColumn, removeItemColumn, editItemColumn, paymentMethodColumn; private final Navigation navigation; @@ -86,6 +87,8 @@ public OpenOffersView(OpenOffersViewModel model, Navigation navigation, OfferDet public void initialize() { paymentMethodColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.paymentMethod"))); priceColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.price"))); + deviationColumn.setGraphic(new AutoTooltipTableColumn<>(Res.get("shared.deviation"), + Res.get("portfolio.closedTrades.deviation.help")).getGraphic()); amountColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.BTCMinMax"))); volumeColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.amountMinMax"))); marketColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.market"))); @@ -100,6 +103,7 @@ public void initialize() { setDirectionColumnCellFactory(); setMarketColumnCellFactory(); setPriceColumnCellFactory(); + setDeviationColumnCellFactory(); setAmountColumnCellFactory(); setVolumeColumnCellFactory(); setPaymentMethodColumnCellFactory(); @@ -116,6 +120,9 @@ public void initialize() { marketColumn.setComparator(Comparator.comparing(model::getMarketLabel)); amountColumn.setComparator(Comparator.comparing(o -> o.getOffer().getAmount())); priceColumn.setComparator(Comparator.comparing(o -> o.getOffer().getPrice(), Comparator.nullsFirst(Comparator.naturalOrder()))); + deviationColumn.setComparator(Comparator.comparing(o -> + o.getOffer().isUseMarketBasedPrice() ? o.getOffer().getMarketPriceMargin() : 1, + Comparator.nullsFirst(Comparator.naturalOrder()))); volumeColumn.setComparator(Comparator.comparing(o -> o.getOffer().getVolume(), Comparator.nullsFirst(Comparator.naturalOrder()))); dateColumn.setComparator(Comparator.comparing(o -> o.getOffer().getDate())); paymentMethodColumn.setComparator(Comparator.comparing(o -> o.getOffer().getPaymentMethod().getId())); @@ -308,6 +315,31 @@ public void updateItem(final OpenOfferListItem item, boolean empty) { }); } + private void setDeviationColumnCellFactory() { + deviationColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); + deviationColumn.setCellFactory( + new Callback<>() { + @Override + public TableCell call( + TableColumn column) { + return new TableCell<>() { + @Override + public void updateItem(final OpenOfferListItem item, boolean empty) { + super.updateItem(item, empty); + getStyleClass().removeAll("offer-disabled"); + + if (item != null) { + if (model.isDeactivated(item)) getStyleClass().add("offer-disabled"); + setGraphic(new AutoTooltipLabel(model.getPriceDeviation(item))); + } else { + setGraphic(null); + } + } + }; + } + }); + } + private void setVolumeColumnCellFactory() { volumeColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue())); volumeColumn.setCellFactory( diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java index 1e3b2884f0c..94b4533935b 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/openoffer/OpenOffersViewModel.java @@ -93,10 +93,18 @@ String getPrice(OpenOfferListItem item) { Offer offer = item.getOffer(); Price price = offer.getPrice(); if (price != null) { - String postFix = ""; - if (offer.isUseMarketBasedPrice()) - postFix = " (" + FormattingUtils.formatPercentagePrice(offer.getMarketPriceMargin()) + ")"; - return FormattingUtils.formatPrice(price) + postFix; + return FormattingUtils.formatPrice(price); + } else { + return Res.get("shared.na"); + } + } + + String getPriceDeviation(OpenOfferListItem item) { + if ((item == null)) + return ""; + Offer offer = item.getOffer(); + if (offer.isUseMarketBasedPrice()) { + return FormattingUtils.formatPercentagePrice(offer.getMarketPriceMargin()); } else { return Res.get("shared.na"); } diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index 0526dee1137..93e1b9dc20e 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -801,14 +801,14 @@ public static void requestFocus(Node node) { public static void reSyncSPVChain(Preferences preferences) { try { - new Popup().feedback(Res.get("settings.net.reSyncSPVSuccess")) + new Popup().information(Res.get("settings.net.reSyncSPVSuccess")) .useShutDownButton() .actionButtonText(Res.get("shared.shutDown")) .onAction(() -> { preferences.setResyncSpvRequested(true); UserThread.runAfter(BisqApp.getShutDownHandler(), 100, TimeUnit.MILLISECONDS); }) - .hideCloseButton() + .closeButtonText(Res.get("shared.cancel")) .show(); } catch (Throwable t) { new Popup().error(Res.get("settings.net.reSyncSPVFailed", t)).show(); diff --git a/seednode/install_seednode_debian.sh b/seednode/install_seednode_debian.sh index e8872219bee..995b1e6ec7d 100755 --- a/seednode/install_seednode_debian.sh +++ b/seednode/install_seednode_debian.sh @@ -61,7 +61,6 @@ echo "[*] Installing base packages" sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ${ROOT_PKG} echo "[*] Installing Git LFS" -sudo -H -i -u "${ROOT_USER}" curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash sudo -H -i -u "${ROOT_USER}" apt-get install git-lfs sudo -H -i -u "${ROOT_USER}" git lfs install